-
1
-
2
-
3
BluetoothGattService
蓝牙设备所拥有的服务。在这里比喻成 班级 吧。bluetoothdevice就比喻成学校吧。 一个学校可以有很多个班级。班级 根据UUID来区别。
BluetoothGattCharacteristic
蓝牙设备所拥有的特征。比喻成 学生。 一个班级里也可以有很多个学生。学生也是根据UUID 来区别
当你需要用手机来和蓝牙设备通信的时候,就相当于 你想 和一个学生交流,你得先知道 这个学生的学号,所在班级号,就是开发中的所说的CharacUUID, ServiceUUID
BluetoothGattCallback
所有操作的回调函数。
private BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
@Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { super.onCharacteristicChanged(gatt, characteristic); //收到设备notify值 (设备上报值) } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicRead(gatt, characteristic, status); //读取到值 } @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicWrite(gatt, characteristic, status); if (status == BluetoothGatt.GATT_SUCCESS) { //write成功(发送值成功) } } @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { super.onConnectionStateChange(gatt, status, newState); if (status == BluetoothGatt.GATT_SUCCESS) { if (newState == BluetoothGatt.STATE_CONNECTED) { // 连接成功 } else if (newState == BluetoothGatt.STATE_DISCONNECTED) { // 断开连接 } } } @Override public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { super.onDescriptorRead(gatt, descriptor, status); } @Override public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { super.onDescriptorWrite(gatt, descriptor, status); } @Override public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { super.onReadRemoteRssi(gatt, rssi, status); if (status == BluetoothGatt.GATT_SUCCESS) { //获取到RSSI, RSSI 正常情况下 是 一个 负值,如 -33 ; 这个值的绝对值越小,代表设备离手机越近 //通过mBluetoothGatt.readRemoteRssi();来获取 } } @Override public void onReliableWriteCompleted(BluetoothGatt gatt, int status) { super.onReliableWriteCompleted(gatt, status); } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { super.onServicesDiscovered(gatt, status); if (status == BluetoothGatt.GATT_SUCCESS) { //寻找到服务 } } }; --------------------------------------------------------------------------------------------------------------------------------------- 当调用了连接函数 mBluetoothGatt = bluetoothDevice.connectGatt(this.context, false, gattCallback);之后, 如果连接成功就会 走到 连接状态回调: @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { if (status == BluetoothGatt.GATT_SUCCESS) { //首先判断这个status 如果等于 BluetoothGatt.GATT_SUCCESS(value=0)代表这个回调是正常的, //如果不等于 0,那边就代表没有成功,也不需要进行其他操作了, // 连接成功和断开连接都会走到这里 if (newState == BluetoothGatt.STATE_CONNECTED) { // 连接成功 //连接成功之后,我们应该立刻去寻找服务(上面提到的BluetoothGattService),只有寻找到服务之后,才可以和设备进行通信 gatt.discoverServices();// 寻找服务 } else if (newState == BluetoothGatt.STATE_DISCONNECTED) { // 断开连接 } } } 当判断到连接成功之后,会去寻找服务, 这个过程是异步的,会耗点时间,当寻找到服务之后,会走到回调: @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { super.onServicesDiscovered(gatt, status); if (status == BluetoothGatt.GATT_SUCCESS) { //寻找到服务 //寻找服务之后,我们就可以和设备进行通信,比如下发配置值,获取设备电量什么的 readBatrery(); //读取电量操作 sendSetting(); //下发配置值 } } /***读操作***/ void readBatrery(){ //如上面所说,想要和一个学生通信,先知道他的班级(ServiceUUID)和学号(CharacUUID) BluetoothGattService batteryService=mBluetoothGatt.getService(UUID.fromString("0000180f-0000-1000-8000-00805f9b34fb")); //此处的0000180f...是举例,实际开发需要询问硬件那边 if(batteryService!=null){ BluetoothGattCharacteristic batteryCharacteristic=batteryService.getCharacteristic(UUID.fromString("00002a19-0000-1000-8000-00805f9b34fb"));//此处的00002a19...是举例,实际开发需要询问硬件那边 if(batteryCharacteristic!=null){ mBluetoothGatt.readCharacteristic(batteryCharacteristic); //读取电量, 这是读取batteryCharacteristic值的方法,读取其他的值也是如此,只是它们的ServiceUUID 和CharacUUID不一样 } } } 如果读取电量(或者读取其他值)成功之后 ,会来到 回调: @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicRead(gatt, characteristic, status); //读取到值,根据UUID来判断读到的是什么值 if (characteristic.getUuid().toString() .equals("00002a19-0000-1000-8000-00805f9b34fb")) {// 获取到电量 int battery = characteristic.getValue()[0]; } } /***写操作***/ void sendSetting(){ BluetoothGattService sendService=mBluetoothGatt.getService(UUID.fromString("00001805-0000-1000-8000-00805f9b34fb"));//此处的00001805...是举例,实际开发需要询问硬件那边 if(sendService!=null){ BluetoothGattCharacteristic sendCharacteristic=sendService.getCharacteristic(UUID.fromString("00002a08-0000-1000-8000-00805f9b34fb"));//此处的00002a08...是举例,实际开发需要询问硬件那边 if(sendCharacteristic!=null){ sendCharacteristic.setValue(new byte[] { 0x01,0x20,0x03 });//随便举个数据 mBluetoothGatt.writeCharacteristic(sendCharacteristic);//写命令到设备, } } } 如果下发配置成功之后,会来到回调: @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicWrite(gatt, characteristic, status); if (status == BluetoothGatt.GATT_SUCCESS) { //write成功(发送值成功),可以根据 characteristic.getValue()来判断是哪个值发送成功了,比如 连接上设备之后你有一大串命令需要下发,你调用多次写命令, 这样你需要判断是不是所有命令都成功了,因为android不太稳定,有必要来check命令是否成功,否则你会发现你明明调用 写命令,但是设备那边不响应 } } 讲解完读写操作,还有一个重要操作 就是 Notify(通知) notify 就是让设备 可以发送通知给你,也可以说上报值给你(发送命令给你) 首先你得打开设备的通知功能: //参数 enable 就是打开还是关闭, characteristic就是你想让具有通知功能的BluetoothGattCharacteristic private boolean enableNotification(boolean enable, BluetoothGattCharacteristic characteristic) { if (mBluetoothGatt == null || characteristic == null) return false; if (!mBluetoothGatt.setCharacteristicNotification(characteristic, enable)) return false; BluetoothGattDescriptor clientConfig = characteristic .getDescriptor(UUIDUtils.CCC); if (clientConfig == null) return false; if (enable) { clientConfig .setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); } else { clientConfig .setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE); } return mBluetoothGatt.writeDescriptor(clientConfig); } 一旦设备那边notify 数据给你,你会在回调里收到: @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { super.onCharacteristicChanged(gatt, characteristic); //收到设备notify值 (设备上报值),根据 characteristic.getUUID()来判断是谁发送值给你,根据characteristic.getValue()来获取这个值 }
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
-
32
-
33
-
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
-
43
-
44
-
45
-
46
-
47
-
48
-
49
-
50
-
51
-
52
-
53
-
54
-
55
-
56
-
57
-
58
-
59
-
60
-
61
-
62
-
63
-
64
-
65
-
66
-
67
-
68
-
69
-
70
-
71
-
72
-
73
-
74
-
75
-
76
-
77
-
78
-
79
-
80
-
81
-
82
-
83
-
84
-
85
-
86
-
87
-
88
-
89
-
90
-
91
-
92
-
93
-
94
-
95
-
96
-
97
-
98
-
99
-
100
-
101
-
102
-
103
-
104
-
105
-
106
-
107
-
108
-
109
-
110
-
111
-
112
-
113
-
114
-
115
-
116
-
117
-
118
-
119
-
120
-
121
-
122
-
123
-
124
-
125
-
126
-
127
-
128
-
129
-
130
-
131
-
132
-
133
-
134
-
135
-
136
-
137
-
138
-
139
-
140
-
141
-
142
-
143
-
144
-
145
-
146
-
147
-
148
-
149
-
150
-
151
-
152
-
153
-
154
-
155
-
156
-
157
-
158
-
159
-
160
-
161
-
162
-
163
-
164
-
165
-
166
-
167
-
168
-
169
-
170
-
171
-
172
-
173
-
174
-
175
-
176
-
177
-
178
-
179
-
180
-
181
-
182
-
183
-
184
-
185
-
186
-
187
-
188
-
189
-
190
-
191
-
192
-
193
-
194
-
195
-
196
-
197
=以上属于BLE基本的操作,下面是进阶================
- 关于发送命令, 比如刚连接上设备,你会把很多配置都发送给设备,此时可能会调用多次 write操作,这个时候你应该需要在 write操作之间 加点间隔 例如开启线程,用 Thread.sleep(150), 因为只有收到onCharacteristicWrite回调之后才代表你的值成功了,如果
不加间隔,你会发现可能只会成功一个值。目前测试下来,我才用的是间隔150毫秒比较好,不会太慢,成功率也会高, 可自己实践慢慢调试,这个数值还与设备端update速率有关
在Write 操作的时候,还有个办法可以增快下发速度,且不需要加sleep
就是设置characteristic .setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE); Wrtite characteristic without requiring a response by the remote device, 就是不需 要回复,这样速度会快,测试下来连续发送9个数据,设备端那边打印数据都收到且是正确的,但回调值有点奇怪:
图1 是我的发送指令 ,连续无间隔发送9个命令:
图2,3是 onCharacteristicWrite 回调打印信息:
从图片看出,有的值没有收到 onCharacteristicWrite 回调,如第一个值 { 1, 0x08, 3 } ,有的值回调了2次 如 { 1, 0x04, 3 },可能会觉得有的值没有发送成功吧?
-
1
-
2
以下是 设备端收到值的打印信息:(0x00FF是用做分隔符,可忽略)
可以发现,9个值均接收正常, 所以说 这种快速发值,还是有点可靠的。测试的不多,各位可斟酌使用
-
1
-
2
2.关于重连
提到ble,估计想的最多的就是重连了。重连当然得是自动重连。可能有人想到的的就是 断开之后再来一次bluetoothDevice.connectGatt(this.context, false, gattCallback)行不行?
先看看官方的说法 :
官方说 mbluetoothGatt.connect()方法就是用来重连的, 只要你断线之后 调用此方法就好了。 测试下来 确实可以,但不同手机体验不一样, 但大家知道 android 碎片化现象很严重,什么 flyme,什么MIUI,emotion之类的。所以需要做什么适配。 只调用 mbluetoothGatt.connect()来重连,我归类为2种 三星 手机 和 非三星手机。 三星手机现象: 重连非常迅速 ,断开之后,只要一回到有效范围内。立马重连成功。 就算隔一天你回来 靠近设备,立马连接成功。这简直就是太棒了。 非三星手机现象: 也会重连成功,但这时间无法保证,有可能1分钟,有可能5分钟,10分钟,甚至就连不上了。体验非常不好。 说一下解决办法: 判断手机是否为三星手机: private boolean checkIsSamsung() { //此方法是我自行使用众多三星手机总结出来,不一定很准确 String brand = android.os.Build.BRAND; Log.e("", " brand:" + brand); if (brand.toLowerCase().equals("samsung")) { return true; } return false; } 在设备断线之后, 我们需要进入重连流程: if(isSamsung){ //这里最好 Sleep 300毫秒,测试发现有时候三星手机断线之后立马调用connect会容易蓝牙奔溃 mbluetoothGatt.connect(); }else{ connectGatt(); handler.removeCallbacks(runnableReconnect ); handler.postDelayed(runnableReconnect , 30*1000); } 流程就是断线之后 三星手机 调用官方的connect()函数,而非三星手机 使用 connectGatt() ,相当于重新建立连接,并且每30s去循环询问是否需要重新建立连接,如果已经连接好,取消这个循环。这样能保证过一天回来你也有机会重连上,非三星手机通过这个方法重连体验特别好,也可以达到回来之后瞬间连接的效果 这样的方案目前是比较好的,手机APP也可以很省电。,像很多其他的防丢器类APP,比如nut.研究过这个app,这个软件总是在扫描,一直scan,不管你连上还是不连上。 android的scan是很耗电的,app功能完善之后同时也要考虑电量优化。 这里测试过 手机的耗电量,用示波器来接手机测试的, 三星断线之后 重连的阶段,耗电量大概在20ma-30ma(锁屏电量 3ma), 魅族MX2 在30s循环期间,耗电量大概在15ma(锁屏电量 15ma), 这里推测三星的connect()期间它系统自己在后台使用低频率的扫描,这样可以使他重连是很快的。scan就要耗电,所以它电量会比锁屏上升一点。 魅族MX2采用每30s重新建立连接,没有scan,基本没有额外耗电。 这种 重连流程 用身边的手机 三星S3,S4,note2,MX2,M3,Nexus5测试过都很好用。亲们也可以用其他测试一下。 public void connectGatt() { if (bluetoothDevice != null) { if (mBluetoothGatt != null) { mBluetoothGatt.close(); mBluetoothGatt.disconnect(); mBluetoothGatt = null; } mBluetoothGatt = bluetoothDevice.connectGatt(this.context, false, gattCallback); } else { Log.e(tagString, "the bluetoothDevice is null, please reset the bluetoothDevice"); } } Runnable runnableReconnect = new Runnable() { @Override public void run() { if (! isConnected() ) { handler.postDelayed(this, 30 * 1000); connectGatt(); } } }; 先保存一下,有空再继续写。欢迎各位一起交流
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
-
32
-
33
-
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
-
43
-
44
-
45
-
46
-
47
-
48
-
49
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
最后我想说
为什么很多程序员做不了架构师?
1、良好健康的职业规划很重要,但大多数人都忽略了
2、学习的习惯很重要,持之以恒才是正解。
3、编程思维没能提升一个台阶,局限在了编码,业务,没考虑过选型、扩展
4、身边没有好的架构师引导、培养。所处的圈子对程序员的成长影响巨大。
金九银十面试季,跳槽季,整理面试题已经成了我多年的习惯!在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。
附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
里面包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
m/2024/03/13/H4lCoPEF.jpg" />
最后我想说
为什么很多程序员做不了架构师?
1、良好健康的职业规划很重要,但大多数人都忽略了
2、学习的习惯很重要,持之以恒才是正解。
3、编程思维没能提升一个台阶,局限在了编码,业务,没考虑过选型、扩展
4、身边没有好的架构师引导、培养。所处的圈子对程序员的成长影响巨大。
金九银十面试季,跳槽季,整理面试题已经成了我多年的习惯!在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。
附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
[外链图片转存中…(img-kJY8JY56-1712482905335)]
里面包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!