1. 前言
蓝牙在我们生活中扮演者许多重要的角色, 蓝牙耳机, 蓝牙音箱, 蓝牙灯泡, 蓝牙智能硬件等等! 可见, 蓝牙无处不在, 而且也是物联网的基础纽带, 它改变了我们的许多生活方式, 给我们带来了许多益处.
前段时间我也简单地使用了小程序的蓝牙 API 操作, 接下来我也要开始重新认识下 Android 蓝牙开发, 蓝牙又分为经典蓝牙和 BLE(低功耗蓝牙), 这次我主要学习蓝牙 BLE, 所以我第一时间找了谷歌给我们提供了官方源码 demo:
2. 权限
配置文件需要先声明蓝牙权限:
- <!-- 蓝牙必须的权限 -->
- <uses-permission Android:name="android.permission.BLUETOOTH" />
- <uses-permission Android:name="android.permission.BLUETOOTH_ADMIN" />
除了蓝牙权限外, 还有如下权限是声明程序是否可用于支持 BLE 或者支持出 BLE 外的设备; 如果 required=true, 则应用只能在支持 BLE 的 Android 设备上安装运行, 不支持 BLE 的设备将 finish.
<uses-feature Android:name="android.hardware.bluetooth_le" Android:required="true"/>
此外还有一个很关键的问题, 不知道你们在开发中有没有遇到过呢? 官方 demo 没有适配到这里. Android6.0 以上的手机仅仅是添加如上蓝牙权限是不行的, 这将会造成无法扫描到其他设备, 针对这问题, 果断查了资料, 需要添加位置权限;
- <!--android6.0 以上使用蓝牙需要的权限, 否则在 Android6.0 以上的手机扫描不到蓝牙设备 -->
- <uses-permission Android:name="android.permission.ACCESS_COARSE_LOCATION"
- />
- <uses-permission Android:name="android.permission.ACCESS_FINE_LOCATION"
- />
在 Android6.0 以上 位置相关的权限属于运行时权限, 所以代码同时也要请求一下 让用户确认, 这个就不多说了.
3. 是否支持蓝牙 BLE
在前面的权限也说了如果 required=true, 则应用只能在支持 BLE 的 Android 设备上安装运行, 不支持 BLE 的设备将 finish;
如果想在 java 代码上实现上面的逻辑也是可以的, 官方 demo 如下:
- /** 使用此检查确定设备是否支持 BLE. 然后你可以有选择地禁用与 BLE 相关的功能.*/
- if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
- Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
- finish();
- }
4. 初始化蓝牙适配器
初始化蓝牙适配器. 对于 API 级别 18 及更高级别, 请通过 BluetoothManager 获取对 BluetoothAdapter 的引用. 最后这里记得要判空!
- // 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;
- }
5. 当前设备是否已开启蓝牙
要想操作蓝牙, 必须确保在设备上启用蓝牙. 如果当前未启用蓝牙, 因此我们可以触发 Intent 调用系统显示一个对话框, 要求用户授予启用蓝牙的权限.
- if (!mBluetoothAdapter.isEnabled()) {
- if (!mBluetoothAdapter.isEnabled()) {
- Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
- startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
- }
- }
6. 初始化 listview 列表适配器
这里目的就是将扫描到的蓝牙设备显示到 listview 列表上, 然后用户就可以点击对应的 item, 程序就可以进行下一步操作. 这一步骤可根据需要定制.
7. 扫描
官方 demo 的扫描方法如下, 如果要扫描的话就要传入 true 执行 scanLeDvice(true) 方法, 然后蓝牙适配器就调用 startLeScan() 方法进行扫描, mLeScanCallback 是扫描回调, 下面会说到; 执行扫描的同时开启了一个延时操作, 时间到了就调用 stopLeScan() 方法停止扫描.
我觉得这里设置了一个延时停止扫描操作是非常友好的, 因为扫描非常耗资源.
- 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();
- }
接下来就看看扫描的 Callback 回调吧, 这个回调里面有个 onLeScan 方法, 蓝牙扫描成功后的结果会返回此方法中, 然后就可以处理 BluetoothDevice 拿到设备信息 最后展示到前面初始化的 listview 列表中, 大概流程就是这样.
- // 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();
- }
- });
- }
- };
接着再看看这个 onLeScan 方法中的值吧!
第一个参数 device, 表示一个远程蓝牙设备, 里面有它独有的蓝牙地址 Address 和 Name 等, 所以后续需要进行连接蓝牙操作也需要用到这里获取的蓝牙 Address;
第二个参数 rssi 表示扫描到的设备信号强度, 这里应该可以用来判断距离的远近.
第三个参数 scanRecord 表示远程设备提供的广告记录的内容.
至此已经完成初始化配置, 一些设备的判断逻辑和扫描操作了, 如果能成功地扫描到设备并展示到界面上的话, 下一步如果用户点击了列表, 将进行蓝牙连接和相关的读写操作!
所以下一篇文章不出意外的话, 就是继续学习 Android 蓝牙的连接和读写操作.
来源: http://www.jianshu.com/p/f20327b40268