Bluetooth LE(低功耗蓝牙) - 第三部分

原文地址: http://blog.csdn.net/likebamboo/article/details/26585333

回顾

     在本系列的前两篇文章中,我们已经了解了一些关于Bluetooth LE的背景并建立一个简单的Activity / Service框架。   在这篇文章中,我们将探讨Bluetooth LE的细节以及蓝牙设备查找的一些问题。

扫描并发现蓝牙设备

     蓝牙设备的发现是十分简单的,它是一个在蓝牙可见范围内查找设备的过程。首先我们要做的就是在Manifest中添加必要的权限,否则我们将在一开始就碰壁。我们需要的权限是android.permission.BLUETOOTH(一般蓝牙使用)和android.permission.BLUETOOTH_ADMIN(额外的任务,如蓝牙发现)。

     在我们深入之前,值得说明的是BleService 将作为一个状态机,在不同的状态执行不同的任务。这些状态中,我们首先要考虑的是扫描状态。当BleService 接收到一个 MSG_START_SCAN  消息后进入扫描状态:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. private static class IncomingHandler extends Handler {  
  2.     @Override  
  3.     public void handleMessage(Message msg) {  
  4.         BleService service = mService.get();  
  5.         if (service != null) {  
  6.             switch (msg.what) {  
  7.                 .  
  8.                 .  
  9.                 .  
  10.                 case MSG_START_SCAN:  
  11.                     service.startScan();  
  12.                     Log.d(TAG, "Start Scan");  
  13.                     break;  
  14.                 default:  
  15.                     super.handleMessage(msg);  
  16.             }  
  17.         }  
  18.     }  
  19. }  

