Android低功耗藍牙的那點事兒
近期項目使用到了藍牙技術,菜鳥一枚,網上各種找資料,發現不是不全就是過時,要么就是抄襲轉載,真實醉了,現在將這一塊的東西整理出來,供大家參考。
基本概念
Android中的藍牙分兩種:經典藍牙、低功耗藍牙。
- 二者本質上沒有太多區別,可以理解為后者是前者的升級優化版本。對于API上的實現區別還是很大的。
- 工作流程:發現設備->配對/綁定設備->建立連接->數據通信。
至于底層如何工作,本人不了解,也不是本文關注的重點。 - 官方文檔: https://developer.android.com/guide/topics/connectivity/bluetooth.html?hl=zh-cn
重要實例
- 經典藍牙聊天實例: https://github.com/googlesamples/android-BluetoothChat
- 低功耗藍牙: https://github.com/googlesamples/android-BluetoothLeGatt
- 作為外設(API>=21): https://github.com/googlesamples/android-BluetoothAdvertisements
- 基礎概念講解: http://blog.csdn.net/qinxiandiqi/article/details/40741269
- 深入理論: http://www.race604.com/android-ble-in-action/
注意事項
-
低功耗藍牙(BLE)Android 4.3(API 18)以上才支持
-
使用藍牙需要權限
<uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
-
Android 5.0(API 21) 掃描藍牙需要定位權限,否則掃描不到設備,實際使用時候發現 5.0不需要也可以掃描,Android 6.0(API 23)以上必須(不知道什么原因,測試機器:MI 2,知道原因的可告知)
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> 或 <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
官方文檔: https://developer.android.com/guide/topics/connectivity/bluetooth-le.html?hl=zh-cn
-
低功耗藍牙要聲明特征,或者代碼判斷
// 如果為true表示只能在支持低功耗藍牙的設備上使用,如果不支持的設備也可以使用,采用代碼判斷 <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/> // 代碼判斷 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show(); finish(); }
-
經典藍牙連接成功后獲取一個socket連接得到輸入輸出流進行通信,低功耗通過特征(具體實現不知道是什么)
-
Android 5.0(API 21)之前不能當成外設(藍牙耳機、音響等)來使用,只能作為中心即主機
低功耗藍牙
現在藍牙開發基本上都是低功耗藍牙,比如心率設備、耳機設備、手環,所以我們先從重要的開始,講講低功耗藍牙的使用,后面在完善經典藍牙。
聲明權限
創建項目在AndroidManifest.xml中聲明權限:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lowett.android">
// 定位權限第二個包含第一個,所以這里就聲明了一個,兩個都聲明也可以
<!-- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
// 是否必須支持低功耗藍牙,此處不必須
<uses-feature
android:name="android.hardware.bluetooth_le"
android:required="false"/>
// 是有gps硬件,這個現在的智能手機沒有不支持的吧
<uses-feature android:name="android.hardware.location.gps"/>
</manifest>
如果manifest中聲明的藍牙特性為false,那么在代碼中監測是否支持BLE特性,
// 使用此檢查確定BLE是否支持在設備上,然后你可以有選擇性禁用BLE相關的功能
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
finish();
}
BluetoothAdapter
獲取BluetoothManager得到藍牙適配器BluetoothAdapter,注意這兩個類都是系統級別,只有一個,代表了你手機上的藍牙模塊
final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();
如果獲取到的Adapter為空說明不支持藍牙,或者沒有藍牙模塊
開啟藍牙
前面獲取到了BluetoothAdapter,可以通過調用isEnabled()函數去檢測是否開啟了藍牙,false表示沒有開啟,如果沒有開啟需要去啟用,
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
activity.startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
代碼執行完后,會有彈框提示用戶是否啟用,我們需要在onActivityResult()中判斷返回
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == BluetoothLeManager.REQUEST_ENABLE_BT) {
if (resultCode == Activity.RESULT_OK) {
// something, 去掃描設備
startScan();
} else {
new AlertDialog.Builder(this)
.setMessage("請開啟藍牙,連接設備")
.setPositiveButton(R.string.confirm, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
// something
}
})
.create()
.show();
}
}
}
掃描設備
低功耗藍牙和經典藍牙的掃描方式不同,如果熟悉經典藍牙,那就要可能掉進坑了起不來了,哈哈。藍牙掃描耗電比較嚴重,所以此處一定要記得在合適的實際停止掃描,比如定時停止、掃描到目標設備就停止(奇怪的是API為何不提供掃描時長的接口呢?)掃描時調用Adapter的startLeScan()方法,然而這個被標記為過時,API>=21被另一個取代。然后通過回掉得到掃描結果(經典藍牙是廣播)
public void startScan() {
// 初始化一個handler
initHandler();
if (!mScanning) {
if (scanRunnable == null) {
scanRunnable = new Runnable() {
@Override
public void run() {
stopScan();
}
};
}
// SCAN_PERIOD = 3 * 10 * 1000, 30s后停止掃面
mHandler.postDelayed(scanRunnable, SCAN_PERIOD);
// 新API
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// mBluetoothAdapter.getBluetoothLeScanner().startScan(this);
// }else {
// this 實現了BluetoothAdapter.LeScanCallback,即掃描結果回掉
mBluetoothAdapter.startLeScan(this);
// }
mScanning = true;
Log.i(TAG, "開始掃描,藍牙設備");
}
}
回調函數
@Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
// 得到掃描結果
Log.i(TAG, "掃描到的設備, name=" + device.getName() + ",address=" + device.toString());
}
注意:device:代表外設即目標設備 rssi:設一個強度值,但是時負值,利用這個值通過公式可以算出離你的距離
scanRecord:廣播數據,附加的數據,沒用到
停止掃描
掃描完成務必停止,因為掃描不僅耗電,還影響連接速度,所以當要連接的時候,先停止掃描時必須的
public void stopScan() {
initHandler();
Log.i(TAG, "停止掃描,藍牙設備");
if (mScanning) {
mScanning = false;
// 開始掃描的接口,要一樣的不然停止不了
mBluetoothAdapter.stopLeScan(this);
}
if (scanRunnable != null) {
mHandler.removeCallbacks(scanRunnable);
scanRunnable = null;
}
}
連接設備
通常連接設備速度還是很快的,連接理論上來說也是無狀態的,所以也需要一個定式任務來,保證超時停止。
public boolean connect(Context context, String address) {
if (mConnectionState == STATE_CONNECTED) {
return false;
}
if (mBluetoothAdapter == null || TextUtils.isEmpty(address)) {
return false;
}
initHandler();
if (connectRunnable == null) {
connectRunnable = new Runnable() {
@Override
public void run() {
mConnectionState = STATE_DISCONNECTING;
disconnect();
}
};
}
// 30s沒反應停止連接
mHandler.postDelayed(connectRunnable, 30 * 1000);
stopScan();
// 獲取到遠程設備,
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
if (device == null) {
return false;
}
// 開始連接,第二個參數表示是否需要自動連接,true設備靠近自動連接,第三個表示連接回調
mBluetoothGatt = device.connectGatt(context, false, mGattCallback);
mBluetoothDeviceAddress = address;
mConnectionState = STATE_CONNECTING;
return true;
}
監聽連接回調
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
// 連接狀態變化
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) { // 連接上
mConnectionState = STATE_CONNECTED;
boolean success = mBluetoothGatt.discoverServices(); // 去發現服務
Log.i(TAG, "Attempting to start service discovery:" +
success);
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) { // 連接斷開
mConnectionState = STATE_DISCONNECTED;
}
}
// 發現服務
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.i(TAG,"發現服務");
// 解析服務
discoverService();
}
}
// 特征讀取變化
@Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
}
}
// 收到數據
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
mConnectionState = STATE_CONNECTED;
}
};
斷開連接
public void disconnect() {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
mConnectionState = STATE_DISCONNECTED;
return;
}
// 連接成功的GATT
mBluetoothGatt.disconnect();
mBluetoothGatt.close();
}
多設備連接
藍牙適配器沒有聽過連接多個設備的接口,需要我們自己實現,即獲取到目標設備的address后調用連接方法,自己維護多個BluetoothGatt即可(代碼稍后放出)。
完整代碼示例
正在做心率相關的藍牙設備,此處代碼發出來。
代碼還在完善中,僅供參考,穩定代碼將會發在Github中
package com.lowett.android.ble;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.IntDef;
import android.text.TextUtils;
import com.fit.android.utils.Logger;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
/**
* Email: fvaryu@qq.com
*/
public class BluetoothLeManager implements BluetoothAdapter.LeScanCallback {
public static final int REQUEST_ENABLE_BT = 1;
private static final int SCAN_PERIOD = 3 * 10 * 1000;
static final int STATE_DISCONNECTED = 1;
public static final int STATE_CONNECTING = 2;
public static final int STATE_DISCONNECTING = 3;
public static final int STATE_CONNECTED = 4;
public static final int STATE_DISCOVER_SERVICES = 5;
@IntDef(value = {STATE_DISCONNECTED, STATE_CONNECTING, STATE_CONNECTED, STATE_DISCONNECTING, STATE_DISCOVER_SERVICES})
@Retention(RetentionPolicy.SOURCE)
public @interface State {
}
public final static UUID UUID_HEART_RATE_MEASUREMENT =
UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT);
private static BluetoothLeManager ourInstance = new BluetoothLeManager();
// private Context mContext;
private boolean is_inited = false;
private android.bluetooth.BluetoothManager mBluetoothManager;
private BluetoothAdapter mBluetoothAdapter;
private String mBluetoothDeviceAddress;
private int mConnectionState;
private BluetoothGatt mBluetoothGatt;
private boolean mScanning;
private Runnable scanRunnable;
private Handler mHandler;
private Runnable connectRunnable;
private OnDataReceivedListener mOnDataReceivedListener;
// 記得清掉監聽 泄漏
private OnLeScanListener mOnLeScanListener;
private OnConnectionStateChangeListener mOnConnectionStateChangeListener;
private int retryCount;
public static BluetoothLeManager getInstance() {
return ourInstance;
}
private BluetoothLeManager() {
}
public void setOnDataReceivedListener(OnDataReceivedListener onDataReceivedListener) {
mOnDataReceivedListener = onDataReceivedListener;
}
public interface OnConnectionStateChangeListener {
void onConnectionStateChange(BluetoothGatt gatt, int status, int newState);
void onConnectTimeout();
}
public void setOnConnectionStateChangeListener(OnConnectionStateChangeListener onConnectionStateChangeListener) {
mOnConnectionStateChangeListener = onConnectionStateChangeListener;
}
public interface OnLeScanListener {
void onLeScan(BluetoothDevice device);
}
public interface OnDataReceivedListener {
void onDataReceived(int heart);
}
private void init() {
if (!is_inited) {
is_inited = true;
}
}
private boolean initialize(Context context) {
init();
if (!is_inited) {
throw new RuntimeException("請先調用init");
}
if (mBluetoothAdapter != null) {
return true;
}
// For API level 18 and above, get a reference to BluetoothAdapter through
// BluetoothLeManager.
if (mBluetoothManager == null) {
mBluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
if (mBluetoothManager == null) {
return false;
}
}
mBluetoothAdapter = mBluetoothManager.getAdapter();
return mBluetoothAdapter != null;
}
public boolean isSupportBluetoothLe(Activity activity) {
return activity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
}
public boolean isSupportBluetooth(Context context) {
return initialize(context);
}
public void enableBluetooth(Activity activity) {
if (!initialize(activity)) {
return;
}
if (!mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
activity.startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}
public boolean isEnabled(Context context) {
return initialize(context) && mBluetoothAdapter.isEnabled();
}
private void initHandler() {
if (mHandler == null) {
mHandler = new Handler(Looper.getMainLooper());
}
}
public void startScan(OnLeScanListener onLeScanListener) {
initHandler();
if (!mScanning) {
if (scanRunnable == null) {
scanRunnable = new Runnable() {
@Override
public void run() {
stopScan();
}
};
}
mHandler.postDelayed(scanRunnable, SCAN_PERIOD);
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// mBluetoothAdapter.getBluetoothLeScanner().startScan(this);
// }else {
mBluetoothAdapter.startLeScan(this);
// }
mScanning = true;
this.mOnLeScanListener = onLeScanListener;
Logger.i("開始掃描,藍牙設備");
}
}
@Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
final BluetoothDevice tmp = device;
Logger.i("掃描到的設備, name=" + device.getName() + ",address=" + device.toString());
if (mOnLeScanListener != null) {
mHandler.post(new Runnable() {
@Override
public void run() {
mOnLeScanListener.onLeScan(tmp);
}
});
}
}
public void stopScan() {
initHandler();
mOnLeScanListener = null;
Logger.i("停止掃描,藍牙設備");
if (mScanning) {
mScanning = false;
mBluetoothAdapter.stopLeScan(this);
}
if (scanRunnable != null) {
mHandler.removeCallbacks(scanRunnable);
scanRunnable = null;
}
}
private void removeConnectRunnable() {
if (connectRunnable != null) {
mHandler.removeCallbacks(connectRunnable);
connectRunnable = null;
}
}
private void retry() {
if (TextUtils.isEmpty(mBluetoothDeviceAddress)) {
return;
}
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
if (++retryCount < 11 && mConnectionState < STATE_CONNECTED) {
reconnect(retryCount);
mHandler.postDelayed(this, retryCount * 5 * 1000);
Logger.i("藍牙重試次數=" + retryCount);
}
}
}, 2000);
}
private void reconnect(int count) {
if ((mConnectionState >= STATE_CONNECTING)) {
return;
}
if (connectRunnable == null) {
connectRunnable = new Runnable() {
@Override
public void run() {
mConnectionState = STATE_DISCONNECTING;
disconnect();
}
};
}
mHandler.postDelayed(connectRunnable, count * 3 * 1000);
if (mBluetoothDeviceAddress != null
&& mBluetoothGatt != null) {
mBluetoothGatt.connect();
mConnectionState = STATE_CONNECTING;
}
}
public boolean connect(Context context, String address) {
if (mConnectionState == STATE_CONNECTED) {
return false;
}
if (mBluetoothAdapter == null || TextUtils.isEmpty(address)) {
return false;
}
initHandler();
if (connectRunnable == null) {
connectRunnable = new Runnable() {
@Override
public void run() {
mConnectionState = STATE_DISCONNECTING;
disconnect();
if (mOnConnectionStateChangeListener != null) {
mOnConnectionStateChangeListener.onConnectTimeout();
}
}
};
}
mHandler.postDelayed(connectRunnable, 30 * 1000);
stopScan();
// Previously connected device. Try to reconnect.
if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress)
&& mBluetoothGatt != null) {
if (mBluetoothGatt.connect()) {
mConnectionState = STATE_CONNECTING;
return true;
} else {
return false;
}
}
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
if (device == null) {
return false;
}
// We want to directly connect to the device, so we are setting the autoConnect
// parameter to false.
mBluetoothGatt = device.connectGatt(context, false, mGattCallback);
mBluetoothDeviceAddress = address;
mConnectionState = STATE_CONNECTING;
return true;
}
public void disconnect() {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Logger.i("BluetoothAdapter not initialized");
mConnectionState = STATE_DISCONNECTED;
return;
}
mBluetoothGatt.disconnect();
}
public void close() {
disconnect();
if (mBluetoothGatt == null) {
return;
}
mBluetoothGatt.close();
mBluetoothGatt = null;
}
public void disconnectNoRetry() {
mBluetoothDeviceAddress = null;
close();
}
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
mConnectionState = STATE_CONNECTED;
boolean success = mBluetoothGatt.discoverServices();
Logger.i("Attempting to start service discovery:" +
success);
removeConnectRunnable();
Logger.i("鏈接上");
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
mConnectionState = STATE_DISCONNECTED;
Logger.i("斷開鏈接");
retry();
}
if (mOnConnectionStateChangeListener != null) {
mOnConnectionStateChangeListener.onConnectionStateChange(gatt, status, newState);
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Logger.i("發現服務");
discoverService();
}
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(characteristic);
}
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
mConnectionState = STATE_CONNECTED;
broadcastUpdate(characteristic);
}
};
/**
* Retrieves a list of supported GATT services on the connected device. This should be
* invoked only after {@code BluetoothGatt#discoverServices()} completes successfully.
*
* @return A {@code List} of supported services.
*/
private List<BluetoothGattService> getSupportedGattServices() {
if (mBluetoothGatt == null) return null;
return mBluetoothGatt.getServices();
}
private void discoverService() {
if (mConnectionState == STATE_DISCOVER_SERVICES) {
return;
}
mConnectionState = STATE_DISCOVER_SERVICES;
List<BluetoothGattService> list = getSupportedGattServices();
/**
* BluetoothGattService = 00001800-0000-1000-8000-00805f9b34fb
BluetoothGattCharacteristic = 00002a00-0000-1000-8000-00805f9b34fb
BluetoothGattCharacteristic = 00002a01-0000-1000-8000-00805f9b34fb
BluetoothGattCharacteristic = 00002a04-0000-1000-8000-00805f9b34fb
BluetoothGattService = 00001801-0000-1000-8000-00805f9b34fb
BluetoothGattCharacteristic = 00002a05-0000-1000-8000-00805f9b34fb
心跳服務
BluetoothGattService = 0000180d-0000-1000-8000-00805f9b34fb
心跳特征
BluetoothGattCharacteristic = 00002a37-0000-1000-8000-00805f9b34fb
BluetoothGattCharacteristic = 00002a38-0000-1000-8000-00805f9b34fb
BluetoothGattService = 0000180f-0000-1000-8000-00805f9b34fb
BluetoothGattCharacteristic = 00002a19-0000-1000-8000-00805f9b34fb
// 設備名字
BluetoothGattService = 0000180a-0000-1000-8000-00805f9b34fb
BluetoothGattCharacteristic = 00002a28-0000-1000-8000-00805f9b34fb
*/
for (BluetoothGattService s : list) {
if (!SampleGattAttributes.HEART_RATE_SERVICES.equals(s.getUuid().toString())) {
continue;
}
final List<BluetoothGattCharacteristic> l = s.getCharacteristics();
for (final BluetoothGattCharacteristic bc : l) {
if (!SampleGattAttributes.HEART_RATE_MEASUREMENT.equals(bc.getUuid().toString())) {
continue;
}
Logger.i("連接藍牙 服務成功");
setCharacteristicNotification(bc, true);
return;
}
}
}
private void broadcastUpdate(final BluetoothGattCharacteristic characteristic) {
// This is special handling for the Heart Rate Measurement profile. Data parsing is
// carried out as per profile specifications:
// http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml
if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
int flag = characteristic.getProperties();
int format = -1;
if ((flag & 0x01) != 0) {
format = BluetoothGattCharacteristic.FORMAT_UINT16;
} else {
format = BluetoothGattCharacteristic.FORMAT_UINT8;
}
int heartRate = characteristic.getIntValue(format, 1);
Logger.i(String.format(Locale.getDefault(), "Received heart rate: %d", heartRate));
if (mOnDataReceivedListener != null) {
mOnDataReceivedListener.onDataReceived(heartRate);
}
}
// else {
// For all other profiles, writes the data formatted in HEX.
// final byte[] data = characteristic.getValue();
// if (data != null && data.length > 0) {
// final StringBuilder stringBuilder = new StringBuilder(data.length);
// for (byte byteChar : data)
// stringBuilder.append(String.format("%02X ", byteChar));
// intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString());
// }
// }
/**
* 2、
*/
// sendBroadcast(intent);
}
public boolean isConnected() {
return mConnectionState == STATE_CONNECTED;
}
public String getConnectedAddress() {
if (!isConnected()) {
return null;
}
return mBluetoothDeviceAddress;
}
public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Logger.i("BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.readCharacteristic(characteristic);
}
private void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
boolean enabled) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Logger.i("BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
// This is specific to Heart Rate Measurement.
if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
}
}
public void clear() {
mOnLeScanListener = null;
mOnConnectionStateChangeListener = null;
}
public void release() {
// connectRunnable = null;
// mHandler = null;
// ourInstance = null;
}
}
來自:http://lowett.com/2017/03/23/android-bluetooth/