Android开发中遇到关于Byte位运算通信协议类项目的文档解读分析

Posted Engineer-Jsp

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android开发中遇到关于Byte位运算通信协议类项目的文档解读分析相关的知识,希望对你有一定的参考价值。

android开发中经常会遇到Byte位运算通信协议的项目,一个简单的Byte可能隐藏着极其复杂的数据,需要根据既定的协议来解析和封装。那么开发中要怎么解决这类项目呢,还是要多熟悉文档和源码。

这类项目笔者15年的时候接触过,是独立开发的。因为当初入行不到两年,所以接触的这类项目可以说是初次接触,看个文档对于那时很菜鸟的我来说简直是要了我的命,但是客户的对接工程师是个C老鸟,人也不错,我有问题就会找他帮忙,而且他也不吝赐教,这让笔者倍感欣慰。然而今天又遇到了一个这样的项目,就想着写一篇博客笔记来记录这个分析的过程,避免以后笔者或读者再遇到这类项目时无从下手。

首先笔者介绍开发中常接触到的一些位运算符号。

<<(左移)

	/**
	 * 逻辑运算符
	 * 
	 * @author Engineer-Jsp
	 *  运算符:<<
	 */
	public static void ByteCalc() 
		// 0x01为1的16进制,运行结果是2
		System.out.println(0x01 << 1);
	

该段代码的意思是将1向左移1位,先将16进制的0x01转换成10进制即1,运行结果为2。

解析,先将1转换为二进制数据,然后向左(从右边开始,依次往左)位移1位,在低位处补0

左移前:0000 0000 0000 0000 0000 0000 0000 0001

左移后:0000 0000 0000 0000 0000 0000 0000 0010

将二进制数据 0000 0000 0000 0000 0000 0000 0000 0010 转换为10进制数据,结果为2。 

>>(右移)

	/**
	 * 逻辑运算符
	 * 
	 * @author Engineer-Jsp
	 *  运算符:>>
	 */
	public static void ByteCalc() 
		// 0x02为2的16进制,运行结果是1
		System.out.println(0x02 >> 1);
	

该段代码的意思是将2向右移1位,先将16进制的0x02转换成10进制即2,运行结果为1。

解析(不考虑负数位移),先将2转换为二进制数据,然后向右(从左边开始,依次往右)位移1位,高位补0

右移前:0000 0000 0000 0000 0000 0000 0000 0010

右移后:0000 0000 0000 0000 0000 0000 0000 0001

将二进制数据 0000 0000 0000 0000 0000 0000 0000 0001 转换为10进制数据,结果为1。

&(与)

规则:当相同的位上均为1时结果为1,否则为0。

	/**
	 * 逻辑运算符
	 * 
	 * @author Engineer-Jsp
	 *  运算符:&
	 */
	public static void ByteCalc() 
		// 0x02为2的16进制,运行结果是0
		System.out.println(0x02 & 1);
	

该段代码的意思是将2和1进行&的位运算,先将16进制的0x02转换成10进制即2,运行结果为0。

解析,先将2转换为二进制数据,然后将1也转换为二进制数据再进行与运算:

2的二进制:0000 0000 0000 0000 0000 0000 0000 0010

1的二进制:0000 0000 0000 0000 0000 0000 0000 0001

&运算后的二进制:0000 0000 0000 0000 0000 0000 0000 0000

将二进制数据 0000 0000 0000 0000 0000 0000 0000 0000 转换为10进制数据,结果为0。

|(或)

规则:当两边操作数的位有一边为1时,结果为1,否则为0。

	/**
	 * 逻辑运算符
	 * 
	 * @author Engineer-Jsp
	 *  运算符:|
	 */
	public static void ByteCalc() 
		// 0x02为2的16进制,运行结果是3
		System.out.println(0x02 | 1);
	

该段代码的意思是将2和1进行|的位运算,先将16进制的0x02转换成10进制即2,运行结果为3。

解析,先将2转换为二进制数据,然后将1也转换为二进制数据再进行或运算:

2的二进制:0000 0000 0000 0000 0000 0000 0000 0010

1的二进制:0000 0000 0000 0000 0000 0000 0000 0001

|运算后的二进制:0000 0000 0000 0000 0000 0000 0000 0011

将二进制数据 0000 0000 0000 0000 0000 0000 0000 0011 转换为10进制数据,结果为3。

^(异或)

规则:两边的位不同时,结果为1,否则为0。

	/**
	 * 逻辑运算符
	 * 
	 * @author Engineer-Jsp
	 *  运算符:^
	 */
	public static void ByteCalc() 
		// 0x02为2的16进制,运行结果是3
		System.out.println(0x02 ^ 1);
	

