码迷,mamicode.com
首页 > 移动开发 > 详细

Android BLE 总结-源码篇(BluetoothLeAdvertiser)

时间:2018-03-20 15:17:07      阅读:433      评论:0      收藏:0      [点我收藏+]

标签:isp   会展   变量   bluetooth   开启   parcel   callback   moved   文章   

在做Android BLE的应用程序时,我们发出广播数据是调用BluetoothLeAdvertiser的startAdvertising方法,如下所示:

  1. mBluetoothLeAdvertiser.startAdvertising(advertiseSettings,  
  2.                 advertiseData, myAdvertiseCallback);  



那么我打算写的BLE总结之源码篇就以此为线索来分析Android BLE FrameWork方面的东西。

 

  1.  public void startAdvertising(AdvertiseSettings settings,  
  2.             AdvertiseData advertiseData, final AdvertiseCallback callback) {  
  3.         startAdvertising(settings, advertiseData, null, callback);  
  4.     }  
  5. public void startAdvertising(AdvertiseSettings settings,  
  6.             AdvertiseData advertiseData, AdvertiseData scanResponse,  
  7.             final AdvertiseCallback callback) {  
  8.         synchronized (mLeAdvertisers) {  
  9.   
  10. //该check只是检查mBluetoothAdater是否为null和其状态是否为State_ON  
  11.   
  12.             BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);  
  13.             if (callback == null) {  
  14.                 throw new IllegalArgumentException("callback cannot be null");  
  15.             }  
  16.   
  17.             if (!mBluetoothAdapter.isMultipleAdvertisementSupported() &&  
  18.                     !mBluetoothAdapter.isPeripheralModeSupported()) {//是否支持广播和作为外围设备  
  19.                 postStartFailure(callback,  
  20.                         AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED);  
  21.                 return;  
  22.             }  
  23.             boolean isConnectable = settings.isConnectable();  
  24.             if (totalBytes(advertiseData, isConnectable) > MAX_ADVERTISING_DATA_BYTES ||  
  25.                     totalBytes(scanResponse, false) > MAX_ADVERTISING_DATA_BYTES) {  
  26.                 postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE);  
  27.                 return;  
  28.             }  
  29.             if (mLeAdvertisers.containsKey(callback)) {  
  30.                 postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED);  
  31.                 return;  
  32.             }  
  33.             IBluetoothGatt gatt;  
  34.             try {  
  35.                 gatt = mBluetoothManager.getBluetoothGatt();  
  36.             } catch (RemoteException e) {  
  37.                 Log.e(TAG, "Failed to get Bluetooth gatt - ", e);  
  38.                 postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR);  
  39.                 return;  
  40.             }  
  41.             AdvertiseCallbackWrapper wrapper = new AdvertiseCallbackWrapper(callback, advertiseData,  
  42.                     scanResponse, settings, gatt);  
  43.             wrapper.startRegisteration();  
  44.         }  
  45.     }  



大家可以看到在startAdvertising内部,首先经过了一系列的判断,然后包装了一个叫作AdvertiseCallbackWrapper的类来做发广播数据的行为。

我们先看一下startAdvertising内部都是做了哪些判断:
1.判断蓝牙是否已经打开,否则抛出异常。

2.判断回调callback是否为空

3.判断当前设备是否支持广播数据和作为外围设备

4.判断广播数据包的长度是否超过了31字节

5.判断广播是否已经开始

经过了这5步初步的判断,下面来到了最重要的地方,mBluetoothManager.getBluetoothGatt();获取一个引用,最终的发送广播和停止广播都是通过这个引用来进行实现的。这里不进行展开,因为本文主要是对BluetoothLeAdvertiser的解读。