startScan()方法开始扫描:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class BleService extends Service implements   
  2.     BluetoothAdapter.LeScanCallback {  
  3.     private final Map<String,BluetoothDevice> mDevices =   
  4.         new HashMap<String, BluetoothDevice>();  
  5.   
  6.     public enum State {  
  7.         UNKNOWN,  
  8.         IDLE,  
  9.         SCANNING,  
  10.         BLUETOOTH_OFF,  
  11.         CONNECTING,  
  12.         CONNECTED,  
  13.         DISCONNECTING  
  14.     }  
  15.     private BluetoothAdapter mBluetooth = null;  
  16.     private State mState = State.UNKNOWN;  
  17.     .  
  18.     .  
  19.     .  
  20.     private void startScan() {  
  21.         mDevices.clear();  
  22.         setState(State.SCANNING);  
  23.         if (mBluetooth == null) {  
  24.             BluetoothManager bluetoothMgr = (BluetoothManager)   
  25.                 getSystemService(BLUETOOTH_SERVICE);  
  26.             mBluetooth = bluetoothMgr.getAdapter();  
  27.         }  
  28.         if (mBluetooth == null || !mBluetooth.isEnabled()) {  
  29.             setState(State.BLUETOOTH_OFF);  
  30.         } else {  
  31.             mHandler.postDelayed(new Runnable() {  
  32.                 @Override  
  33.                 public void run() {  
  34.                     if (mState == State.SCANNING) {  
  35.                         mBluetooth.stopLeScan(  
  36.                             BleService.this);  
  37.                         setState(State.IDLE);  
  38.                     }  
  39.                 }  
  40.             }, SCAN_PERIOD);  
  41.             mBluetooth.startLeScan(this);  
  42.         }  
  43.     }  
  44. }  
      首先,我们需要确保手机上的蓝牙已启用,如果没有则提示用户打开它。这个过程也很简单。 首先我们需要获取 BluetoothService 实例,这是一个Android系统服务。 通过 BluetoothService 对象可以得到一个代表了设备上蓝牙无线电的 BluetoothAdapter 对象的一个实例。 然后我们可以做一个非空判断,之后调用 isenabled() 方法来确定蓝牙是否打开。 如果是,那么我们就可以下面的操作了,但如果不是则设置适当的状态 (State.BLUETOOTH_OFF)。 状态时变化 将发送一个消息 此Service的所有客户端 (相应 的Activity为

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class BleActivity extends Activity {  
  2.     private final int ENABLE_BT = 1;  
  3.     .  
  4.     .  
  5.     .  
  6.     private void enableBluetooth() {  
  7.         Intent enableBtIntent =   
  8.             new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);  
  9.         startActivityForResult(enableBtIntent, ENABLE_BT);  
  10.     }  
  11.   
  12.     @Override  
  13.     protected void onActivityResult(int requestCode,   
  14.         int resultCode, Intent data) {  
  15.         if(requestCode == ENABLE_BT) {  
  16.             if(resultCode == RESULT_OK) {  
  17.                 //Bluetooth connected, we may continue  
  18.                 startScan();  
  19.             } else {  
  20.                 //The user has elected not to turn on   
  21.                 //Bluetooth. There's nothing we can do  
  22.                 //without it, so let's finish().  
  23.                 finish();  
  24.             }  
  25.         } else {  
  26.             super.onActivityResult(requestCode,   
  27.                 resultCode, data);  
  28.         }  
  29.     }  
  30.   
  31.     private void startScan() {  
  32.         mRefreshItem.setEnabled(false);  
  33.         mDeviceList.setDevices(thisnull);  
  34.         mDeviceList.setScanning(true);  
  35.         Message msg = Message.obtain(null,   
  36.             BleService.MSG_START_SCAN);  
  37.         if (msg != null) {  
  38.             try {  
  39.                 mService.send(msg);  
  40.             } catch (RemoteException e) {  
  41.                 Log.w(TAG,   
  42.                     "Lost connection to service", e);  
  43.                 unbindService(mConnection);  
  44.             }  
  45.         }  
  46.     }  
  47. }  

     为了提示用户打开蓝牙,我们可以使用系统服务来完成,这确保了在不同平台上用户的体验是一致的。虽然在程序中也可以打开蓝牙,但提示用户是推荐的方法。这真的是很容易的,我们通过适当的方式(BluetoothAdapter  的7-9行)启动另一个Activity 来提醒用户我们想做什么,在那个Activity结束的时候接收返回结果(onactivityresult()方法)。

     到目前为止还没有什么具体的Bluetooth LE 相关的内容,但这些都是标准蓝牙需要经历的步骤。

     我们要做的下一件事是扫描Bluetooth LE设备。这其实是很容易的,因为 BluetoothAdapter 类正好包含一个startLeScan()方法!该方法接收一个BluetoothAdapter.LeScanCallback 的实例作为参数,该参数会在扫描过程中接收回调:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class BleService extends Service implements   
  2.     BluetoothAdapter.LeScanCallback  
  3.     private static final String DEVICE_NAME = "SensorTag";  
  4.     .  
  5.     .  
  6.     .  
  7.     private void startScan() {  
  8.         mDevices.clear();  
  9.         setState(State.SCANNING);  
  10.         if (mBluetooth == null) {  
  11.             BluetoothManager bluetoothMgr = (BluetoothManager)   
  12.                 getSystemService(BLUETOOTH_SERVICE);  
  13.             mBluetooth = bluetoothMgr.getAdapter();  
  14.         }  
  15.         if (mBluetooth == null || !mBluetooth.isEnabled()) {  
  16.             setState(State.BLUETOOTH_OFF);  
  17.         } else {  
  18.             mHandler.postDelayed(new Runnable() {  
  19.                 @Override  
  20.                 public void run() {  
  21.                     if (mState == State.SCANNING) {  
  22.                         mBluetooth.stopLeScan(  
  23.                             BleService.this);  
  24.                         setState(State.IDLE);  
  25.                     }  
  26.                 }  
  27.             }, SCAN_PERIOD);  
  28.             mBluetooth.startLeScan(this);  
  29.         }  
  30.     }  
  31.   
  32.     @Override  
  33.     public void onLeScan(final BluetoothDevice device,   
  34.         int rssi, byte[] scanRecord) {  
  35.         if (device != null && !mDevices.containsValue(device) &&   
  36.             device.getName() != null &&   
  37.             device.getName().equals(DEVICE_NAME)) {  
  38.             mDevices.put(device.getAddress(), device);  
  39.             Message msg = Message.obtain(null,   
  40.                 MSG_DEVICE_FOUND);  
  41.             if (msg != null) {  
  42.                 Bundle bundle = new Bundle();  
  43.                 String[] addresses = mDevices.keySet()  
  44.                     .toArray(new String[mDevices.size()]);  
  45.                 bundle.putStringArray(KEY_MAC_ADDRESSES,   
  46.                     addresses);  
  47.                 msg.setData(bundle);  
  48.                 sendMessage(msg);  
  49.             }  
  50.             Log.d(TAG, "Added " + device.getName() + ": " +   
  51.                 device.getAddress());  
  52.         }  
  53.     }  
  54. }  
      关于onstartlescan()很重要的一点是,它只管一件事--开始扫描。 我们必须停止扫描。 根据需求, 一旦 发现设备便停止扫描是 没错的, 在我们的例子中 我们要 扫描一个 固定的时间 所以我们使用  postdelayed()  在未来的某个时间点调用  stoplescan() 停止扫描

      在扫描的过程中每次蓝牙适配器接收来自BLE设备的广播信息 onLeScan() 方法都会被调用。当蓝牙设备处于广播模式的时候通常每秒会发出十次广播信息,所以在扫描的工程中我们必须小心地只回应新的设备。我们通过维护一个已发现设备的映射表(由他们的MAC地址做映射,这在后面很有用)来实现,在扫描过程中如果 onLeScan() 方法被调用,我们检查是否知道这个设备。

      另外我们还需要过滤出那些我们感兴趣的设备,这通常是基于某种特征(在之后的文章中会有更多的介绍)。 SensorTag 文档 表明蓝牙名称为SensorTag,所以我们应当匹配设备名称为SensorTag”的设备

     这里值得一提的是, the host needs to scan while sensors are advertising their presence. This is the heart of security on BLE – sensors must be told by the user to advertise their presence before they can be discovered. (又不知道该怎么翻译了,罪过罪过)一旦蓝牙被发现并建立起主设备与传感器之间的相互信任关系,那么之后主设备就可以直接连接到传感器而无需将其置入广播模式下,although this behaviour any vary on different sensors 。

     每当我们发现一个新设备我们将它加入到映射表(Map)中同时以字符串数组封装所有已发现设备MAC地址并把字符串数组加入到一个 MSG_DEVICE_FOUND  消息中发送给Activity

     还需说明的是我们的Service运行在UI线程中,但我们真的不需要担心它会阻塞UI线程。调用 Bluetooth LE 扫描的操作是异步的,我们开启了一个后台线程去执行扫描并通过onLeScan() 方法回调。如果我们没在onLeScan()方法中做任何复杂的工作我们就不需要担心后台线程所做的任何事情

     Activity还作为一个状态机,利用BleService的状态改变UI。BleService是否处于SCANNING状态决定了Activity的“刷新”菜单是否可用,BleService的状态也决定了是否切换到显示设备列表的Fragment(当发现设备的时候)。每当Activity收到MSG_DEVICE_FOUND 消息时候都会更新已发现设备的列表。我不想在这里解释所有的UI代码,因为他们和BLE并不相关,但如果你想看,源代码 是公开的

     我们运行上述代码后开始扫描设备,如果周边有SensorTag蓝牙设备,他将显示在设备列表中:



下期预告

     现在我们完成了基本的设备扫描查找工作接下来我们需要做的是连接到一个或多个我们已经发现的传感器我们将在下一篇文章中讨论

     本文的源代码在这里 可以找到


-----------------------------------------------------不怎么华丽的分割线----------------------------------------------------------------------

原文链接:http://blog.stylingandroid.com/archives/2419

© 2014, Mark Allison. All rights reserved. This article originally appeared on Styling Android.

Portions of this page are modifications based on work created and shared by Google and used according to terms described in the Creative Commons 3.0 Attribution License   


  第三篇了,继续努力,既然出发了,就一条道走到黑吧。。。



  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值