在本系列之前的文章中,我们了解了 Bluetooth LE(低功耗蓝牙,后文简称为BLE)的一些背景并且构建了一个简单的Activity/Service模式的蓝牙低功耗框架。在今天的文章里,我们将更深入的探讨BLE的技术细节,并且实现BLE下的“设备发现”功能。
发现设备简单的说,是在蓝牙可见范围内搜索可用设备的过程。为了避免一开始就因为缺乏权限而无法实现搜索,首先我们需要在AndroidManifest中添加必要的权限。我们所需要添加的权限有android.permission.BLUETOOTH
以及android.permission.BLUETOOTH_ADMIN
。其中第一个权限是 Android使用蓝牙所必要的权限,而第二个则是一些蓝牙附加功能的权限,比如本次讨论的发现设备功能。
在我们开始一头钻入代码之前,有必要解释一下本文中的BleService
是以状态机的形式运作的。BleService
可以在不同的状态下执行不同的任务,所以我们从第一个状态——SCANNING
开始切入。BleService
会在收到一条名为MSG_START_SCAN
的消息后进入这个状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
; html-script: false ] private static class IncomingHandler extends Handler { @Override public void handleMessage(Message msg) { BleService service = mService.get(); if (service != null) { switch (msg.what) { . . . case MSG_START_SCAN: service.startScan(); Log.d(TAG, "Start Scan"); break; default: super.handleMessage(msg); } } } } |
然后是开始搜索的startScan()
函数代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
; html-script: false ] public class BleService extends Service implements BluetoothAdapter.LeScanCallback { private final Map mDevices = new HashMap(); public enum State { UNKNOWN, IDLE, SCANNING, BLUETOOTH_OFF, CONNECTING, CONNECTED, DISCONNECTING } private BluetoothAdapter mBluetooth = null; private State mState = State.UNKNOWN; . . . private void startScan() { mDevices.clear(); setState(State.SCANNING); if (mBluetooth == null) { BluetoothManager bluetoothMgr = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE); mBluetooth = bluetoothMgr.getAdapter(); } if (mBluetooth == null || !mBluetooth.isEnabled()) { setState(State.BLUETOOTH_OFF); } else { mHandler.postDelayed(new Runnable() { @Override public void run() { if (mState == State.SCANNING) { mBluetooth.stopLeScan( BleService.this); setState(State.IDLE); } } }, SCAN_PERIOD); mBluetooth.startLeScan(this); } } } |
从代码中我们不难发现:首先,我们确认了蓝牙服务是否已经开启,如果检测到用户关闭了蓝牙服务,那么就需要去提示用户打开。这一过程的实现非常简单,只要先获取Android蓝牙系统服务BluetoothService
的实例对象,然后从这个对象中我们又可以获取到代表蓝牙射频的BluetoothAdapter
实例。注意这里需要做一下非空判断,接着可以通过这个Adapter
实例的isEnabled()
函数来判断蓝牙是否打开并且处于可用状态了。如果服务是不可用状态,那么我们需要定义一个恰当的状态,并且通知给所有监听了服务的客户端,以便于进一步的处理(本文中就是我们的 Activity)。
发现设备简单的说,是在蓝牙可见范围内搜索可用设备的过程。为了避免一开始就因为缺乏权限而无法实现搜索,首先我们需要在AndroidManifest中添加必要的权限。我们所需要添加的权限有android.permission.BLUETOOTH
以及android.permission.BLUETOOTH_ADMIN
。其中第一个权限是 Android使用蓝牙所必要的权限,而第二个则是一些蓝牙附加功能的权限,比如本次讨论的发现设备功能。
在我们开始一头钻入代码之前,有必要解释一下本文中的BleService
是以状态机的形式运作的。BleService
可以在不同的状态下执行不同的任务,所以我们从第一个状态——SCANNING
开始切入。BleService
会在收到一条名为MSG_START_SCAN
的消息后进入这个状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
; html-script: false ] private static class IncomingHandler extends Handler { @Override public void handleMessage(Message msg) { BleService service = mService.get(); if (service != null) { switch (msg.what) { . . . case MSG_START_SCAN: service.startScan(); Log.d(TAG, "Start Scan"); break; default: super.handleMessage(msg); } } } } |
然后是开始搜索的startScan()
函数代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
; html-script: false ] public class BleService extends Service implements BluetoothAdapter.LeScanCallback { private final Map mDevices = new HashMap(); public enum State { UNKNOWN, IDLE, SCANNING, BLUETOOTH_OFF, CONNECTING, CONNECTED, DISCONNECTING } private BluetoothAdapter mBluetooth = null; private State mState = State.UNKNOWN; . . . private void startScan() { mDevices.clear(); setState(State.SCANNING); if (mBluetooth == null) { BluetoothManager bluetoothMgr = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE); mBluetooth = bluetoothMgr.getAdapter(); } if (mBluetooth == null || !mBluetooth.isEnabled()) { setState(State.BLUETOOTH_OFF); } else { mHandler.postDelayed(new Runnable() { @Override public void run() { if (mState == State.SCANNING) { mBluetooth.stopLeScan( BleService.this); setState(State.IDLE); } } }, SCAN_PERIOD); mBluetooth.startLeScan(this); } } } |
从代码中我们不难发现:首先,我们确认了蓝牙服务是否已经开启,如果检测到用户关闭了蓝牙服务,那么就需要去提示用户打开。这一过程的实现非常简单,只要先获取Android蓝牙系统服务BluetoothService
的实例对象,然后从这个对象中我们又可以获取到代表蓝牙射频的BluetoothAdapter
实例。注意这里需要做一下非空判断,接着可以通过这个Adapter
实例的isEnabled()
函数来判断蓝牙是否打开并且处于可用状态了。如果服务是不可用状态,那么我们需要定义一个恰当的状态,并且通知给所有监听了服务的客户端,以便于进一步的处理(本文中就是我们的 Activity)。