ATAK插件BLE扫描报错:扫描过频及找不到回调包装器求助
解决ATAK插件BLE扫描问题:扫描过频、回调包装器缺失及页面闪退
问题现状
参考官方BLE Gatt示例代码为ATAK插件(com.atakmap.android.helloworld.plugin)添加BLE功能,已更新所有过时方法,通过DeviceScanActivity的BroadcastReceiver与主程序做Intent通信,但存在以下问题:
- BLE设备页面偶尔显示少量设备,多数时候为空
- 频繁触发「扫描过频」和「could not find callback wrapper」错误
- 无法在示例核心函数中找到扫描频率控制逻辑
- 已有Flutter应用可正常扫描周边BLE设备,但当前插件的扫描页面弹出几秒后即消失
核心疑问
- 「could not find callback wrapper」错误的具体含义是什么?
- 如何修复扫描过频的问题?
- 怎么解决扫描页面闪退的问题?
相关代码片段
DeviceScanListener广播接收器代码
/** * Broadcast Receiver that is responsible for getting the data back to the * plugin. */ static class DeviceScanListener extends BroadcastReceiver { private boolean registered = false; private DeviceScanActivity.DeviceScanReceiver sdr = null; synchronized public void register(Context context, DeviceScanActivity.DeviceScanReceiver sdr) { if (!registered) context.registerReceiver(this, new IntentFilter(DSCAN_INFO)); this.sdr = sdr; registered = true; } @Override public void onReceive(Context context, Intent intent) { synchronized (this) { try { Bundle extras = intent.getExtras(); if (extras != null) { ArrayList<BluetoothDevice> s = mLeDeviceListAdapter.mLeDevices; for (int i = 0; i < mLeDeviceListAdapter.mLeDevices.size(); i++){ BluetoothDevice currb = mLeDeviceListAdapter.mLeDevices.get(i); Log.d("mine","Stuff in the DeviceList"); Log.d("mine",String.valueOf(i)); } if (s != null && sdr != null) sdr.onDeviceScanReceived(s); } } catch (Exception ignored) { } if (registered) { context.unregisterReceiver(this); registered = false; } } } }
启动BLE扫描的按钮点击代码
final Button connectDevice = helloView.findViewById(R.id.connect_device); connectDevice.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //Todo dd1.register(getMapView().getContext(), ddr); Intent intent = new Intent(); intent.setClassName("com.atakmap.android.helloworld.plugin", "com.atakmap.android.helloworld.DeviceScanActivity"); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra("EXTRA_MESSAGE", ""); parentActivity.startActivityForResult(intent, 0); } });
解决方案
一、修复「could not find callback wrapper」错误
该错误本质是BLE扫描回调未正确绑定到BluetoothLeScanner实例,常见原因及修复:
- 避免回调被GC回收:将
ScanCallback实例声明为Activity或全局成员变量,不要在局部方法中创建后立即失去引用 - 统一管理扫描生命周期:每次调用
startScan后,必须在页面销毁、扫描超时等时机调用stopScan,且使用同一个ScanCallback实例 - 规范Context使用:不要用临时Context注册广播或启动扫描,改用插件全局Context或Activity的生命周期Context
二、解决扫描过频问题
Android系统对BLE扫描有频率限制,需手动添加扫描间隔策略:
- 实现循环扫描机制:设置扫描时长+间隔时长,避免持续扫描触发系统限制
private Handler scanHandler = new Handler(); private static final long SCAN_DURATION = 10000; // 单次扫描10秒 private static final long SCAN_INTERVAL = 3000; // 扫描间隔3秒 private boolean isScanning = false; private void startCyclicScan() { if (isScanning) return; isScanning = true; startScan(); // 启动扫描的核心方法 scanHandler.postDelayed(() -> { stopScan(); // 停止扫描 isScanning = false; scanHandler.postDelayed(this::startCyclicScan, SCAN_INTERVAL); }, SCAN_DURATION); } - 防止重复启动扫描:在按钮点击事件中先判断扫描状态,再执行启动逻辑
connectDevice.setOnClickListener(v -> { if (!isScanning) { startCyclicScan(); } });
三、修复扫描页面闪退问题
页面闪退大概率是广播接收器注册/注销逻辑错误导致:
- 修正广播注销时机:不要在
onReceive中立即注销广播,改为在Activity销毁时统一处理@Override public void onReceive(Context context, Intent intent) { synchronized (this) { try { Bundle extras = intent.getExtras(); if (extras != null) { ArrayList<BluetoothDevice> s = mLeDeviceListAdapter.mLeDevices; if (s != null && sdr != null) sdr.onDeviceScanReceived(s); } } catch (Exception ignored) { } // 移除此处的广播注销代码 } } // 在DeviceScanActivity的onDestroy中统一注销广播和停止扫描 @Override protected void onDestroy() { super.onDestroy(); if (deviceScanListener != null && deviceScanListener.registered) { unregisterReceiver(deviceScanListener); deviceScanListener.registered = false; } stopScan(); scanHandler.removeCallbacksAndMessages(null); } - 规范Activity启动方式:ATAK插件中启动Activity需适配插件生命周期,可改用
parentActivity.startActivity(intent)替代startActivityForResult,或确保插件已申请正确的Activity启动权限
四、其他优化点
- 完善权限配置:在
AndroidManifest.xml中添加BLE相关权限<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <uses-feature android:name="android.hardware.bluetooth_le" android:required="true" /> - 动态申请权限:Android 12+需动态申请
BLUETOOTH_SCAN、BLUETOOTH_CONNECT权限 - 规范适配器数据访问:不要直接访问
mLeDeviceListAdapter的私有成员mLeDevices,改用getItemCount()和getItem()方法获取数据
内容的提问来源于stack exchange,提问作者Jsmnbx
相关产品推荐
相关产品推荐

