Android低功耗蓝牙的那点事儿
ShaClutterb
8年前
<p>近期项目使用到了蓝牙技术,菜鸟一枚,网上各种找资料,发现不是不全就是过时,要么就是抄袭转载,真实醉了,现在将这一块的东西整理出来,供大家参考。</p> <p>基本概念</p> <p>Android中的蓝牙分两种:经典蓝牙、低功耗蓝牙。</p> <ol> <li>二者本质上没有太多区别,可以理解为后者是前者的升级优化版本。对于API上的实现区别还是很大的。</li> <li>工作流程:发现设备->配对/绑定设备->建立连接->数据通信。<br> 至于底层如何工作,本人不了解,也不是本文关注的重点。</li> <li>官方文档: <a href="/misc/goto?guid=4959745927891287011" rel="nofollow,noindex">https://developer.android.com/guide/topics/connectivity/bluetooth.html?hl=zh-cn</a></li> </ol> <p>重要实例</p> <ol> <li>经典蓝牙聊天实例: <a href="/misc/goto?guid=4959745927976441274" rel="nofollow,noindex">https://github.com/googlesamples/android-BluetoothChat</a></li> <li>低功耗蓝牙: <a href="/misc/goto?guid=4959745928059303347" rel="nofollow,noindex">https://github.com/googlesamples/android-BluetoothLeGatt</a></li> <li>作为外设(API>=21): <a href="/misc/goto?guid=4959745928154502904" rel="nofollow,noindex">https://github.com/googlesamples/android-BluetoothAdvertisements</a></li> <li>基础概念讲解: <a href="/misc/goto?guid=4959745928235577911" rel="nofollow,noindex">http://blog.csdn.net/qinxiandiqi/article/details/40741269</a></li> <li>深入理论: <a href="/misc/goto?guid=4959727008490868790" rel="nofollow,noindex">http://www.race604.com/android-ble-in-action/</a></li> </ol> <p>注意事项</p> <ol> <li> <p>低功耗蓝牙(BLE)Android 4.3(API 18)以上才支持</p> </li> <li> <p>使用蓝牙需要权限</p> <pre> <code class="language-java"><uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> </code></pre> </li> <li> <p>Android 5.0(API 21) 扫描蓝牙需要定位权限,否则扫描不到设备,实际使用时候发现 5.0不需要也可以扫描,Android 6.0(API 23)以上必须(不知道什么原因,测试机器:MI 2,知道原因的可告知)</p> <pre> <code class="language-java"><uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> 或 <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> </code></pre> <p>官方文档: <a href="/misc/goto?guid=4959745928349516766" rel="nofollow,noindex">https://developer.android.com/guide/topics/connectivity/bluetooth-le.html?hl=zh-cn</a></p> </li> <li> <p>低功耗蓝牙要声明特征,或者代码判断</p> <pre> <code class="language-java">// 如果为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(); } </code></pre> </li> <li> <p>经典蓝牙连接成功后获取一个socket连接得到输入输出流进行通信,低功耗通过特征(具体实现不知道是什么)</p> </li> <li> <p>Android 5.0(API 21)之前不能当成外设(蓝牙耳机、音响等)来使用,只能作为中心即主机</p> </li> </ol> <p>低功耗蓝牙</p> <p>现在蓝牙开发基本上都是低功耗蓝牙,比如心率设备、耳机设备、手环,所以我们先从重要的开始,讲讲低功耗蓝牙的使用,后面在完善经典蓝牙。</p> <p>声明权限</p> <p>创建项目在AndroidManifest.xml中声明权限:</p> <pre> <code class="language-java"><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> </code></pre> <p>如果manifest中声明的蓝牙特性为false,那么在代码中监测是否支持BLE特性,</p> <pre> <code class="language-java">// 使用此检查确定BLE是否支持在设备上,然后你可以有选择性禁用BLE相关的功能 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show(); finish(); } </code></pre> <p>BluetoothAdapter</p> <p>获取BluetoothManager得到蓝牙适配器BluetoothAdapter,注意这两个类都是系统级别,只有一个,代表了你手机上的蓝牙模块</p> <pre> <code class="language-java">final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter(); </code></pre> <p>如果获取到的Adapter为空说明不支持蓝牙,或者没有蓝牙模块</p> <p>开启蓝牙</p> <p>前面获取到了BluetoothAdapter,可以通过调用isEnabled()函数去检测是否开启了蓝牙,false表示没有开启,如果没有开启需要去启用,</p> <pre> <code class="language-java">Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); activity.startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); </code></pre> <p>代码执行完后,会有弹框提示用户是否启用,我们需要在onActivityResult()中判断返回</p> <pre> <code class="language-java">@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(); } } } </code></pre> <p>扫描设备</p> <p>低功耗蓝牙和经典蓝牙的扫描方式不同,如果熟悉经典蓝牙,那就要可能掉进坑了起不来了,哈哈。蓝牙扫描耗电比较严重,所以此处一定要记得在合适的实际停止扫描,比如定时停止、扫描到目标设备就停止(奇怪的是API为何不提供扫描时长的接口呢?)扫描时调用Adapter的startLeScan()方法,然而这个被标记为过时,API>=21被另一个取代。然后通过回掉得到扫描结果(经典蓝牙是广播)</p> <pre> <code class="language-java"> 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, "开始扫描,蓝牙设备"); } } </code></pre> <p>回调函数</p> <pre> <code class="language-java">@Override public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) { // 得到扫描结果 Log.i(TAG, "扫描到的设备, name=" + device.getName() + ",address=" + device.toString()); } </code></pre> <p>注意:device:代表外设即目标设备 rssi:设一个强度值,但是时负值,利用这个值通过公式可以算出离你的距离</p> <p>scanRecord:广播数据,附加的数据,没用到</p> <p>停止扫描</p> <p>扫描完成务必停止,因为扫描不仅耗电,还影响连接速度,所以当要连接的时候,先停止扫描时必须的</p> <pre> <code class="language-java">public void stopScan() { initHandler(); Log.i(TAG, "停止扫描,蓝牙设备"); if (mScanning) { mScanning = false; // 开始扫描的接口,要一样的不然停止不了 mBluetoothAdapter.stopLeScan(this); } if (scanRunnable != null) { mHandler.removeCallbacks(scanRunnable); scanRunnable = null; } } </code></pre> <p>连接设备</p> <p>通常连接设备速度还是很快的,连接理论上来说也是无状态的,所以也需要一个定式任务来,保证超时停止。</p> <pre> <code class="language-java">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; } </code></pre> <p>监听连接回调</p> <pre> <code class="language-java">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; } }; </code></pre> <p>断开连接</p> <pre> <code class="language-java">public void disconnect() { if (mBluetoothAdapter == null || mBluetoothGatt == null) { mConnectionState = STATE_DISCONNECTED; return; } // 连接成功的GATT mBluetoothGatt.disconnect(); mBluetoothGatt.close(); } </code></pre> <p>多设备连接</p> <p>蓝牙适配器没有听过连接多个设备的接口,需要我们自己实现,即获取到目标设备的address后调用连接方法,自己维护多个BluetoothGatt即可(代码稍后放出)。</p> <p>完整代码示例</p> <p>正在做心率相关的蓝牙设备,此处代码发出来。</p> <p>代码还在完善中,仅供参考,稳定代码将会发在Github中</p> <pre> <code class="language-java">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; } } </code></pre> <p> </p> <p>来自:http://lowett.com/2017/03/23/android-bluetooth/</p> <p> </p>