編輯:Android開發實例
最近穿戴設備發展得很火,把相關技術也帶旺了,其中一項是BLE(Bluetooth Low Energy)。BLE是藍牙4.0的核心Profile,主打功能是快速搜索,快速連接,超低功耗保持連接和傳輸數據,弱點是數據傳輸速率低,由於BLE的低功耗特點,因此普遍用於穿戴設備。Android 4.3才開始支持BLE API,所以請各位客官把本文代碼運行在藍牙4.0和Android 4.3及其以上的系統,另外本文所用的BLE終端是一個藍牙4.0的串口藍牙模塊。
注:筆者的i9100刷了4.4系統後,竟然也能跟BLE藍牙模塊通信。
BLE分為三部分Service、Characteristic、Descriptor,這三部分都由UUID作為唯一標示符。一個藍牙4.0的終端可以包含多個Service,一個Service可以包含多個Characteristic,一個Characteristic包含一個Value和多個Descriptor,一個Descriptor包含一個Value。一般來說,Characteristic是手機與BLE終端交換數據的關鍵,Characteristic有較多的跟權限相關的字段,例如PERMISSION和PROPERTY,而其中最常用的是PROPERTY,本文所用的BLE藍牙模塊竟然沒有標准的Characteristic的PERMISSION。Characteristic的PROPERTY可以通過位運算符組合來設置讀寫屬性,例如READ|WRITE、READ|WRITE_NO_RESPONSE|NOTIFY,因此讀取PROPERTY後要分解成所用的組合(本文代碼已含此分解方法)。
本文代碼改自Android 4.3 Sample的BluetoothLeGatt,把冗余代碼去掉,獲取的BLE設備信息都通過Log,還有一些必要的讀寫藍牙方法,應該算是簡化到大家一看就可以懂了。本文完整代碼可以點擊此處本站下載。
接下來貼出本文運行的結果,首先是連接BLE設備後,枚舉出設備所有Service、Characteristic、Descriptor,並且手機會往Characteristic uuid=0000ffe1-0000-1000-8000-00805f9b34fb寫入“send data->”字符串,BLE終端收到數據通過串口傳到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()回調而打出Log

以下Log是PC上的串口工具通過BLE模塊發送過來,由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;
/**讀寫BLE終端*/
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;
}
//開啟藍牙
mBluetoothAdapter.enable();
mBLE = new BluetoothLeClass(this);
if (!mBLE.initialize()) {
Log.e(TAG, "Unable to initialize Bluetooth");
finish();
}
//發現BLE終端的Service時回調
mBLE.setOnServiceDiscoverListener(mOnServiceDiscover);
//收到BLE終端數據交互的事件
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終端服務的事件
*/
private BluetoothLeClass.OnServiceDiscoverListener mOnServiceDiscover = new OnServiceDiscoverListener(){
@Override
public void onServiceDiscover(BluetoothGatt gatt) {
displayGattServices(mBLE.getSupportedGattServices());
}
};
/**
* 收到BLE終端數據交互的事件
*/
private BluetoothLeClass.OnDataAvailableListener mOnDataAvailable = new OnDataAvailableListener(){
/**
* BLE終端數據被讀的事件
*/
@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終端寫入數據回調
*/
@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是可以跟藍牙模塊串口通信的Characteristic
if(gattCharacteristic.getUuid().toString().equals(UUID_KEY_DATA)){
//測試讀取當前Characteristic數據,會觸發mOnDataAvailable.onCharacteristicRead()
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mBLE.readCharacteristic(gattCharacteristic);
}
}, 500);
//接受Characteristic被寫的通知,收到藍牙模塊的數據後會觸發mOnDataAvailable.onCharacteristicWrite()
mBLE.setCharacteristicNotification(gattCharacteristic, true);
//設置數據內容
gattCharacteristic.setValue("send data->");
//往藍牙模塊寫入數據
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));
}
}
}
}//
}
}
感興趣的讀者可以動手測試一下代碼的運行情況,希望能對大家的Android項目開發有所幫助。
Android MediaPlayer(多媒體播放)
Android提供了許多方法來控制播放的音頻/視頻文件和流。其中該方法是通過一類稱為MediaPlayer。Android是提供MediaPlayer類訪問內置的媒體播放
Android加載Spinner
可以顯示在的Android任務,通過加載進度條的進展。進度條有兩種形狀。加載欄和加載微調(spinner)。在本章中,我們將討論微調(spinner)。Spinner 用
Android學習筆記——Menu介紹(三)
知識點 今天繼續昨天沒有講完的Menu的學習,主要是Popup Menu的學習。 Popup Menu(彈出式菜單) 彈出式菜單是一種固定在View上的菜單模型。
android開發 eclipse alt+”/”自動提示失效的解決方法
1、用於沒有一點提示的情況:依次打開eclipse上面的windows ——preferences ——java ——editor —— content assi