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才開(kāi)始支持BLE API,所以請(qǐng)各位客官把本文代碼運(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。一般來(lái)說(shuō),Characteristic是手機(jī)與BLE終端交換數(shù)據(jù)的關(guān)鍵,Characteristic有較多的跟權(quán)限相關(guān)的字段,例如PERMISSION和PROPERTY,而其中最常用的是PROPERTY,本文所用的BLE藍(lán)牙模塊竟然沒(méi)有標(biāo)準(zhǔn)的Characteristic的PERMISSION。Characteristic的PROPERTY可以通過(guò)位運(yùn)算符組合來(lái)設(shè)置讀寫(xiě)屬性,例如READ|WRITE、READ|WRITE_NO_RESPONSE|NOTIFY,因此讀取PROPERTY后要分解成所用的組合(本文代碼已含此分解方法)。
本文代碼改自Android 4.3 Sample的BluetoothLeGatt,把冗余代碼去掉,獲取的BLE設(shè)備信息都通過(guò)Log,還有一些必要的讀寫(xiě)藍(lán)牙方法,應(yīng)該算是簡(jiǎn)化到大家一看就可以懂了。本文完整代碼可以點(diǎn)擊此處本站下載。
接下來(lái)貼出本文運(yùn)行的結(jié)果,首先是連接BLE設(shè)備后,枚舉出設(shè)備所有Service、Characteristic、Descriptor,并且手機(jī)會(huì)往Characteristic uuid=0000ffe1-0000-1000-8000-00805f9b34fb寫(xiě)入“send data->”字符串,BLE終端收到數(shù)據(jù)通過(guò)串口傳到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上的串口工具通過(guò)BLE模塊發(fā)送過(guò)來(lái),由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
接下來(lái)貼出本文核心代碼:
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;
/**讀寫(xiě)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;
}
//開(kāi)啟藍(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終端寫(xiě)入數(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)){
//測(cè)試讀取當(dāng)前Characteristic數(shù)據(jù),會(huì)觸發(fā)mOnDataAvailable.onCharacteristicRead()
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mBLE.readCharacteristic(gattCharacteristic);
}
}, 500);
//接受Characteristic被寫(xiě)的通知,收到藍(lán)牙模塊的數(shù)據(jù)后會(huì)觸發(fā)mOnDataAvailable.onCharacteristicWrite()
mBLE.setCharacteristicNotification(gattCharacteristic, true);
//設(shè)置數(shù)據(jù)內(nèi)容
gattCharacteristic.setValue("send data->");
//往藍(lán)牙模塊寫(xiě)入數(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));
}
}
}
}//
}
}
感興趣的讀者可以動(dòng)手測(cè)試一下代碼的運(yùn)行情況,希望能對(duì)大家的Android項(xiàng)目開(kāi)發(fā)有所幫助。
- 淺析Android手機(jī)衛(wèi)士之手機(jī)實(shí)現(xiàn)短信指令獲取位置
- 基于JavaScript實(shí)現(xiàn)根據(jù)手機(jī)定位獲取當(dāng)前具體位置(X省X市X縣X街道X號(hào))
- android計(jì)算pad或手機(jī)的分辨率/像素/密度/屏幕尺寸/DPI值的方法
- android開(kāi)發(fā)之調(diào)用手機(jī)的攝像頭使用MediaRecorder錄像并播放
- android 手機(jī)SD卡讀寫(xiě)操作(以txt文本為例)實(shí)現(xiàn)步驟
- js和html5實(shí)現(xiàn)手機(jī)端刮刮卡抽獎(jiǎng)效果完美兼容android/IOS
- Android獲取手機(jī)型號(hào)/系統(tǒng)版本號(hào)/App版本號(hào)等信息實(shí)例講解
- js判斷手機(jī)端(Android手機(jī)還是iPhone手機(jī))
- Android如何通過(guò)手機(jī)獲取驗(yàn)證碼來(lái)完成注冊(cè)功能
- Android獲取手機(jī)位置的實(shí)現(xiàn)代碼
相關(guān)文章
Android中DownloadManager實(shí)現(xiàn)文件下載實(shí)例詳解
這篇文章主要介紹了Android中DownloadManager實(shí)現(xiàn)文件下載實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-03-03
Service與Activity之間的通信(同一進(jìn)程)
這篇文章主要介紹了Service與Activity之間的通信(同一進(jìn)程)的相關(guān)資料,需要的朋友可以參考下2016-03-03
Android調(diào)節(jié)屏幕亮度實(shí)現(xiàn)代碼
這篇文章主要介紹了Android調(diào)節(jié)屏幕亮度實(shí)現(xiàn)代碼,調(diào)節(jié)屏幕亮度時(shí),先設(shè)置當(dāng)前activity亮度,再并保存為系統(tǒng)亮度即可,本文分別給出兩個(gè)步驟的實(shí)現(xiàn)代碼,需要的朋友可以參考下2015-05-05
Android通過(guò)BLE傳輸文件遇到問(wèn)題解決
這篇文章主要為大家介紹了Android通過(guò)BLE傳輸文件遇到問(wèn)題解決方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04
Android SQLite數(shù)據(jù)庫(kù)增刪改查操作的案例分析
本篇文章介紹了,在Android中SQLite數(shù)據(jù)庫(kù)增刪改查操作的案例分析,需要的朋友參考下2013-04-04
Android冷啟動(dòng)優(yōu)化的3個(gè)小案例分享
為了提高App的冷啟動(dòng)耗時(shí),除了在常規(guī)的業(yè)務(wù)側(cè)進(jìn)行耗時(shí)代碼優(yōu)化之外,為了進(jìn)一步縮短啟動(dòng)耗時(shí),需要在純技術(shù)測(cè)做一些優(yōu)化探索,本期我們從類(lèi)預(yù)加載、Retrofit 、ARouter方面進(jìn)行了進(jìn)一步的優(yōu)化,感興趣的同學(xué)跟著小編一起來(lái)看看吧2023-07-07
一文搞懂Android RecyclerView點(diǎn)擊展開(kāi)、折疊效果的實(shí)現(xiàn)代碼
雖然在日常開(kāi)發(fā)中已經(jīng)多次接觸過(guò)RecycleView,但也只是用到其最基本的功能,并沒(méi)有深入研究其他內(nèi)容。接下來(lái)將抽出時(shí)間去了解RecycleView的相關(guān)內(nèi)容,這篇文章主要是介紹Android RecyclerView點(diǎn)擊展開(kāi)、折疊效果的實(shí)現(xiàn)方式,一起看看吧2021-06-06
Android 中對(duì)JSON數(shù)據(jù)解析實(shí)例代碼
這篇文章主要介紹了Android 中對(duì)JSON數(shù)據(jù)解析實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-03-03