该段代码的意思是将2和1进行^的位运算,先将16进制的0x02转换成10进制即2,运行结果为3。

解析,先将2转换为二进制数据,然后将1也转换为二进制数据再进行异或运算:

2的二进制:0000 0000 0000 0000 0000 0000 0000 0010

1的二进制:0000 0000 0000 0000 0000 0000 0000 0001

^运算后的二进制:0000 0000 0000 0000 0000 0000 0000 0011

将二进制数据 0000 0000 0000 0000 0000 0000 0000 0011 转换为10进制数据,结果为3。

关于常用的位运算就介绍到这里,下面介绍项目案例,根据文档需求,分析位的运算。

看上述表中的状态字段,DWORD为无符号四字节整型(双字,32 位),那怎么对需求文档中所述,对位0进行赋值来表示定位状态、对位1赋值表示经度类型......

分析:既然DWORD为无符号四字节整型(双字,32 位),又因为Java int类型占4个字节,所以把其视为Java中的int类型。

处理:Java中一个Byte占一个字节(8个位),把int拆分成4个Byte进行重组赋值来表示位的变化。

	/**
	 * 位赋值运算
	 * 
	 * @author Engineer-Jsp
	 */
	public static void ByteCalc() 
		// 将Bytes(由4个Byte组成的Byte数组)视为一个int类型的值
		byte[]Bytes = new byte[4];
		
		// 伪代码假设满足条件:定到位置
		if(定位成功)
			//表示对位0进行赋值
			Bytes[3] |= 0x01;
		
		// 伪代码假设满足条件:西经
 		if(西经)
 			//表示对位1进行赋值
 			Bytes[3] |= 0x02;
 		
 		// 伪代码假设满足条件:南纬
 		if(南纬)
 			//表示对位2进行赋值
 			Bytes[3] |= 0x04;
 		
 		// 伪代码假设满足条件:震动
 		if(震动)
 			//表示对位8进行赋值
 			Bytes[2] |= 0x01;
 		
 		// 伪代码假设满足条件:切断电源
 		if(切断电源)
			//表示对位9进行赋值
 			Bytes[2] |= 0x02;
 		
 		// 伪代码假设满足条件:Acc 开
 		if(Acc 开)
 			//表示对位10进行赋值
 			Bytes[2] |= 0x04;
	

上述代码是分别对 位0~位2位8~位10 进行了赋值,下面用图加文字分析来介绍为什么要这么写。


见上图,笔者进行逐步分解介绍:

1.Bytes是长度为4(即4个16进制字节组成)的字节数组,因为是空数组,所以元素值均为0。

2.Bytes[0]~Bytes[3]分别为Bytes数组的元素,每个元素对应一个16进制的字节。

3.每个字节对应Bytes(看作4个字节的int类型,总共32位)的8个位,升序排列从右开始,依次往左。

4.0000 0000 0000 0000 0000 0000 0000 0000二进制数据由int类型的值即十进制的0转换而来。

5.0000 0000 0000 0000 0000 0000 0000 0000二进制数据顶部的小字体的数字就代表着位,最后一位应该是31,因为是从0开始的,这里笔者作图的时候写错了,所以最后一位应该是31

搞清楚这个之后,开始详细介绍伪代码的位运算。

因为Bytes是长度为4(即4个16进制字节组成)的字节数组,因为是空数组,所以元素值均等于0,转换成int也是0,再将0转换成二进制数据 0000 0000 0000 0000 0000 0000 0000 0000。

①位0表示定位状态:

位计算:

因为位0~位2处于Bytes数组元素的第3个即Bytes[3](从0开始,所以元素下标为0~3),所以只需要将位0进行赋值即可。

Bytes[3]|=0x01;

Bytes[3]= 0x00 = 0 = 0000 0000 0000 0000 0000 0000 0000 0000 将第三个16进制元素byte转换成10进制,然后再将其转换成2进制

Bytes[3]二进制:0000 0000 0000 0000 0000 0000 0000 0000

0x01二进制     :0000 0000 0000 0000 0000 0000 0000 0001

或运算结果:0000 0000 0000 0000 0000 0000 0000 0001

结果说明 位0 已为1,表示定位成功。

②位1表示经度类型:

位计算:

因为位0~位2处于Bytes数组元素的第3个即Bytes[3](从0开始,所以元素下标为0~3),所以只需要将位1进行赋值即可。

Bytes[3]|=0x02;

Bytes[3]= 0x00 = 0 = 0000 0000 0000 0000 0000 0000 0000 0000 将第三个16进制元素byte转换成10进制,然后再将其转换成2进制

