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





  • 6
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
要定义蓝牙通信协议,你需要在Android Studio使用Bluetooth API。下面是一些步骤: 1. 添加蓝牙权限到AndroidManifest.xml文件: ```xml <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> ``` 2. 初始化BluetoothAdapter: ```java BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if (mBluetoothAdapter == null) { // 设备不支持蓝牙 } ``` 3. 开启蓝牙: ```java if (!mBluetoothAdapter.isEnabled()) { Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); } ``` 4. 扫描蓝牙设备并连接: ```java // 扫描设备 mBluetoothAdapter.startDiscovery(); // 监听扫描结果 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (BluetoothDevice.ACTION_FOUND.equals(action)) { // 发现设备 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); // 连接设备 ConnectThread connectThread = new ConnectThread(device); connectThread.start(); } } }; // 连接设备 private class ConnectThread extends Thread { private final BluetoothSocket mmSocket; private final BluetoothDevice mmDevice; public ConnectThread(BluetoothDevice device) { // 获取一个BluetoothSocket来连接设备 BluetoothSocket tmp = null; mmDevice = device; try { tmp = device.createRfcommSocketToServiceRecord(MY_UUID); } catch (IOException e) { // 错误处理 } mmSocket = tmp; } public void run() { // 取消搜索设备 mBluetoothAdapter.cancelDiscovery(); try { // 连接 mmSocket.connect(); } catch (IOException connectException) { // 错误处理 try { mmSocket.close(); } catch (IOException closeException) { // 错误处理 } return; } // 通信处理 manageConnectedSocket(mmSocket); } public void cancel() { try { mmSocket.close(); } catch (IOException e) { // 错误处理 } } } ``` 5. 通信处理: ```java // 获取输入输出流 InputStream inputStream = mmSocket.getInputStream(); OutputStream outputStream = mmSocket.getOutputStream(); // 发送数据 outputStream.write(data); // 接收数据 byte[] buffer = new byte[1024]; int bytes; while (true) { try { bytes = inputStream.read(buffer); String message = new String(buffer, 0, bytes); // 处理接收到的数据 } catch (IOException e) { break; } } ``` 以上是一些基本的步骤,具体的通信协议需要根据你的需求来定义。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Engineer-Jsp

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值