藍牙是一種短距離的無線通訊技術,能夠實現固定設備、移動設備之間的數據交換。通常將藍牙分爲兩大類,藍牙3.0規範以前的版本稱爲傳統藍牙,藍牙4.0規範以後的版本稱爲低功耗藍牙,也就是常說的BLE(Bluetooth Low Energy)。android
本文主要講解的是Android設備與BLE設備之間的通訊,Android 從4.3版本(API Level 18)開始支持BLE通訊。git
看圖說話:github
首先要判斷當前的Android設備是否支持藍牙,若是支持則再判斷當前藍牙是否處於開啓狀態,若是未開啓則發送廣播通知系統開啓藍牙,藍牙開啓後開始搜索周圍的藍牙設備,注意搜索必定要設置超時處理,搜索到指定藍牙設備後中止搜索任務。bash
此時能夠以列表的形式供用戶選擇須要鏈接的設備,或者內部自動鏈接特定的設備,鏈接成功後,搜索此藍牙設備提供的服務(特性、描述符的集合),搜索完成後設置一些對應的參數,便可與藍牙設備進行通訊了。ide
看下咱們在開發過程當中須要用到的一些API:post
本地藍牙適配器,用於一些藍牙的基本操做,好比判斷藍牙是否開啓、搜索藍牙設備等。ui
藍牙設備對象,包含一些藍牙設備的屬性,好比設備名稱、mac地址等。this
一個通用的藍牙規範,設備之間按照這個規範來收發數據。spa
藍牙通用屬性協議,定義了BLE通信的基本規則,是BluetoothProfile的實現類,Gatt是Generic Attribute Profile的縮寫,用於鏈接設備、搜索服務等操做。code
藍牙設備鏈接成功後,用於回調一些操做的結果,必須鏈接成功後纔會回調。
藍牙設備提供的服務,是藍牙設備特徵的集合。
藍牙設備特徵,是構建GATT服務的基本數據單元。
藍牙設備特徵描述符,是對特徵的額外描述。
需用用到的權限:
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
複製代碼
Demo中把藍牙的相關操做寫在了Service中,這樣即便應用退出也不會影響藍牙的鏈接,在Service中經過廣播的方式通知Activity作相應的處理,看下Service:
public class BleService extends Service {
private final String TAG = BleService.class.getSimpleName();
private BluetoothGatt mBluetoothGatt;
// 藍牙鏈接狀態
private int mConnectionState = 0;
// 藍牙鏈接已斷開
private final int STATE_DISCONNECTED = 0;
// 藍牙正在鏈接
private final int STATE_CONNECTING = 1;
// 藍牙已鏈接
private final int STATE_CONNECTED = 2;
// 藍牙已鏈接
public final static String ACTION_GATT_CONNECTED = "com.yl.ble.ACTION_GATT_CONNECTED";
// 藍牙已斷開
public final static String ACTION_GATT_DISCONNECTED = "com.yl.ble.ACTION_GATT_DISCONNECTED";
// 發現GATT服務
public final static String ACTION_GATT_SERVICES_DISCOVERED = "com.yl.ble.ACTION_GATT_SERVICES_DISCOVERED";
// 收到藍牙數據
public final static String ACTION_DATA_AVAILABLE = "com.yl.ble.ACTION_DATA_AVAILABLE";
// 鏈接失敗
public final static String ACTION_CONNECTING_FAIL = "com.yl.ble.ACTION_CONNECTING_FAIL";
// 藍牙數據
public final static String EXTRA_DATA = "com.yl.ble.EXTRA_DATA";
// 服務標識
private final UUID SERVICE_UUID = UUID.fromString("0000ace0-0000-1000-8000-00805f9b34fb");
// 特徵標識(讀取數據)
private final UUID CHARACTERISTIC_READ_UUID = UUID.fromString("0000ace0-0001-1000-8000-00805f9b34fb");
// 特徵標識(發送數據)
private final UUID CHARACTERISTIC_WRITE_UUID = UUID.fromString("0000ace0-0003-1000-8000-00805f9b34fb");
// 描述標識
private final UUID DESCRIPTOR_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
// 服務相關
private final IBinder mBinder = new LocalBinder();
public class LocalBinder extends Binder {
public BleService getService() {
return BleService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
release();
return super.onUnbind(intent);
}
/**
* 藍牙操做回調
* 藍牙鏈接狀態纔會回調
*/
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
// 藍牙已鏈接
mConnectionState = STATE_CONNECTED;
sendBleBroadcast(ACTION_GATT_CONNECTED);
// 搜索GATT服務
mBluetoothGatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
// 藍牙已斷開鏈接
mConnectionState = STATE_DISCONNECTED;
sendBleBroadcast(ACTION_GATT_DISCONNECTED);
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
// 發現GATT服務
if (status == BluetoothGatt.GATT_SUCCESS) {
setBleNotification();
}
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
// 收到數據
sendBleBroadcast(ACTION_DATA_AVAILABLE, characteristic);
}
};
/**
* 發送通知
*
* @param action 廣播Action
*/
private void sendBleBroadcast(String action) {
Intent intent = new Intent(action);
sendBroadcast(intent);
}
/**
* 發送通知
*
* @param action 廣播Action
* @param characteristic 數據
*/
private void sendBleBroadcast(String action, BluetoothGattCharacteristic characteristic) {
Intent intent = new Intent(action);
if (CHARACTERISTIC_READ_UUID.equals(characteristic.getUuid())) {
intent.putExtra(EXTRA_DATA, characteristic.getValue());
}
sendBroadcast(intent);
}
/**
* 藍牙鏈接
*
* @param bluetoothAdapter BluetoothAdapter
* @param address 設備mac地址
* @return true:成功 false:
*/
public boolean connect(BluetoothAdapter bluetoothAdapter, String address) {
if (bluetoothAdapter == null || TextUtils.isEmpty(address)) {
return false;
}
BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address);
if (device == null) {
return false;
}
mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
mConnectionState = STATE_CONNECTING;
return true;
}
/**
* 藍牙斷開鏈接
*/
public void disconnect() {
if (mBluetoothGatt == null) {
return;
}
mBluetoothGatt.disconnect();
}
/**
* 釋放相關資源
*/
public void release() {
if (mBluetoothGatt == null) {
return;
}
mBluetoothGatt.close();
mBluetoothGatt = null;
}
/**
* 設置藍牙設備在數據改變時,通知App
*/
public void setBleNotification() {
if (mBluetoothGatt == null) {
sendBleBroadcast(ACTION_CONNECTING_FAIL);
return;
}
// 獲取藍牙設備的服務
BluetoothGattService gattService = mBluetoothGatt.getService(SERVICE_UUID);
if (gattService == null) {
sendBleBroadcast(ACTION_CONNECTING_FAIL);
return;
}
// 獲取藍牙設備的特徵
BluetoothGattCharacteristic gattCharacteristic = gattService.getCharacteristic(CHARACTERISTIC_READ_UUID);
if (gattCharacteristic == null) {
sendBleBroadcast(ACTION_CONNECTING_FAIL);
return;
}
// 獲取藍牙設備特徵的描述符
BluetoothGattDescriptor descriptor = gattCharacteristic.getDescriptor(DESCRIPTOR_UUID);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
if (mBluetoothGatt.writeDescriptor(descriptor)) {
// 藍牙設備在數據改變時,通知App,App在收到數據後回調onCharacteristicChanged方法
mBluetoothGatt.setCharacteristicNotification(gattCharacteristic, true);
}
}
/**
* 發送數據
*
* @param data 數據
* @return true:發送成功 false:發送失敗
*/
public boolean sendData(byte[] data) {
// 獲取藍牙設備的服務
BluetoothGattService gattService = null;
if (mBluetoothGatt != null) {
gattService = mBluetoothGatt.getService(SERVICE_UUID);
}
if (gattService == null) {
return false;
}
// 獲取藍牙設備的特徵
BluetoothGattCharacteristic gattCharacteristic = gattService.getCharacteristic(CHARACTERISTIC_WRITE_UUID);
if (gattCharacteristic == null) {
return false;
}
// 發送數據
gattCharacteristic.setValue(data);
return mBluetoothGatt.writeCharacteristic(gattCharacteristic);
}
}
複製代碼
當藍牙鏈接成功後,會回調BluetoothGattCallback中的onConnectionStateChange方法,可是此時還不能與藍牙設備進行通訊,還須要調用BluetoothGatt中的discoverServices方法搜索藍牙設備提供的服務,也就是咱們上文中提到的BluetoothGattService,搜索完成後會回調BluetoothGattCallback中的onServicesDiscovered方法,這時就輪到setBleNotification方法大顯身手了。
首先經過一個UUID獲取到藍牙設備提供的服務(BluetoothGattService),這個UUID是由硬件程序定義的,開發的過程當中看文檔就能夠了,獲取到服務後,再經過一個UUID獲取到藍牙設備的特徵(BluetoothGattCharacteristic),而後再獲取設備特徵的描述符(BluetoothGattDescriptor),設置在藍牙設備數據改變時,主動通知App,此時回調BluetoothGattCallback中的onCharacteristicChanged方法,經過characteristic.getValue()能夠獲取到通知的數據。
在BluetoothGattCallback還有不少回調方法,都是與BluetoothGatt的操做相對應的,很好理解,在這裏就不一一舉例了。
到這裏Service的邏輯就介紹完了,下面來看下在Activity中如何進行處理:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private final int REQUEST_ENABLE_BT = 1;
private RecyclerView rvDeviceList;
private Button btnScan;
private BluetoothAdapter mBtAdapter;
private BleService mBleService;
private BroadcastReceiver mBleReceiver;
private DeviceListAdapter mDeviceListAdapter;
private List<BluetoothDevice> mBluetoothDeviceList;
private List<String> mRssiList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rvDeviceList = findViewById(R.id.rv_device_list);
btnScan = findViewById(R.id.btn_scan);
btnScan.setOnClickListener(this);
initBle();
initData();
registerBleReceiver();
}
/**
* 初始化藍牙
*/
private void initBle() {
mBtAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBtAdapter == null) {
Toast.makeText(this, "藍牙不可用", Toast.LENGTH_LONG).show();
return;
}
if (!mBtAdapter.isEnabled()) {
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent, REQUEST_ENABLE_BT);
return;
}
// 搜索藍牙設備
scanBleDevice();
}
/**
* 初始化數據
*/
private void initData() {
// 藍牙設備列表
mBluetoothDeviceList = new ArrayList<>();
// 藍牙設備RSSI列表
mRssiList = new ArrayList<>();
mDeviceListAdapter = new DeviceListAdapter(mBluetoothDeviceList, mRssiList);
rvDeviceList.setLayoutManager(new LinearLayoutManager(this));
rvDeviceList.setAdapter(mDeviceListAdapter);
// 鏈接藍牙設備
mDeviceListAdapter.setOnItemClickListener(new DeviceListAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
Toast.makeText(MainActivity.this, "開始鏈接", Toast.LENGTH_SHORT).show();
mBtAdapter.stopLeScan(mLeScanCallback);
mBleService.connect(mBtAdapter, mBluetoothDeviceList.get(position).getAddress());
}
});
}
/**
* 註冊藍牙信息接收器
*/
private void registerBleReceiver() {
// 綁定服務
Intent intent = new Intent(this, BleService.class);
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
startService(intent);
// 註冊藍牙信息廣播接收器
IntentFilter filter = new IntentFilter();
filter.addAction(BleService.ACTION_GATT_CONNECTED);
filter.addAction(BleService.ACTION_GATT_DISCONNECTED);
filter.addAction(BleService.ACTION_GATT_SERVICES_DISCOVERED);
filter.addAction(BleService.ACTION_DATA_AVAILABLE);
filter.addAction(BleService.ACTION_CONNECTING_FAIL);
mBleReceiver = new BleReceiver();
registerReceiver(mBleReceiver, filter);
}
/**
* 搜索藍牙設備
*/
private void scanBleDevice() {
mBtAdapter.stopLeScan(mLeScanCallback);
mBtAdapter.startLeScan(mLeScanCallback);
// 搜索10s
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
mBtAdapter.stopLeScan(mLeScanCallback);
}
}, 10000);
}
/**
* 搜索藍牙設備回調
*/
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(BluetoothDevice bluetoothDevice, int i, byte[] bytes) {
if (!mBluetoothDeviceList.contains(bluetoothDevice)) {
mBluetoothDeviceList.add(bluetoothDevice);
mRssiList.add(String.valueOf(i));
mDeviceListAdapter.notifyDataSetChanged();
}
}
};
/**
* 服務
*/
private ServiceConnection mServiceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder rawBinder) {
mBleService = ((BleService.LocalBinder) rawBinder).getService();
}
public void onServiceDisconnected(ComponentName classname) {
mBleService = null;
}
};
/**
* 藍牙信息接收器
*/
private class BleReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (TextUtils.isEmpty(action)) {
return;
}
switch (action) {
case BleService.ACTION_GATT_CONNECTED:
Toast.makeText(MainActivity.this, "藍牙已鏈接", Toast.LENGTH_SHORT).show();
break;
case BleService.ACTION_GATT_DISCONNECTED:
Toast.makeText(MainActivity.this, "藍牙已斷開", Toast.LENGTH_SHORT).show();
mBleService.release();
break;
case BleService.ACTION_CONNECTING_FAIL:
Toast.makeText(MainActivity.this, "藍牙已斷開", Toast.LENGTH_SHORT).show();
mBleService.disconnect();
break;
case BleService.ACTION_DATA_AVAILABLE:
byte[] data = intent.getByteArrayExtra(BleService.EXTRA_DATA);
Log.i("藍牙", "收到的數據:" + ByteUtils.byteArrayToHexString(data));
break;
default:
break;
}
}
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_scan: // 搜索藍牙
// 搜索藍牙設備
scanBleDevice();
// 初始化數據
initData();
// 註冊藍牙信息接收器
registerBleReceiver();
break;
default:
break;
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != Activity.RESULT_OK) {
return;
}
switch (requestCode) {
case REQUEST_ENABLE_BT:
// 搜索藍牙設備
scanBleDevice();
break;
default:
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mBleReceiver != null) {
unregisterReceiver(mBleReceiver);
mBleReceiver = null;
}
unbindService(mServiceConnection);
mBleService = null;
}
}
複製代碼
應用啓動後首先檢測當前設備藍牙是否可用,以及藍牙是否開啓,而後開始搜索藍牙設備,注意必定要設置搜索的超時時間,將搜索到的藍牙設備信息存到一個集合中,搜索時間爲10s。
而後綁定BleService,以便調用Service中的方法,同時再經過startService的方式開啓服務,這樣Activity被銷燬後service依舊能夠運行。
接下來再註冊一個廣播接收器,接收service發來的消息並作一些處理,這時在藍牙設備列表中點擊你想要鏈接的設備就能夠正常通訊了。
在產品需求中,通常應用都是長時間鏈接一個硬件設備,好比手環,此時就須要作一些特殊處理,好比藍牙鏈接斷開後主動搜索以前已經鏈接的設備,或者應用運行過程當中手機藍牙被關閉以後的處理等。
到這裏藍牙BLE開發就介紹完了,若有錯誤或者遺漏的地方能夠給我留言評論,謝謝!
代碼已上傳至GitHub,歡迎Star、Fork!
GitHub地址:https://github.com/alidili/Demos/tree/master/BleDemo
本文Demo的Apk下載地址:https://github.com/alidili/Demos/raw/master/BleDemo/BleDemo.apk