本文旨在解决Android平台上蓝牙低功耗(BLE)广告无法正常停止的问题。核心在于启动和停止BLE广告时,必须使用同一个AdvertiseCallback实例。文章将详细阐述这一关键原理,并通过代码示例展示正确的广告启动与停止流程,强调AdvertiseCallback作为广告会话唯一标识的重要性,帮助开发者确保BLE广告功能的稳定可靠。
1. Android BLE广告启动概述
在Android平台上,应用程序可以通过蓝牙低功耗(BLE)功能广播数据,这通常用于设备发现或数据传输。启动BLE广告需要通过BluetoothLeAdvertiser类,并配置AdvertiseSettings和AdvertiseData。其中,AdvertiseCallback是一个至关重要的组件,用于接收广告启动成功或失败的回调。
以下是启动BLE广告的基本代码示例:
public class BleAdvertiserManager { private static final String TAG = "BleAdvertiserManager"; private BluetoothLeAdvertiser advertiser; private AdvertiseCallback advertiseCallback; // 用于存储AdvertiseCallback实例 // 假设 serviceId 和 packet 已经定义 private ParcelUuid serviceId = ParcelUuid.fromString("0000180A-0000-1000-8000-00805F9B34FB"); // 示例服务UUID private byte[] packet = new byte[]{0x01, 0x02, 0x03, 0x04}; // 示例服务数据 public BleAdvertiserManager() { BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if (bluetoothAdapter != null) { advertiser = bluetoothAdapter.getBluetoothLeAdvertiser(); } } public void startAdvertising() { if (advertiser == null) { Log.e(TAG, "BluetoothLeAdvertiser not available."); return; } AdvertiseSettings advSettings = new AdvertiseSettings.Builder() .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY) .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH) .setConnectable(false) // 设置为不可连接的广告 .setTimeout(0) // 广告不超时,持续广播 .build(); AdvertiseData data = new AdvertiseData.Builder() .setIncludeDeviceName(true) .setIncludeTxPowerLevel(false) .addServiceUuid(serviceId) .build(); AdvertiseData response = new AdvertiseData.Builder() .setIncludeDeviceName(false) .setIncludeTxPowerLevel(false) .addServiceData(serviceId, packet) .build(); // 创建并存储AdvertiseCallback实例 advertiseCallback = new AdvertiseCallback() { @Override public void onStartSuccess(AdvertiseSettings settingsInEffect) { super.onStartSuccess(settingsInEffect); Log.d(TAG, "BLE advertising started successfully."); } @Override public void onStartFailure(int errorCode) { super.onStartFailure(errorCode); Log.e(TAG, "BLE advertising failed to start: " + errorCode); } }; advertiser.startAdvertising(advSettings, data, response, advertiseCallback); } }
在上述代码中,startAdvertising方法创建了一个AdvertiseCallback匿名内部类实例,并将其传递给advertiser.startAdvertising()。这个advertiseCallback实例被存储在类成员变量中,以便后续使用。
2. 广告停止的常见误区与问题分析
许多开发者在尝试停止BLE广告时,可能会遇到广告无法停止的问题,即使调用了stopAdvertising方法。这通常是由于在停止广告时,传递了与启动广告时不同的AdvertiseCallback实例。
考虑以下错误的停止广告实现方式:
// 错误的停止广告方法示例 public void stopAdvertisingIncorrect() { if (advertiser == null) { Log.d(TAG, "Advertiser is null, nothing to stop."); return; } Log.d(TAG, "Attempting to stop advertising incorrectly..."); // 错误:这里创建了一个新的 AdvertiseCallback 实例 advertiser.stopAdvertising(new AdvertiseCallback() { @Override public void onStartSuccess(AdvertiseSettings settingsInEffect) { // 注意:停止成功的回调实际上是 onStopSuccess,这里是示例错误 Log.d(TAG, "onStopSuccess (incorrect callback)"); super.onStartSuccess(settingsInEffect); } @Override public void onStartFailure(int errorCode) { // 注意:停止失败的回调实际上是 onStopFailure,这里是示例错误 Log.e(TAG, "onStopFailure (incorrect callback): " + errorCode); super.onStartFailure(errorCode); } }); // 即使将 advertiser 置为 null,也不会停止实际的广播 // advertiser = null; }
当使用上述stopAdvertisingIncorrect方法时,日志中可能会出现类似D/BluetoothLeAdvertiser: wrapper is null的提示,并且广告实际上并未停止。这是因为BluetoothLeAdvertiser内部使用AdvertiseCallback对象作为标识符来管理不同的广告会话。如果你传递了一个新的、不同的AdvertiseCallback对象给stopAdvertising,系统将无法识别到你想要停止的是哪一个正在运行的广告会话,从而导致停止操作失败。
3. 正确停止BLE广告的实现
根据Google官方文档的说明,stopAdvertising方法中的callback参数必须是与startAdvertising方法中使用的同一个实例。这是因为AdvertiseCallback对象在内部被用作一个唯一的键,以识别和管理特定的广告会话。
因此,正确的做法是保存启动广告时使用的AdvertiseCallback实例,并在停止广告时重复使用它。
// 正确的停止广告方法示例 public void stopAdvertising() { if (advertiser == null || advertiseCallback == null) { Log.d(TAG, "Advertiser or callback is null, nothing to stop."); return; } Log.d(TAG, "Attempting to stop advertising correctly..."); // 正确:使用之前启动广告时存储的同一个 advertiseCallback 实例 advertiser.stopAdvertising(advertiseCallback); // 停止后可以将 advertiseCallback 置为 null,防止重复停止或资源泄露 advertiseCallback = null; // advertiser = null; // 根据需要决定是否清空advertiser引用 }
通过这种方式,当调用stopAdvertising(advertiseCallback)时,系统能够通过传入的advertiseCallback实例准确地找到对应的正在运行的广告会话并将其停止。
4. 原理深入解析
BluetoothLeAdvertiser的stopAdvertising方法要求传入的AdvertiseCallback对象与启动广告时使用的对象完全一致。这背后的原理是,Android系统在内部维护了一个映射表,将每个活动的BLE广告会话与一个特定的AdvertiseCallback实例关联起来。当调用stopAdvertising时,系统会查找与传入的AdvertiseCallback对象匹配的活动会话,然后将其终止。
Google官方文档对此有明确说明: * Stop Bluetooth LE advertising. The {@code callback} must be the same one use in * {@link BluetoothLeAdvertiser#startAdvertising}. (停止蓝牙低功耗广告。callback必须是与BluetoothLeAdvertiser#startAdvertising中使用的同一个。)
这意味着AdvertiseCallback不仅是一个回调接口,它更是一个会话的唯一标识符。如果传入的AdvertiseCallback对象不匹配,系统就无法知道要停止哪个广告,因此广告会持续运行。
5. 注意事项
- AdvertiseCallback实例管理: 务必在启动广告时将AdvertiseCallback实例存储为一个类的成员变量或以其他方式使其可被stopAdvertising方法访问。
- 生命周期管理: 在Android应用中,应将BLE广告的启动和停止与组件(如Activity或Service)的生命周期同步。例如,在onStart()或onCreate()中启动广告,在onStop()或onDestroy()中停止广告,以避免资源泄露和不必要的电池消耗。
- 权限检查: 在进行BLE操作前,确保应用已获得BLUETOOTH、BLUETOOTH_ADMIN和ACCESS_FINE_LOCATION(或ACCESS_COARSE_LOCATION)等必要的运行时权限。
- 蓝牙状态检查: 在启动或停止广告前,检查设备的蓝牙是否已开启,以及是否支持BLE广告功能(BluetoothAdapter.isMultipleAdvertisementSupported())。
- 错误处理: AdvertiseCallback中的onStartFailure和onStopFailure提供了错误码,开发者应根据这些错误码进行适当的错误处理和用户提示。
6. 总结
Android BLE广告无法停止的问题,其根本原因在于startAdvertising和stopAdvertising方法中AdvertiseCallback实例的不一致。通过确保在停止广告时使用与启动广告时完全相同的AdvertiseCallback对象,开发者可以有效解决此问题,并确保BLE广告功能的正常启动与停止。理解AdvertiseCallback作为广告会话唯一标识符的关键作用,是正确实现Android BLE广告管理的核心。
评论(已关闭)
评论已关闭