在做Android BLE的应用程序时,我们发出广播数据是调用BluetoothLeAdvertiser的startAdvertising方法,如下所示:
[java] view plain copy
- mBluetoothLeAdvertiser.startAdvertising(advertiseSettings,
- advertiseData, myAdvertiseCallback);
那么我打算写的BLE总结之源码篇就以此为线索来分析Android BLE FrameWork方面的东西。
[java] view plain copy
-
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,代码如下:
[java] view plain copy
-
/**
-
* 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方法
[java] view plain copy
- /**
- * 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();
- }
- }
大家可以看到,stopAdvertising方法内部是调用AdvertiseCallbackWrapper.stopAdvertising方法。这里必须注意stopAdvertising方法的callback必须和start时传入的callback参数是同一个。否则在mLeAdvertisers缓存里是找不到相应的AdvertiseCallbackWrapper的实例的,就无法正常停止广播。
转载请注明:http://blog.csdn.net/android_jiangjun/article/details/77946857