• Android BLE 总结-源码篇(BluetoothLeAdvertiser)


    在做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

  • 相关阅读:
    JDBC
    Listener监听器
    Filter过滤器
    Jstl标签库
    el表达式
    Ajax技术
    数据交换格式之
    MVC模式
    函数
    二维数组练习
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/8608896.html
Copyright © 2020-2023  润新知