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


    在做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();
            }
        }
    
    
    大家可以看到,stopAdvertising方法内部是调用AdvertiseCallbackWrapper.stopAdvertising方法。这里必须注意stopAdvertising方法的callback必须和start时传入的callback参数是同一个。否则在mLeAdvertisers缓存里是找不到相应的AdvertiseCallbackWrapper的实例的,就无法正常停止广播。
    转载请注明:http://blog.csdn.net/android_jiangjun/article/details/77946857
  • 相关阅读:
    Office办公软件停止工作解决方案
    Jquery blockUI用法
    IE浏览器对js代码的高要求
    IIS中应用程序切换物理路径遇到的问题
    using关键字
    剑指offer-面试题23-链表中环的入口节点-双指针
    剑指offer-面试题22-链表中倒数第k个节点-双指针
    剑指offer-面试题21-调整数组顺序使奇数位于偶数前面-双指针
    剑指offer-面试题20-表示数值的字符串-字符串
    剑指offer-面试题19-正则表达式匹配-字符串
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/9314775.html
Copyright © 2020-2023  润新知