Bytes[3]二进制:0000 0000 0000 0000 0000 0000 0000 0000 

0x02二进制     :0000 0000 0000 0000 0000 0000 0000 0010 

或运算结果:0000 0000 0000 0000 0000 0000 0000 0010

结果说明 位1 已为1,表示经度为西经。

③位2表示纬度类型:

位计算:

因为位0~位2处于Bytes数组元素的第3个即Bytes[3](从0开始,所以元素下标为0~3),所以只需要将位2进行赋值即可。

Bytes[3]|=0x04;

Bytes[3]= 0x00 = 0 = 0000 0000 0000 0000 0000 0000 0000 0000 将第三个16进制元素byte转换成10进制,然后再将其转换成2进制

Bytes[3]二进制:0000 0000 0000 0000 0000 0000 0000 0000 

0x04二进制     :0000 0000 0000 0000 0000 0000 0000 0100 

或运算结果:0000 0000 0000 0000 0000 0000 0000 0100

结果说明 位2 已为1,表示纬度为南纬。

④位8表示防盗震动报警状态:

位计算:

因为位8~位10处于Bytes数组元素的第2个即Bytes[2](从0开始,所以元素下标为0~3),所以只需要将位8进行赋值即可。

Bytes[2]|=0x01;

Bytes[2]= 0x00 = 0 = 0000 0000 0000 0000 0000 0000 0000 0000 将第三个16进制元素byte转换成10进制,然后再将其转换成2进制

Bytes[2]二进制:0000 0000 0000 0000 0000 0000 0000 0000 

0x01二进制     :0000 0000 0000 0000 0000 0000 0000 0001 

或运算结果:0000 0000 0000 0000 0000 0000 0000 0001

结果说明 位8 已为1,表示报警。


⑤位9表示终端电源状态:

位计算:

因为位8~位10处于Bytes数组元素的第2个即Bytes[2](从0开始,所以元素下标为0~3),所以只需要将位9进行赋值即可。

Bytes[2]|=0x02;

Bytes[2]= 0x00 = 0 = 0000 0000 0000 0000 0000 0000 0000 0000 将第三个16进制元素byte转换成10进制,然后再将其转换成2进制

Bytes[2]二进制:0000 0000 0000 0000 0000 0000 0000 0000 

0x02二进制     :0000 0000 0000 0000 0000 0000 0000 0010 

或运算结果:0000 0000 0000 0000 0000 0000 0000 0010

结果说明 位9 已为1,表示电源被切断。


⑥位10表示Acc状态:

位计算:

因为位8~位10处于Bytes数组元素的第2个即Bytes[2](从0开始,所以元素下标为0~3),所以只需要将位10进行赋值即可。

Bytes[2]|=0x04;

Bytes[2]= 0x00 = 0 = 0000 0000 0000 0000 0000 0000 0000 0000 将第三个16进制元素byte转换成10进制,然后再将其转换成2进制

Bytes[2]二进制:0000 0000 0000 0000 0000 0000 0000 0000 

0x04二进制     :0000 0000 0000 0000 0000 0000 0000 0100 

或运算结果:0000 0000 0000 0000 0000 0000 0000 0100

结果说明 位10 已为1,表示Acc开。

6个位的值全部赋值完后,Bytes数组的元素应为: 0x00 , 0x00 , 0x07 , 0x07

转换成10进制后应为: 0 , 0 , 7 , 7

		// 输出结果:0,0,7,7
		for (int i = 0; i < Bytes.length; i++) 
			int v = Bytes[i] & 0xFF;
			if (i == Bytes.length - 1) 
				result += v + "";
			 else 
				result += v + ",";
			
		

若转换成2进制后应为(简写): 0 , 0 , 111 , 111 

再将Bytes转换成10进制应为:1799;1799 对应的二进制数据为:0000 0000 0000 0000 0000 0111 0000 0111

红色的部分正好对应 位0~位2位8~位10

至此,关于Byte通信协议的介绍就结束了,如发现其中存在问题的,欢迎随时批评指正,以免误人子弟。因为本博客的目的是帮助更多的人,而不是害读者,所以各位读者发现问题请在评论区评论指出,谢谢大家!





以上是关于Android开发中遇到关于Byte位运算通信协议类项目的文档解读分析的主要内容,如果未能解决你的问题,请参考以下文章

Android与单片机 | 开发板 | 智能硬件 | 智能设备 | 数据协议 |开发总结

如何查询手柄与PC通信的数据格式?USB HID协议方面的问题

23.Python位运算符详解

网络协议编码

位运算的运用场景使用总结

Android的fastboot协议