下面我们就来看看刚才提到的AdvertiseCallbackWrapper,代码如下:

 

  1. /** 
  2.      * Bluetooth GATT interface callbacks for advertising. 
  3.      */  
  4.     private class AdvertiseCallbackWrapper extends BluetoothGattCallbackWrapper {  
  5.         private static final int LE_CALLBACK_TIMEOUT_MILLIS = 2000;  
  6.         private final AdvertiseCallback mAdvertiseCallback;  
  7.         private final AdvertiseData mAdvertisement;  
  8.         private final AdvertiseData mScanResponse;  
  9.         private final AdvertiseSettings mSettings;  
  10.         private final IBluetoothGatt mBluetoothGatt;  
  11.         // mClientIf 0: not registered  
  12.         // -1: advertise stopped or registration timeout  
  13.         // >0: registered and advertising started  
  14.         private int mClientIf;  
  15.         private boolean mIsAdvertising = false;  
  16.         public AdvertiseCallbackWrapper(AdvertiseCallback advertiseCallback,  
  17.                 AdvertiseData advertiseData, AdvertiseData scanResponse,  
  18.                 AdvertiseSettings settings,  
  19.                 IBluetoothGatt bluetoothGatt) {  
  20.             mAdvertiseCallback = advertiseCallback;  
  21.             mAdvertisement = advertiseData;  
  22.             mScanResponse = scanResponse;  
  23.             mSettings = settings;  
  24.             mBluetoothGatt = bluetoothGatt;  
  25.             mClientIf = 0;  
  26.         }  
  27.         public void startRegisteration() {  
  28.             synchronized (this) {  
  29.                 if (mClientIf == -1) return;//这个就不解释了  
  30.                 try {  
  31.                     UUID uuid = UUID.randomUUID();  
  32.                     mBluetoothGatt.registerClient(new ParcelUuid(uuid), this);//注册  
  33.                     wait(LE_CALLBACK_TIMEOUT_MILLIS);//等待2秒,在过程中会依次回调onClientRegistered和onMultiAdvertiseCallback  
  34.                 } catch (InterruptedException | RemoteException e) {  
  35.                     Log.e(TAG, "Failed to start registeration", e);  
  36.                 }  
  37.   
  38. //注册成功并且广播成功,加入广播缓存,以callback为key的Hashmap,callback为用户自己定义的Callback  
  39.   
  40.                 if (mClientIf > 0 && mIsAdvertising) {  
  41.                     mLeAdvertisers.put(mAdvertiseCallback, this);  
  42.                 } else if (mClientIf <= 0) {//注册失败  
  43.                     // Registration timeout, reset mClientIf to -1 so no subsequent operations can  
  44.                     // proceed.  
  45.                     if (mClientIf == 0) mClientIf = -1;  
  46.                     // Post internal error if registration failed.  
  47.                     postStartFailure(mAdvertiseCallback,  
  48.                             AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR);  
  49.                 } else {//注册成功但广播开启失败  
  50.                     // Unregister application if it‘s already registered but advertise failed.  
  51.                     try {  
  52.                         mBluetoothGatt.unregisterClient(mClientIf);  
  53.                         mClientIf = -1;  
  54.                     } catch (RemoteException e) {  
  55.                         Log.e(TAG, "remote exception when unregistering", e);  
  56.                     }  
  57.                 }  
  58.             }  
  59.         }  
  60.         public void stopAdvertising() {  
  61.             synchronized (this) {  
  62.                 try {  
  63.                     mBluetoothGatt.stopMultiAdvertising(mClientIf);  
  64.                     wait(LE_CALLBACK_TIMEOUT_MILLIS);  
  65.                 } catch (InterruptedException | RemoteException e) {  
  66.                     Log.e(TAG, "Failed to stop advertising", e);  
  67.                 }  
  68.                 // Advertise callback should have been removed from LeAdvertisers when  
  69.                 // onMultiAdvertiseCallback was called. In case onMultiAdvertiseCallback is never  
  70.                 // invoked and wait timeout expires, remove callback here.  
  71.                 if (mLeAdvertisers.containsKey(mAdvertiseCallback)) {  
  72.                     mLeAdvertisers.remove(mAdvertiseCallback);  
  73.                 }  
  74.             }  
  75.         }  
  76.         /** 
  77.          * Application interface registered - app is ready to go 
  78.          */  
  79.         @Override  
  80.         public void onClientRegistered(int status, int clientIf) {  
  81.             Log.d(TAG, "onClientRegistered() - status=" + status + " clientIf=" + clientIf);  
  82.             synchronized (this) {  
  83.                 if (status == BluetoothGatt.GATT_SUCCESS) {  
  84.                     try {  
  85.                         if (mClientIf == -1) {//在2秒内未完成注册,超时  
  86.                             // Registration succeeds after timeout, unregister client.  
  87.                             mBluetoothGatt.unregisterClient(clientIf);  
  88.                         } else {//完成注册,并开始广播  
  89.                             mClientIf = clientIf;  
  90.                             mBluetoothGatt.startMultiAdvertising(mClientIf, mAdvertisement,  
  91.                                     mScanResponse, mSettings);  
  92.                         }  
  93.                         return;  
  94.                     } catch (RemoteException e) {  
  95.                         Log.e(TAG, "failed to start advertising", e);  
  96.                     }  
  97.                 }  
  98.                 // Registration failed.  
  99.                 mClientIf = -1;  
  100.                 notifyAll();  
  101.             }  
  102.         }  
  103.         @Override  
  104.         public void onMultiAdvertiseCallback(int status, boolean isStart,  
  105.                 AdvertiseSettings settings) {  
  106.             synchronized (this) {  
  107.                 if (isStart) {//广播成功时的回调  
  108.                     if (status == AdvertiseCallback.ADVERTISE_SUCCESS) {  
  109.                         // Start success  
  110.                         mIsAdvertising = true;  
  111.                         postStartSuccess(mAdvertiseCallback, settings);  
  112.                     } else {  
  113.                         // Start failure.  
  114.                         postStartFailure(mAdvertiseCallback, status);  
  115.                     }  
  116.                 } else {//stop 时的回调,用来反注册和清除缓存的callback  
  117.                     // unregister client for stop.  
  118.                     try {  
  119.                         mBluetoothGatt.unregisterClient(mClientIf);  
  120.                         mClientIf = -1;  
  121.                         mIsAdvertising = false;  
  122.                         mLeAdvertisers.remove(mAdvertiseCallback);  
  123.                     } catch (RemoteException e) {  
  124.                         Log.e(TAG, "remote exception when unregistering", e);  
  125.                     }  
  126.                 }  
  127.                 notifyAll();  
  128.             }  
  129.         }  
  130.     }  
  131.     private void postStartFailure(final AdvertiseCallback callback, final int error) {  
  132.         mHandler.post(new Runnable() {  
  133.             @Override  
  134.             public void run() {  
  135.                 callback.onStartFailure(error);  
  136.             }  
  137.         });  
  138.     }  
  139.     private void postStartSuccess(final AdvertiseCallback callback,  
  140.             final AdvertiseSettings settings) {  
  141.         mHandler.post(new Runnable() {  
  142.             @Override  
  143.             public void run() {  
  144.                 callback.onStartSuccess(settings);  
  145.             }  
  146.         });  
  147.     }  



