蓝牙4.0和蓝牙多通道
蓝牙4.0和蓝牙多通道
1.首先连接蓝牙的时候需要在配置文件manifest中增加权限
1 | <uses-permission android:name=”android.permission.BLUETOOTH” /> |
2 | <uses-permission android:name=”android.permission.BLUETOOTH_ADMIN” /> |
这个时候我们才算正式开始进入主题
2.判断你的移动设备是否支持蓝牙
如果你想声明你的应用程序只能在支持BLE的设备上运行,可以将下面声明包含进你的应用程序manifest文<uses-feature android:name=”android.hardware.bluetooth_le” android:required=”true”/>
想让你的应用程序也能够在不支持BLE的设备上运行,你就应该将上面标签中的属性设置为required=”false”。然后在运行的过程中使用PackageManager.hasSystemFeature()方法来判断设备是否支持BLE:
PackageManager.hasSystemFeature()方法来判断设备是否支持BLE:
1 if(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)){
2 Toast.makeText(this, “没有可提供的蓝牙设备”, Toast.LENGTH_SHORT).show();
3 finish();
4 }
3.获取蓝牙适配器 BluetoothAdapter
先声明 private BluetoothAdapter mBluetoothAdapter;
然后
1 final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
2 mBluetoothAdapter = bluetoothManager.getAdapter();
1 //如果蓝牙设配器里面没有数据的话就让它返回if(mBluetoothAdapter == null){
2 Toast.makeText(this, “没有可提供的蓝牙设备”, Toast.LENGTH_SHORT).show();
3 finish();
4 return;
5 }
4.搜索蓝牙
/**
* 1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
3.对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
*/
//我这里使用了需要连接多台蓝牙设备 所有放在list里面
private LinkedList mDeviceContainer = new LinkedList();
private ArrayList mDeviceList = new ArrayList();
1 | private void scanLeDevice(final boolean enable){ |
2 | if(enable){ |
3 | new Thread(new Runnable() { |
4 | |
5 | @Override |
6 | public void run() { |
7 | try { |
8 | Thread.sleep(SCAN_PERIOD); |
9 | if(mScanning){ //10秒钟后停止搜索 |
10 | mScanning = false; //状态标记为false |
11 | |
12 | mBluetoothAdapter.stopLeScan(mLeScanCallback); |
13 | invalidateOptionsMenu(); |
14 | } |
15 | |
16 | } catch (InterruptedException e) { |
17 | |
18 | e.printStackTrace(); |
19 | } |
20 | |
21 | } |
22 | }).start(); |
23 | |
24 | mScanning = true; |
25 | mBluetoothAdapter.startLeScan(mLeScanCallback); //开始搜索 |
26 | }else { |
27 | mScanning = false; |
28 | mBluetoothAdapter.stopLeScan(mLeScanCallback); //停止搜索 |
29 | } |
30 | |
31 | invalidateOptionsMenu(); |
32 | } |
5.搜到设备的时候有一个回调接口
1 | private BluetoothAdapter.LeScanCallback mLeScanCallback = new LeScanCallback() { |
2 | |
3 | @Override |
4 | public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) { |
5 | runOnUiThread(new Runnable() { |
6 | |
7 | @Override |
8 | public void run() { |
9 | if(!mDeviceContainer.isEmpty()){ |
10 | if(!isEquals(device)){ |
11 | connectBle(device); |
12 | } |
13 | }else{ |
14 | connectBle(device); |
15 | } |
16 | |
17 | } |
18 | }); |
19 | |
20 | } |
21 | }; |
6.启用蓝牙模块
1 | private final ServiceConnection mServiceConnection = new ServiceConnection() { |
2 | |
3 | @Override |
4 | public void onServiceDisconnected(ComponentName name) { |
5 | |
6 | mBluetoothLeService = null; |
7 | } |
8 | |
9 | @Override |
10 | public void onServiceConnected(ComponentName name, IBinder service) { |
11 | mBluetoothLeService = ((TemperatureServices.LocalBinder)service).getService(); |
12 | if(!mBluetoothLeService.initialize()){ |
13 | Log.e(TAG, “Unable to initialize Bluetooth”); |
14 | finish(); |
15 | } |
16 | Log.e(TAG, “mBluetoothLeService is okay”); |
17 | } |
18 | }; |
19 | |
20 | /** |
21 | *接收从BlutoothLeService发送过来的广播 |
22 | 包扣 连接 未连接 以及发送过来的数据 |
23 | */ |
24 | private final BroadcastReceiver mGattUpdateRecevicer = new BroadcastReceiver() { |
25 | |
26 | @Override |
27 | public void onReceive(Context context, Intent intent) { |
28 | final String action = intent.getAction(); |
29 | Bundle extras = intent.getExtras(); |
30 | |
31 | if(TemperatureServices.ACTION_GATT_CONNECTED.equals(action)){ |
32 | Log.i(TAG, “Only gatt, just wait”); |
33 | }else if(TemperatureServices.ACTION_GATT_DISCONNECTED.equals(action)){ |
34 | if(!mDeviceList.isEmpty()){ |
35 | String strAddress = intent.getStringExtra(“DEVICE_ADDRESS”); |
36 | if(removeDevice(strAddress)){ |
37 | int deviceNum = mDeviceList.size() – 1; |
38 | numDevice.setText(deviceText+deviceNum); |
39 | } |
40 | } |
41 | |
42 | invalidateOptionsMenu(); |
43 | }else if(TemperatureServices.ACTION_GATT_SERVICES_DISCOVERED.equals(action)){ |
44 | if(!mDeviceContainer.isEmpty()){ |
45 | String strAddress =intent.getStringExtra(“DEVICE_ADDRESS”); |
46 | |
47 | for(BluetoothDevice bluetoothDevice:mDeviceContainer){ |
48 | if(bluetoothDevice.getAddress().equals(strAddress)){ |
49 | mDeviceList.add(bluetoothDevice); |
50 | } |
51 | } |
52 | } |
53 | numDevice.setText(deviceText+mDeviceList.size()); |
54 | |
55 | Log.e(TAG, “Discover GATT Services”); |
56 | invalidateOptionsMenu(); |
57 | |
58 | }else if(TemperatureServices.ACTION_DATA_AVAILABLE.equals(action)){ |
59 | Log.i(TAG, “ACTION_DATA_AVAILABLE”); |
60 | String data = intent.getStringExtra(TemperatureServices.EXTRA_DATA_TEMP); |
61 | if(extras.containsKey(TemperatureServices.EXTRA_DATA_TEMP)){ |
62 | |
63 | if (data != null) { |
64 | if (mDataField.length() > 500) { |
65 | mDataField.setText(“”); |
66 | } |
67 | mDataField.append(data); |
68 | Log.i(“==temp_data==”, data); // 打印温度 |
69 | } |
70 | }else if (extras.containsKey(TemperatureService.EXTRA_DATA)) { |
71 | // 如果是蓝牙的读取通知到显示 |
72 | |
73 | } |
74 | } |
75 | |
76 | |
77 | |
78 | } |
79 | }; |
1 | /** |
2 | * 连接蓝牙 |
3 | * @param device |
4 | */ |
5 | private void connectBle(BluetoothDevice device){ |
6 | mDeviceContainer.add(device); |
7 | while (true) { |
8 | if(mBluetoothLeService!=null){ |
9 | mBluetoothLeService.connect(device.getAddress(), this); |
10 | break; |
11 | }else{ |
12 | try{ |
13 | Thread.sleep(250); |
14 | |
15 | }catch(InterruptedException e){ |
16 | e.printStackTrace(); |
17 | } |
18 | } |
19 | |
20 | } |
21 | } |
7.*主要的 服务
从上面代码中我们可以看到 有一个mBluetoothLeSerivice 它正是我们创建的BluetoothLeService他继承了Service。接下来这个类我详细讲解。
1 | public class BluetoothLeService extends Service{ |
2 | private static final String TAG=”BluetoothLeService”; //这里是用来打印的 |
3 | private String mBluetoothDeviceAddress; //蓝牙地址 |
4 | private BluetoothManager mBluetoothManager; //通过BluetoothManager来获取BluetoothAdapter |
5 | private BluetoothGatt mBluetoothGatt; //通过BluetoothGatt可以连接设备(connect),发现服务(discoverServices),并把相应地属性返回到BluetoothGattCallback |
6 | |
7 | //常用的广播 |
8 | public final static String ACTION_GATT_CONNECTED = “com.example.bluetooth.le.ACTION_GATT_CONNECTED”; //连接 |
9 | public final static String ACTION_GATT_DISCONNECTED = “com.example.bluetooth.le.ACTION_GATT_DISCONNECTED”; //断开链接 |
10 | public final static String ACTION_GATT_SERVICES_DISCOVERED = “com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED”; //发现设备 |
11 | public final static String ACTION_DATA_AVAILABLE = “com.example.bluetooth.le.ACTION_DATA_AVAILABLE”; //官方的demo里面有这四个 |
12 | //这2个是我自己需要读取的所以增加了 |
13 | public final static String EXTRA_DATA = “com.example.bluetooth.le.EXTRA_DATA”; //有数据的时候传输数据 |
14 | |
15 | public final static String EXTRA_DATA_TEMP = “com.example.bluetooth.le.EXTRA_DATA_TEMP”; //温度类型变量 |
16 | |
17 | private BluetoothAdapter mBluetoothAdapter = null; |
18 | |
19 | private ArrayList<BluetoothGatt> connectionQueue = new ArrayList<BluetoothGatt>(); //创建一个BluetoothGatt的队列 |
20 | //相当于一个数据类型,它包括一个value和0~n个value的描述(BluetoothGattDescriptor) |
21 | public BluetoothGattCharacteristic mNotifyCharacteristic; |
22 | |
23 | private Context mContext; //上下文 |
24 | |
25 | //查找服务 |
26 | public void findService(BluetoothGatt gatt){ |
27 | List<BluetoothGattService> gattservices = gatt.getServices(); |
28 | Log.i(TAG,”Count is:”+gattservices.size()); |
29 | for(BluetoothGattService gattservice : gattservices){ |
30 | Log.i(TAG,gattservice.getUuid().toString()); |
31 | //如果服务要等于温度服务 |
32 | if(gattservice.getUuid().toString().equalsIgnoreCase(GattAttributes.HEALTH_THERMO_SERVICE)){ |
33 | List<BluetoothGattCharacteristic> gattCharacteristics = gattservice.getCharacteristics(); |
34 | Log.i(TAG,”Count is:”+gattCharacteristics.size()); |
35 | |
36 | |
37 | for(BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics){ |
38 | String uuidchara = gattCharacteristic.getUuid().toString(); |
39 | //如果是温度计的类型的话 |
40 | /** |
41 | 注意:这里的GattAttributes 这个值相当于 |
42 | //实际用你们自己的 |
43 | public static final String HEALTH_TEMP_MEASUREMENT = “0000xxxx-0000-1000-8000-00805f9b34fb”; |
44 | */ |
45 | if(uuidchara.equalsIgnoreCase(GattAttributes.TEMPERATURE_TYPE)){ |
46 | prepareBroadcastDataRead(gattCharacteristic); |
47 | } |
48 | |
49 | //如果是温度计的度数的时候 |
50 | else if(uuidchara.equalsIgnoreCase(GattAttributes.HEALTH_TEMP_MEASUREMENT)){ |
51 | prepareBroadcastDataIndicate(gattCharacteristic); |
52 | |
53 | //发现服务获取设备的地址 |
54 | brocastUpdate(ACTION_GATT_SERVICES_DISCOVERED, gatt.getDevice().getAddress()); |
55 | } |
56 | } |
57 | } |
58 | } |
59 | |
60 | |
61 | } |
62 | |
63 | /** |
64 | * *重要的回调 |
65 | */ |
66 | private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { |
67 | |
68 | @Override |
69 | public void onConnectionStateChange(BluetoothGatt gatt,int status,int newState){ |
70 | String initAction; |
71 | Log.i(TAG,”oldStatus = “+status+”NewStates”+newState); //打印出来 |
72 | if(status == BluetoothGatt.GATT_SUCCESS){ //这个值一般返回的为0 |
73 | //如果连接成功 |
74 | if(newState == BluetoothProfile.STATE_CONNECTED){ |
75 | initAction = ACTION_GATT_CONNECTED; |
76 | |
77 | brocastUpdate(initAction); |
78 | |
79 | //连接之后马上去发现服务 |
80 | gatt.discoverServices(); |
81 | } |
82 | }else if(newState == BluetoothProfile.STATE_DISCONNECTED){ //断开连接 |
83 | initAction = ACTION_GATT_DISCONNECTED; |
84 | |
85 | brocastUpdate(initAction, gatt.getDevice().getAddress()); |
86 | } |
87 | |
88 | } |
89 | |
90 | @Override |
91 | public void onServicesDiscovered(BluetoothGatt gatt, int status) { |
92 | if(status == BluetoothGatt.GATT_SUCCESS){ |
93 | Log.w(TAG, “onServicesDiscovered received: ” + status); |
94 | findService(gatt); |
95 | }else{ |
96 | if(gatt.getDevice().getUuids() == null){ |
97 | Log.w(TAG, “onServicesDiscovered received: ” + status); |
98 | } |
99 | } |
100 | }; |
101 | //这个是读取数据 |
102 | @Override |
103 | public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { |
104 | if(status == BluetoothGatt.GATT_SUCCESS){ |
105 | brocastUpdate(ACTION_DATA_AVAILABLE, characteristic); |
106 | } |
107 | }; |
108 | |
109 | //当数据发生改变的时候 |
110 | @Override |
111 | public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { |
112 | |
113 | brocastUpdate(ACTION_DATA_AVAILABLE, characteristic); |
114 | |
115 | }; |
116 | //写入数据 |
117 | @Override |
118 | public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { |
119 | |
120 | if (status == BluetoothGatt.GATT_SUCCESS) { |
121 | System.out.println(“onDescriptorWriteonDescriptorWrite = ” + status + “, descriptor =” + descriptor.getUuid().toString()); |
122 | } |
123 | }; |
124 | }; |
125 | |
126 | |
127 | |
128 | |
129 | /** |
130 | * 接收广播可读的特征 |
131 | */ |
132 | public void prepareBroadcastDataRead(BluetoothGattCharacteristic gattCharacteristic){ |
133 | if((gattCharacteristic.getProperties()|BluetoothGattCharacteristic.PROPERTY_READ)>0){ |
134 | readCharacteristic(gattCharacteristic); //当特征为可读的特征的时候就直接可以读取 |
135 | } |
136 | } |
137 | |
138 | /** |
139 | * 接收广播通知特性 |
140 | */ |
141 | public void prepareBroadcastDataIndicate(BluetoothGattCharacteristic gattCharacteristic){ |
142 | if((gattCharacteristic.getProperties()|BluetoothGattCharacteristic.PROPERTY_INDICATE)>0){ |
143 | setCharacteristicIndication(gattCharacteristic, |
144 | true); //当特征为不可读的时候哪么就是为通知 |
145 | } |
146 | } |
147 | |
148 | /** |
149 | * 处理连接蓝牙后进入这里面操作 |
150 | */ |
151 | private final IBinder mBinder = new LocalBinder(); |
152 | |
153 | public class LocalBinder extends Binder{ |
154 | public TemperatureServices getService(){ |
155 | return TemperatureServices.this; |
156 | } |
157 | } |
158 | |
159 | |
160 | @Override |
161 | public IBinder onBind(Intent intent) { |
162 | // TODO Auto-generated method stub |
163 | return mBinder; |
164 | } |
165 | |
166 | @Override |
167 | public boolean onUnbind(Intent intent) { |
168 | // After using a given device, you should make sure that BluetoothGatt.close() is called |
169 | // such that resources are cleaned up properly. In this particular example, close() is |
170 | // invoked when the UI is disconnected from the Service. |
171 | close(); |
172 | return super.onUnbind(intent); |
173 | } |
174 | |
175 | |
176 | /** |
177 | * 对外事件处理类 |
178 | * 更新广播事件 |
179 | */ |
180 | private void brocastUpdate(final String action){ |
181 | final Intent intent = new Intent(action); |
182 | sendBroadcast(intent); |
183 | } |
184 | |
185 | /** |
186 | * 传地址广播 |
187 | * @param action |
188 | * @param strAddress |
189 | */ |
190 | private void brocastUpdate(final String action,final String strAddress){ |
191 | final Intent intent = new Intent(action); |
192 | intent.putExtra(“DEVICE_ADDRESS”, strAddress); |
193 | sendBroadcast(intent); |
194 | } |
195 | |
196 | private void brocastUpdate(final String action,final BluetoothGattCharacteristic characteristic){ |
197 | final Intent intent = new Intent(action); |
198 | |
199 | //当温度计为温度类型的时候 |
200 | if(characteristic.getUuid().equals(UUIDDatabase.UUID_HEALTH_THERMOMETER_SENSOR_LOCATION)){ |
201 | String a = HTMParser.getHealthThermoSensorLocation(characteristic, mContext); |
202 | intent.putExtra(EXTRA_DATA, a); |
203 | } |
204 | |
205 | else if(characteristic.getUuid().equals(UUIDDatabase.UUID_HEALTH_THERMOMETER)){ |
206 | String health_temp = HTMParser.getHealthThermo(characteristic, mContext); |
207 | intent.putExtra(EXTRA_DATA_TEMP, health_temp); |
208 | } |
209 | |
210 | sendBroadcast(intent); |
211 | } |
212 | |
213 | /** |
214 | * 初始化一个参考本地蓝牙适配器 |
215 | * @return |
216 | */ |
217 | public boolean initialize(){ |
218 | if(mBluetoothManager == null){ |
219 | mBluetoothManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE); |
220 | if(mBluetoothManager == null){ |
221 | Log.e(TAG,”Unable to initialize BluetoothManager.”); |
222 | return false; |
223 | } |
224 | } |
225 | |
226 | mBluetoothAdapter = mBluetoothManager.getAdapter(); |
227 | if(mBluetoothAdapter == null){ |
228 | Log.e(TAG, “Unable to obtain a BluetoothAdapter.”); |
229 | return false; |
230 | } |
231 | |
232 | return true; |
233 | } |
234 | |
235 | |
236 | /** |
237 | * 连接代码 |
238 | * @param address |
239 | * @param context |
240 | * @return |
241 | */ |
242 | public boolean connect(final String address,Context context){ |
243 | mContext = context; |
244 | if(mBluetoothAdapter == null && address == null){ |
245 | Log.w(TAG, “BluetoothAdapter not initialized or unspecified address.”); |
246 | return false; |
247 | } |
248 | |
249 | BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); |
250 | |
251 | if(device == null){ |
252 | Log.w(TAG,”Device not found. Unable to connect.”); |
253 | return false; |
254 | } |
255 | |
256 | BluetoothGatt bluetoothGatt; |
257 | |
258 | bluetoothGatt = device.connectGatt(mContext, false, mGattCallback); |
259 | if(checkGatt(bluetoothGatt)){ |
260 | connectionQueue.add(bluetoothGatt); |
261 | } |
262 | |
263 | Log.d(TAG, “Trying to create a new connection.”); |
264 | |
265 | return true; |
266 | |
267 | |
268 | } |
269 | |
270 | /** |
271 | * 检查是否加入了相同的特征 如果相同就移除掉 |
272 | * @param bluetoothGatt |
273 | * @return |
274 | */ |
275 | private boolean checkGatt(BluetoothGatt bluetoothGatt){ |
276 | if(!connectionQueue.isEmpty()){ |
277 | for(BluetoothGatt btg:connectionQueue){ |
278 | if(btg.equals(bluetoothGatt)){ |
279 | return false; |
280 | } |
281 | } |
282 | } |
283 | |
284 | return true; |
285 | } |
286 | |
287 | /** |
288 | * 断开蓝牙连接 一按断开是全部都断开 |
289 | */ |
290 | public void disConnect(){ |
291 | if(mBluetoothAdapter == null && connectionQueue.isEmpty()){ |
292 | Log.w(TAG, “BluetoothAdapter not initialized”); |
293 | return; |
294 | } |
295 | |
296 | for(BluetoothGatt bluetoothGatt:connectionQueue){ |
297 | bluetoothGatt.disconnect(); |
298 | } |
299 | } |
300 | |
301 | public void close(){ |
302 | if(connectionQueue.isEmpty()){ |
303 | return; |
304 | } |
305 | |
306 | listClose(null); |
307 | } |
308 | |
309 | /** |
310 | * 清除连接蓝牙的数据 |
311 | * 我这里用的是连接多台的设备,所以清除的时候需要一个一个的移除掉 |
312 | * @param gatt |
313 | */ |
314 | private synchronized void listClose(BluetoothGatt gatt){ |
315 | if(!connectionQueue.isEmpty()){ |
316 | if(gatt!=null){ |
317 | for(final BluetoothGatt bluetoothGatt:connectionQueue){ |
318 | if(bluetoothGatt.equals(gatt)){ |
319 | bluetoothGatt.close(); |
320 | |
321 | new Thread(new Runnable() { |
322 | |
323 | @Override |
324 | public void run() { |
325 | try{ |
326 | Thread.sleep(250); |
327 | connectionQueue.remove(bluetoothGatt); |
328 | |
329 | }catch(Exception ex){ |
330 | ex.printStackTrace(); |
331 | } |
332 | |
333 | } |
334 | }).start(); |
335 | } |
336 | } |
337 | }else{ |
338 | for(BluetoothGatt bluetoothGatt:connectionQueue){ |
339 | bluetoothGatt.close(); |
340 | } |
341 | |
342 | connectionQueue.clear(); |
343 | } |
344 | } |
345 | } |
346 | |
347 | /** |
348 | * 读取蓝牙数据 |
349 | */ |
350 | public void readCharacteristic(BluetoothGattCharacteristic characteristic) { |
351 | if (mBluetoothAdapter == null || connectionQueue.isEmpty()) { |
352 | Log.w(TAG, “BluetoothAdapter not initialized”); |
353 | return; |
354 | } |
355 | for (BluetoothGatt bluetoothGatt : connectionQueue) { |
356 | bluetoothGatt.readCharacteristic(characteristic); |
357 | } |
358 | } |
359 | |
360 | /** |
361 | * Enables or disables notification on a give characteristic. |
362 | * |
363 | * @param characteristic Characteristic to act on. |
364 | * @param enabled If true, enable notification. False otherwise. |
365 | */ |
366 | public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, |
367 | boolean enabled) { |
368 | if (mBluetoothAdapter == null || connectionQueue.isEmpty()) { |
369 | Log.w(TAG, “BluetoothAdapter not initialized”); |
370 | return; |
371 | } |
372 | |
373 | for(BluetoothGatt bluetoothGatt:connectionQueue){ |
374 | bluetoothGatt.setCharacteristicNotification(characteristic, enabled); |
375 | } |
376 | } |
377 | |
378 | /** |
379 | * 蓝牙温度计*关键的一步 有些特征只能通过这个 读取 并启用通知更新数据 |
380 | * Enables or disables indications on a give characteristic. |
381 | * 启用或禁用一个给定特性的指示 |
382 | * @param characteristic Characteristic to act on. |
383 | * @param enabled If true, enable indications. False otherwise. |
384 | */ |
385 | public void setCharacteristicIndication( |
386 | BluetoothGattCharacteristic characteristic, boolean enabled) { |
387 | String serviceUUID = characteristic.getService().getUuid().toString(); |
388 | |
389 | String characteristicUUID = characteristic.getUuid().toString(); |
390 | |
391 | Log.i(“==TAG==”,serviceUUID+” “+characteristicUUID); |
392 | |
393 | for(BluetoothGatt mBluetoothGatt:connectionQueue){ |
394 | |
395 | if (mBluetoothAdapter == null || mBluetoothGatt == null) { |
396 | return; |
397 | } |
398 | |
399 | if (characteristic.getDescriptor(UUID |
400 | .fromString(GattAttributes.CLIENT_CHARACTERISTIC_CONFIG)) != null) { |
401 | if (enabled == true) { |
402 | BluetoothGattDescriptor descriptor = characteristic |
403 | .getDescriptor(UUID |
404 | .fromString(GattAttributes.CLIENT_CHARACTERISTIC_CONFIG)); |
405 | descriptor |
406 | .setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE); |
407 | mBluetoothGatt.writeDescriptor(descriptor); |
408 | |
409 | } else { |
410 | BluetoothGattDescriptor descriptor = characteristic |
411 | .getDescriptor(UUID |
412 | .fromString(GattAttributes.CLIENT_CHARACTERISTIC_CONFIG)); |
413 | descriptor |
414 | .setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE); |
415 | mBluetoothGatt.writeDescriptor(descriptor); |
416 | |
417 | } |
418 | } |
419 | mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); |
420 | } |
421 | } |
422 | |
423 | |
424 | } |
其中我里面用了一些外部的方法代码如下
/**
* Class used for parsing Health temperature related information
* 用于解析温度相关信息的类
*/
1 | public class HTMParser { |
2 | |
3 | private static ArrayList<String> mTempInfo = new ArrayList<String>(); |
4 | |
5 | //Byte character format |
6 | private static final String BYTE_CHAR_FORMAT = “%02X “; |
7 | |
8 | //Switch case Constants |
9 | private static final int CASE_ARMPIT = 1; |
10 | private static final int CASE_BODY = 2; |
11 | private static final int CASE_EAR_LOBE = 3; |
12 | private static final int CASE_FINGER = 4; |
13 | private static final int CASE_GIT = 5; |
14 | private static final int CASE_MOUTH = 6; |
15 | private static final int CASE_RECTUM = 7; |
16 | private static final int CASE_TYMPANUM = 8; |
17 | private static final int CASE_TOE = 9; |
18 | private static final int CASE_TOE_REP = 10; |
19 | |
20 | /** |
21 | * Get the thermometer reading |
22 | * 温度计的读数 |
23 | * |
24 | * @param characteristic |
25 | * @return |
26 | */ |
27 | public static String getHealthThermo( |
28 | BluetoothGattCharacteristic characteristic, Context context) { |
29 | String tempUnit = “”; |
30 | // For all other profiles, writes the data formatted in HEX. |
31 | final byte[] data = characteristic.getValue(); |
32 | if (data != null && data.length > 0) { |
33 | final StringBuilder stringBuilder = new StringBuilder(data.length); |
34 | byte flagByte = data[0]; |
35 | if ((flagByte & 0x01) != 0) { |
36 | tempUnit = context.getString(R.string.tt_fahren_heit); |
37 | } else { |
38 | tempUnit = context.getString(R.string.tt_celcius); |
39 | } |
40 | for (byte byteChar : data) |
41 | stringBuilder.append(String.format(BYTE_CHAR_FORMAT, byteChar)); |
42 | } |
43 | final float temperature = characteristic.getFloatValue(BluetoothGattCharacteristic.FORMAT_FLOAT, 1); |
44 | |
45 | //Logger.i(“tempRate ” + temperature); |
46 | |
47 | String ss = temperature+”,”+tempUnit; |
48 | //mTempInfo.add(1, tempUnit); |
49 | return ss; |
50 | } |
51 | |
52 | /** |
53 | * Get the thermometer sensor location |
54 | * |
55 | * @param characteristic |
56 | * @return |
57 | */ |
58 | public static String getHealthThermoSensorLocation( |
59 | BluetoothGattCharacteristic characteristic, Context context) { |
60 | String healthTherSensorLocation = “”; |
61 | final byte[] data = characteristic.getValue(); |
62 | if (data != null && data.length > 0) { |
63 | final StringBuilder stringBuilder = new StringBuilder(data.length); |
64 | for (byte byteChar : data) |
65 | stringBuilder.append(String.format(BYTE_CHAR_FORMAT, byteChar)); |
66 | int healthBodySensor = Integer.valueOf(stringBuilder.toString() |
67 | .trim()); |
68 | switch (healthBodySensor) { |
69 | case CASE_ARMPIT: |
70 | //这个R.string.armpit 里面就是这个airpit的英文意思 |
71 | healthTherSensorLocation = context.getString(R.string.armpit); |
72 | break; |
73 | case CASE_BODY: |
74 | healthTherSensorLocation = context.getString(R.string.body); |
75 | break; |
76 | case CASE_EAR_LOBE: |
77 | healthTherSensorLocation = context.getString(R.string.ear); |
78 | break; |
79 | case CASE_FINGER: |
80 | healthTherSensorLocation = context.getString(R.string.finger); |
81 | break; |
82 | case CASE_GIT: |
83 | healthTherSensorLocation = context.getString(R.string.intestine); |
84 | break; |
85 | case CASE_MOUTH: |
86 | healthTherSensorLocation = context.getString(R.string.mouth); |
87 | break; |
88 | case CASE_RECTUM: |
89 | healthTherSensorLocation = context.getString(R.string.rectum); |
90 | break; |
91 | case CASE_TYMPANUM: |
92 | healthTherSensorLocation = context.getString(R.string.tympanum); |
93 | break; |
94 | case CASE_TOE: |
95 | healthTherSensorLocation = context.getString(R.string.toe_1); |
96 | break; |
97 | case CASE_TOE_REP: |
98 | healthTherSensorLocation = context.getString(R.string.toe_2); |
99 | break; |
100 | default: |
101 | healthTherSensorLocation = context.getString(R.string.reserverd); |
102 | break; |
103 | } |
104 | |
105 | } |
106 | return healthTherSensorLocation; |
107 | } |
108 | } |
8.总结:
蓝牙连接*主要的是要掌握 BluetoothLeService这个类里面的内容,主要包扣BluetoothGattCallback 回调
这里面是处理蓝牙数据的核心,包扣连接设备回调 断开设备回调 发现服务回调 发现数据回调。多台连接蓝牙设备的时候 把BluetoothGatt 放到一个List里面去。断开的时候也要移次的移除掉 list里面的BluetoothGatt数据。