在做Android BLE的應用程序時,我們發出廣播數據是調用BluetoothLeAdvertiser的startAdvertising方法,如下所示:
mBluetoothLeAdvertiser.startAdvertising(advertiseSettings,
advertiseData, myAdvertiseCallback);
那麼我打算寫的BLE總結之源碼篇就以此爲線索來分析Android BLE FrameWork方面的東西。
public void startAdvertising(AdvertiseSettings settings,
AdvertiseData advertiseData, final AdvertiseCallback callback) {
startAdvertising(settings, advertiseData, null, callback);
}
public void startAdvertising(AdvertiseSettings settings,
AdvertiseData advertiseData, AdvertiseData scanResponse,
final AdvertiseCallback callback) {
synchronized (mLeAdvertisers) {
//該check只是檢查mBluetoothAdater是否爲null和其狀態是否爲State_ON
BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
if (callback == null) {
throw new IllegalArgumentException("callback cannot be null");
}
if (!mBluetoothAdapter.isMultipleAdvertisementSupported() &&
!mBluetoothAdapter.isPeripheralModeSupported()) {//是否支持廣播和作爲外圍設備
postStartFailure(callback,
AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED);
return;
}
boolean isConnectable = settings.isConnectable();
if (totalBytes(advertiseData, isConnectable) > MAX_ADVERTISING_DATA_BYTES ||
totalBytes(scanResponse, false) > MAX_ADVERTISING_DATA_BYTES) {
postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE);
return;
}
if (mLeAdvertisers.containsKey(callback)) {
postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED);
return;
}
IBluetoothGatt gatt;
try {
gatt = mBluetoothManager.getBluetoothGatt();
} catch (RemoteException e) {
Log.e(TAG, "Failed to get Bluetooth gatt - ", e);
postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR);
return;
}
AdvertiseCallbackWrapper wrapper = new AdvertiseCallbackWrapper(callback, advertiseData,
scanResponse, settings, gatt);
wrapper.startRegisteration();
}
}
大家可以看到在startAdvertising內部,首先經過了一系列的判斷,然後包裝了一個叫作AdvertiseCallbackWrapper的類來做發廣播數據的行爲。
我們先看一下startAdvertising內部都是做了哪些判斷:
1.判斷藍牙是否已經打開,否則拋出異常。
2.判斷回調callback是否爲空
3.判斷當前設備是否支持廣播數據和作爲外圍設備
4.判斷廣播數據包的長度是否超過了31字節
5.判斷廣播是否已經開始
經過了這5步初步的判斷,下面來到了最重要的地方,mBluetoothManager.getBluetoothGatt();獲取一個引用,最終的發送廣播和停止廣播都是通過這個引用來進行實現的。這裏不進行展開,因爲本文主要是對BluetoothLeAdvertiser的解讀。
下面我們就來看看剛纔提到的AdvertiseCallbackWrapper,代碼如下:
/**
* Bluetooth GATT interface callbacks for advertising.
*/
private class AdvertiseCallbackWrapper extends BluetoothGattCallbackWrapper {
private static final int LE_CALLBACK_TIMEOUT_MILLIS = 2000;
private final AdvertiseCallback mAdvertiseCallback;
private final AdvertiseData mAdvertisement;
private final AdvertiseData mScanResponse;
private final AdvertiseSettings mSettings;
private final IBluetoothGatt mBluetoothGatt;
// mClientIf 0: not registered
// -1: advertise stopped or registration timeout
// >0: registered and advertising started
private int mClientIf;
private boolean mIsAdvertising = false;
public AdvertiseCallbackWrapper(AdvertiseCallback advertiseCallback,
AdvertiseData advertiseData, AdvertiseData scanResponse,
AdvertiseSettings settings,
IBluetoothGatt bluetoothGatt) {
mAdvertiseCallback = advertiseCallback;
mAdvertisement = advertiseData;
mScanResponse = scanResponse;
mSettings = settings;
mBluetoothGatt = bluetoothGatt;
mClientIf = 0;
}
public void startRegisteration() {
synchronized (this) {
if (mClientIf == -1) return;//這個就不解釋了
try {
UUID uuid = UUID.randomUUID();
mBluetoothGatt.registerClient(new ParcelUuid(uuid), this);//註冊
wait(LE_CALLBACK_TIMEOUT_MILLIS);//等待2秒,在過程中會依次回調onClientRegistered和onMultiAdvertiseCallback
} catch (InterruptedException | RemoteException e) {
Log.e(TAG, "Failed to start registeration", e);
}
//註冊成功並且廣播成功,加入廣播緩存,以callback爲key的Hashmap,callback爲用戶自己定義的Callback
if (mClientIf > 0 && mIsAdvertising) {
mLeAdvertisers.put(mAdvertiseCallback, this);
} else if (mClientIf <= 0) {//註冊失敗
// Registration timeout, reset mClientIf to -1 so no subsequent operations can
// proceed.
if (mClientIf == 0) mClientIf = -1;
// Post internal error if registration failed.
postStartFailure(mAdvertiseCallback,
AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR);
} else {//註冊成功但廣播開啓失敗
// Unregister application if it's already registered but advertise failed.
try {
mBluetoothGatt.unregisterClient(mClientIf);
mClientIf = -1;
} catch (RemoteException e) {
Log.e(TAG, "remote exception when unregistering", e);
}
}
}
}
public void stopAdvertising() {
synchronized (this) {
try {
mBluetoothGatt.stopMultiAdvertising(mClientIf);
wait(LE_CALLBACK_TIMEOUT_MILLIS);
} catch (InterruptedException | RemoteException e) {
Log.e(TAG, "Failed to stop advertising", e);
}
// Advertise callback should have been removed from LeAdvertisers when
// onMultiAdvertiseCallback was called. In case onMultiAdvertiseCallback is never
// invoked and wait timeout expires, remove callback here.
if (mLeAdvertisers.containsKey(mAdvertiseCallback)) {
mLeAdvertisers.remove(mAdvertiseCallback);
}
}
}
/**
* Application interface registered - app is ready to go
*/
@Override
public void onClientRegistered(int status, int clientIf) {
Log.d(TAG, "onClientRegistered() - status=" + status + " clientIf=" + clientIf);
synchronized (this) {
if (status == BluetoothGatt.GATT_SUCCESS) {
try {
if (mClientIf == -1) {//在2秒內未完成註冊,超時
// Registration succeeds after timeout, unregister client.
mBluetoothGatt.unregisterClient(clientIf);
} else {//完成註冊,並開始廣播
mClientIf = clientIf;
mBluetoothGatt.startMultiAdvertising(mClientIf, mAdvertisement,
mScanResponse, mSettings);
}
return;
} catch (RemoteException e) {
Log.e(TAG, "failed to start advertising", e);
}
}
// Registration failed.
mClientIf = -1;
notifyAll();
}
}
@Override
public void onMultiAdvertiseCallback(int status, boolean isStart,
AdvertiseSettings settings) {
synchronized (this) {
if (isStart) {//廣播成功時的回調
if (status == AdvertiseCallback.ADVERTISE_SUCCESS) {
// Start success
mIsAdvertising = true;
postStartSuccess(mAdvertiseCallback, settings);
} else {
// Start failure.
postStartFailure(mAdvertiseCallback, status);
}
} else {//stop 時的回調,用來反註冊和清除緩存的callback
// unregister client for stop.
try {
mBluetoothGatt.unregisterClient(mClientIf);
mClientIf = -1;
mIsAdvertising = false;
mLeAdvertisers.remove(mAdvertiseCallback);
} catch (RemoteException e) {
Log.e(TAG, "remote exception when unregistering", e);
}
}
notifyAll();
}
}
}
private void postStartFailure(final AdvertiseCallback callback, final int error) {
mHandler.post(new Runnable() {
@Override
public void run() {
callback.onStartFailure(error);
}
});
}
private void postStartSuccess(final AdvertiseCallback callback,
final AdvertiseSettings settings) {
mHandler.post(new Runnable() {
@Override
public void run() {
callback.onStartSuccess(settings);
}
});
}
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方法
/**
* Stop Bluetooth LE advertising. The {@code callback} must be the same one use in
* {@link BluetoothLeAdvertiser#startAdvertising}.
* <p>
* Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
*
* @param callback {@link AdvertiseCallback} identifies the advertising instance to stop.
*/
public void stopAdvertising(final AdvertiseCallback callback) {
synchronized (mLeAdvertisers) {
if (callback == null) {
throw new IllegalArgumentException("callback cannot be null");
}
AdvertiseCallbackWrapper wrapper = mLeAdvertisers.get(callback);
if (wrapper == null) return;
wrapper.stopAdvertising();
}
}
轉載請註明:http://blog.csdn.net/android_jiangjun/article/details/77946857