Android BLE 總結-源碼篇(BluetoothLeAdvertiser)


在做Android BLE的應用程序時,我們發出廣播數據是調用BluetoothLeAdvertiser的startAdvertising方法,如下所示:

[java] view plain copy
  1. mBluetoothLeAdvertiser.startAdvertising(advertiseSettings,  
  2.                 advertiseData, myAdvertiseCallback);  



那么我打算寫的BLE總結之源碼篇就以此為線索來分析Android BLE FrameWork方面的東西。

 

[java] view plain copy
  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,代碼如下:

 

[java] view plain copy
  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方法

 

 
[java] view plain copy
  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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM