Android提高之Android手機(jī)與BLE終端通信
最近穿戴設(shè)備發(fā)展得很火,把相關(guān)技術(shù)也帶旺了,其中一項(xiàng)是BLE(Bluetooth Low Energy)。BLE是藍(lán)牙4.0的核心Profile,主打功能是快速搜索,快速連接,超低功耗保持連接和傳輸數(shù)據(jù),弱點(diǎn)是數(shù)據(jù)傳輸速率低,由于BLE的低功耗特點(diǎn),因此普遍用于穿戴設(shè)備。Android 4.3才開始支持BLE API,所以請各位客官把本文代碼運(yùn)行在藍(lán)牙4.0和Android 4.3及其以上的系統(tǒng),另外本文所用的BLE終端是一個(gè)藍(lán)牙4.0的串口藍(lán)牙模塊。
注:筆者的i9100刷了4.4系統(tǒng)后,竟然也能跟BLE藍(lán)牙模塊通信。
BLE分為三部分Service、Characteristic、Descriptor,這三部分都由UUID作為唯一標(biāo)示符。一個(gè)藍(lán)牙4.0的終端可以包含多個(gè)Service,一個(gè)Service可以包含多個(gè)Characteristic,一個(gè)Characteristic包含一個(gè)Value和多個(gè)Descriptor,一個(gè)Descriptor包含一個(gè)Value。一般來說,Characteristic是手機(jī)與BLE終端交換數(shù)據(jù)的關(guān)鍵,Characteristic有較多的跟權(quán)限相關(guān)的字段,例如PERMISSION和PROPERTY,而其中最常用的是PROPERTY,本文所用的BLE藍(lán)牙模塊竟然沒有標(biāo)準(zhǔn)的Characteristic的PERMISSION。Characteristic的PROPERTY可以通過位運(yùn)算符組合來設(shè)置讀寫屬性,例如READ|WRITE、READ|WRITE_NO_RESPONSE|NOTIFY,因此讀取PROPERTY后要分解成所用的組合(本文代碼已含此分解方法)。
本文代碼改自Android 4.3 Sample的BluetoothLeGatt,把冗余代碼去掉,獲取的BLE設(shè)備信息都通過Log,還有一些必要的讀寫藍(lán)牙方法,應(yīng)該算是簡化到大家一看就可以懂了。本文完整代碼可以點(diǎn)擊此處本站下載。
接下來貼出本文運(yùn)行的結(jié)果,首先是連接BLE設(shè)備后,枚舉出設(shè)備所有Service、Characteristic、Descriptor,并且手機(jī)會往Characteristic uuid=0000ffe1-0000-1000-8000-00805f9b34fb寫入“send data->”字符串,BLE終端收到數(shù)據(jù)通過串口傳到PC串口助手:
04-21 18:28:25.465: E/DeviceScanActivity(12254): -->service type:PRIMARY
04-21 18:28:25.465: E/DeviceScanActivity(12254): -->includedServices size:0
04-21 18:28:25.465: E/DeviceScanActivity(12254): -->service uuid:00001800-0000-1000-8000-00805f9b34fb
04-21 18:28:25.465: E/DeviceScanActivity(12254): ---->char uuid:00002a00-0000-1000-8000-00805f9b34fb
04-21 18:28:25.465: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.465: E/DeviceScanActivity(12254): ---->char property:READ
04-21 18:28:25.465: E/DeviceScanActivity(12254): ---->char uuid:00002a01-0000-1000-8000-00805f9b34fb
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char property:READ
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char uuid:00002a02-0000-1000-8000-00805f9b34fb
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char property:READ|WRITE|
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char uuid:00002a03-0000-1000-8000-00805f9b34fb
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.475: E/DeviceScanActivity(12254): ---->char property:READ|WRITE|
04-21 18:28:25.475: E/DeviceScanActivity(12254): ---->char uuid:00002a04-0000-1000-8000-00805f9b34fb
04-21 18:28:25.475: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.475: E/DeviceScanActivity(12254): ---->char property:READ
04-21 18:28:25.475: E/DeviceScanActivity(12254): -->service type:PRIMARY
04-21 18:28:25.475: E/DeviceScanActivity(12254): -->includedServices size:0
04-21 18:28:25.475: E/DeviceScanActivity(12254): -->service uuid:00001801-0000-1000-8000-00805f9b34fb
04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char uuid:00002a05-0000-1000-8000-00805f9b34fb
04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char property:INDICATE
04-21 18:28:25.480: E/DeviceScanActivity(12254): -------->desc uuid:00002902-0000-1000-8000-00805f9b34fb
04-21 18:28:25.480: E/DeviceScanActivity(12254): -------->desc permission:UNKNOW
04-21 18:28:25.480: E/DeviceScanActivity(12254): -->service type:PRIMARY
04-21 18:28:25.480: E/DeviceScanActivity(12254): -->includedServices size:0
04-21 18:28:25.480: E/DeviceScanActivity(12254): -->service uuid:0000ffe0-0000-1000-8000-00805f9b34fb
04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char uuid:0000ffe1-0000-1000-8000-00805f9b34fb
04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char property:READ|WRITE_NO_RESPONSE|NOTIFY|
04-21 18:28:25.490: E/DeviceScanActivity(12254): -------->desc uuid:00002902-0000-1000-8000-00805f9b34fb
04-21 18:28:25.490: E/DeviceScanActivity(12254): -------->desc permission:UNKNOW
04-21 18:28:25.490: E/DeviceScanActivity(12254): -------->desc uuid:00002901-0000-1000-8000-00805f9b34fb
04-21 18:28:25.490: E/DeviceScanActivity(12254): -------->desc permission:UNKNOW
04-21 18:28:26.025: E/DeviceScanActivity(12254): onCharRead BLE DEVICE read 0000ffe1-0000-1000-8000-00805f9b34fb -> 00
這里紅字是由BluetoothGattCallback的onCharacteristicRead()回調(diào)而打出Log
以下Log是PC上的串口工具通過BLE模塊發(fā)送過來,由BluetoothGattCallback的 onCharacteristicChanged()打出Log
04-21 18:30:18.260: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:18.745: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:19.085: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:19.350: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:19.605: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:19.835: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:20.055: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:20.320: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:20.510: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:20.735: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:21.000: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
接下來貼出本文核心代碼:
public class DeviceScanActivity extends ListActivity { private final static String TAG = DeviceScanActivity.class.getSimpleName(); private final static String UUID_KEY_DATA = "0000ffe1-0000-1000-8000-00805f9b34fb"; private LeDeviceListAdapter mLeDeviceListAdapter; /**搜索BLE終端*/ private BluetoothAdapter mBluetoothAdapter; /**讀寫B(tài)LE終端*/ private BluetoothLeClass mBLE; private boolean mScanning; private Handler mHandler; // Stops scanning after 10 seconds. private static final long SCAN_PERIOD = 10000; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getActionBar().setTitle(R.string.title_devices); mHandler = new Handler(); // Use this check to determine whether BLE is supported on the device. Then you can // selectively disable BLE-related features. if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show(); finish(); } // Initializes a Bluetooth adapter. For API level 18 and above, get a reference to // BluetoothAdapter through BluetoothManager. final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothAdapter = bluetoothManager.getAdapter(); // Checks if Bluetooth is supported on the device. if (mBluetoothAdapter == null) { Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show(); finish(); return; } //開啟藍(lán)牙 mBluetoothAdapter.enable(); mBLE = new BluetoothLeClass(this); if (!mBLE.initialize()) { Log.e(TAG, "Unable to initialize Bluetooth"); finish(); } //發(fā)現(xiàn)BLE終端的Service時(shí)回調(diào) mBLE.setOnServiceDiscoverListener(mOnServiceDiscover); //收到BLE終端數(shù)據(jù)交互的事件 mBLE.setOnDataAvailableListener(mOnDataAvailable); } @Override protected void onResume() { super.onResume(); // Initializes list view adapter. mLeDeviceListAdapter = new LeDeviceListAdapter(this); setListAdapter(mLeDeviceListAdapter); scanLeDevice(true); } @Override protected void onPause() { super.onPause(); scanLeDevice(false); mLeDeviceListAdapter.clear(); mBLE.disconnect(); } @Override protected void onStop() { super.onStop(); mBLE.close(); } @Override protected void onListItemClick(ListView l, View v, int position, long id) { final BluetoothDevice device = mLeDeviceListAdapter.getDevice(position); if (device == null) return; if (mScanning) { mBluetoothAdapter.stopLeScan(mLeScanCallback); mScanning = false; } mBLE.connect(device.getAddress()); } private void scanLeDevice(final boolean enable) { if (enable) { // Stops scanning after a pre-defined scan period. mHandler.postDelayed(new Runnable() { @Override public void run() { mScanning = false; mBluetoothAdapter.stopLeScan(mLeScanCallback); invalidateOptionsMenu(); } }, SCAN_PERIOD); mScanning = true; mBluetoothAdapter.startLeScan(mLeScanCallback); } else { mScanning = false; mBluetoothAdapter.stopLeScan(mLeScanCallback); } invalidateOptionsMenu(); } /** * 搜索到BLE終端服務(wù)的事件 */ private BluetoothLeClass.OnServiceDiscoverListener mOnServiceDiscover = new OnServiceDiscoverListener(){ @Override public void onServiceDiscover(BluetoothGatt gatt) { displayGattServices(mBLE.getSupportedGattServices()); } }; /** * 收到BLE終端數(shù)據(jù)交互的事件 */ private BluetoothLeClass.OnDataAvailableListener mOnDataAvailable = new OnDataAvailableListener(){ /** * BLE終端數(shù)據(jù)被讀的事件 */ @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { if (status == BluetoothGatt.GATT_SUCCESS) Log.e(TAG,"onCharRead "+gatt.getDevice().getName() +" read " +characteristic.getUuid().toString() +" -> " +Utils.bytesToHexString(characteristic.getValue())); } /** * 收到BLE終端寫入數(shù)據(jù)回調(diào) */ @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { Log.e(TAG,"onCharWrite "+gatt.getDevice().getName() +" write " +characteristic.getUuid().toString() +" -> " +new String(characteristic.getValue())); } }; // Device scan callback. private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) { runOnUiThread(new Runnable() { @Override public void run() { mLeDeviceListAdapter.addDevice(device); mLeDeviceListAdapter.notifyDataSetChanged(); } }); } }; private void displayGattServices(List<BluetoothGattService> gattServices) { if (gattServices == null) return; for (BluetoothGattService gattService : gattServices) { //-----Service的字段信息-----// int type = gattService.getType(); Log.e(TAG,"-->service type:"+Utils.getServiceType(type)); Log.e(TAG,"-->includedServices size:"+gattService.getIncludedServices().size()); Log.e(TAG,"-->service uuid:"+gattService.getUuid()); //-----Characteristics的字段信息-----// List<BluetoothGattCharacteristic> gattCharacteristics =gattService.getCharacteristics(); for (final BluetoothGattCharacteristic gattCharacteristic: gattCharacteristics) { Log.e(TAG,"---->char uuid:"+gattCharacteristic.getUuid()); int permission = gattCharacteristic.getPermissions(); Log.e(TAG,"---->char permission:"+Utils.getCharPermission(permission)); int property = gattCharacteristic.getProperties(); Log.e(TAG,"---->char property:"+Utils.getCharPropertie(property)); byte[] data = gattCharacteristic.getValue(); if (data != null && data.length > 0) { Log.e(TAG,"---->char value:"+new String(data)); } //UUID_KEY_DATA是可以跟藍(lán)牙模塊串口通信的Characteristic if(gattCharacteristic.getUuid().toString().equals(UUID_KEY_DATA)){ //測試讀取當(dāng)前Characteristic數(shù)據(jù),會觸發(fā)mOnDataAvailable.onCharacteristicRead() mHandler.postDelayed(new Runnable() { @Override public void run() { mBLE.readCharacteristic(gattCharacteristic); } }, 500); //接受Characteristic被寫的通知,收到藍(lán)牙模塊的數(shù)據(jù)后會觸發(fā)mOnDataAvailable.onCharacteristicWrite() mBLE.setCharacteristicNotification(gattCharacteristic, true); //設(shè)置數(shù)據(jù)內(nèi)容 gattCharacteristic.setValue("send data->"); //往藍(lán)牙模塊寫入數(shù)據(jù) mBLE.writeCharacteristic(gattCharacteristic); } //-----Descriptors的字段信息-----// List<BluetoothGattDescriptor> gattDescriptors = gattCharacteristic.getDescriptors(); for (BluetoothGattDescriptor gattDescriptor : gattDescriptors) { Log.e(TAG, "-------->desc uuid:" + gattDescriptor.getUuid()); int descPermission = gattDescriptor.getPermissions(); Log.e(TAG,"-------->desc permission:"+ Utils.getDescPermission(descPermission)); byte[] desData = gattDescriptor.getValue(); if (desData != null && desData.length > 0) { Log.e(TAG, "-------->desc value:"+ new String(desData)); } } } }// } }
感興趣的讀者可以動手測試一下代碼的運(yùn)行情況,希望能對大家的Android項(xiàng)目開發(fā)有所幫助。
- 淺析Android手機(jī)衛(wèi)士之手機(jī)實(shí)現(xiàn)短信指令獲取位置
- 基于JavaScript實(shí)現(xiàn)根據(jù)手機(jī)定位獲取當(dāng)前具體位置(X省X市X縣X街道X號)
- android計(jì)算pad或手機(jī)的分辨率/像素/密度/屏幕尺寸/DPI值的方法
- android開發(fā)之調(diào)用手機(jī)的攝像頭使用MediaRecorder錄像并播放
- android 手機(jī)SD卡讀寫操作(以txt文本為例)實(shí)現(xiàn)步驟
- js和html5實(shí)現(xiàn)手機(jī)端刮刮卡抽獎(jiǎng)效果完美兼容android/IOS
- Android獲取手機(jī)型號/系統(tǒng)版本號/App版本號等信息實(shí)例講解
- js判斷手機(jī)端(Android手機(jī)還是iPhone手機(jī))
- Android如何通過手機(jī)獲取驗(yàn)證碼來完成注冊功能
- Android獲取手機(jī)位置的實(shí)現(xiàn)代碼
相關(guān)文章
Android實(shí)現(xiàn)水波紋效果實(shí)例代碼
大家好,本篇文章主要講的是Android實(shí)現(xiàn)水波紋效果實(shí)例代碼,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下2022-02-02Android OpenGL入門之GLSurfaceView
這篇文章主要介紹了OpenGL入門知識,如何在Android中使用GLSurfaceView,如果對OpenGL感興趣的同學(xué),可以參考下2021-04-04Android實(shí)現(xiàn)拍照添加時(shí)間水印
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)拍照添加時(shí)間水印,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03Android編程判斷當(dāng)前應(yīng)用是否在后臺運(yùn)行的方法示例
這篇文章主要介紹了Android編程判斷當(dāng)前應(yīng)用是否在后臺運(yùn)行的方法,涉及Android針對當(dāng)前程序運(yùn)行狀態(tài)相關(guān)屬性操作與判定技巧,需要的朋友可以參考下2018-03-03DCloud的native.js調(diào)用系統(tǒng)分享實(shí)例Android版代碼
本文為大家分享了DCloud的native.js如何調(diào)用系統(tǒng)分享功能Android版的實(shí)例代碼,直接拿來就用2018-09-09Android開發(fā)獲取手機(jī)Mac地址適配所有Android版本
這篇文章主要介紹了Android開發(fā)獲取手機(jī)Mac地址適配所有Android版本,需要的朋友可以參考下2020-03-03Flutter通過Container實(shí)現(xiàn)時(shí)間軸效果
時(shí)間軸是前端UI經(jīng)常用到的效果,本文講解下Flutter如何通過Container實(shí)現(xiàn),感興趣的朋友可以了解下2021-05-05