AdvertiseCallbackWrapper的成员变量mClientIf非常重要,在广播发送和停止的过程中起着重要的作用。这里先简单的记住该属性的以下特征:

mClientIf=0——>未注册

mClinetIf=-1——>广播停止或注册超时

mClientIf>0——>已注册并且已经广播成功

mClientIf默认值为0

这时我们追踪到startRegisteration这个方法了,该方法里面调用了registerClient方法,经过IPC通信后会回调到onClientRegistered方法,继续调用到了startMultiAdvertising方法,接着触发onMultiAdvertiseCallback,成功发送广播后,将该AdvertiseCallbackWrapper对象加入mLeAdvertisers。

这里我们需要注意和了解以下几点:

1.在调用startRegisteration的2秒的时间内,如果没有注册成功且广播成功,这次广播数据的行为均为失败。

2.即使2秒之后onClientRegistered回调,也将视为注册未成功,并进行解注册操作。

 

startAdvertising方法就到这,至于更底层的细节后续的文章会展开,下面我们看一下其对应的stopAdvertising方法

 

 
  1. /** 
  2.    * Stop Bluetooth LE advertising. The {@code callback} must be the same one use in 
  3.    * {@link BluetoothLeAdvertiser#startAdvertising}. 
  4.    * <p> 
  5.    * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. 
  6.    * 
  7.    * @param callback {@link AdvertiseCallback} identifies the advertising instance to stop. 
  8.    */  
  9.   public void stopAdvertising(final AdvertiseCallback callback) {  
  10.       synchronized (mLeAdvertisers) {  
  11.           if (callback == null) {  
  12.               throw new IllegalArgumentException("callback cannot be null");  
  13.           }  
  14.           AdvertiseCallbackWrapper wrapper = mLeAdvertisers.get(callback);  
  15.           if (wrapper == null) return;  
  16.           wrapper.stopAdvertising();  
  17.       }  
  18.   }  


大家可以看到,stopAdvertising方法内部是调用AdvertiseCallbackWrapper.stopAdvertising方法。这里必须注意stopAdvertising方法的callback必须和start时传入的callback参数是同一个。否则在mLeAdvertisers缓存里是找不到相应的AdvertiseCallbackWrapper的实例的,就无法正常停止广播。

转载请注明:http://blog.csdn.net/android_jiangjun/article/details/77946857

Android BLE 总结-源码篇(BluetoothLeAdvertiser)

标签:isp   会展   变量   bluetooth   开启   parcel   callback   moved   文章   

原文地址:https://www.cnblogs.com/Free-Thinker/p/8608896.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!