You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ATAK插件BLE扫描报错:扫描过频及找不到回调包装器求助

解决ATAK插件BLE扫描问题:扫描过频、回调包装器缺失及页面闪退

问题现状

参考官方BLE Gatt示例代码为ATAK插件(com.atakmap.android.helloworld.plugin)添加BLE功能,已更新所有过时方法,通过DeviceScanActivity的BroadcastReceiver与主程序做Intent通信,但存在以下问题:

  • BLE设备页面偶尔显示少量设备,多数时候为空
  • 频繁触发「扫描过频」和「could not find callback wrapper」错误
  • 无法在示例核心函数中找到扫描频率控制逻辑
  • 已有Flutter应用可正常扫描周边BLE设备,但当前插件的扫描页面弹出几秒后即消失

核心疑问

  1. 「could not find callback wrapper」错误的具体含义是什么?
  2. 如何修复扫描过频的问题?
  3. 怎么解决扫描页面闪退的问题?

相关代码片段

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扫描有频率限制,需手动添加扫描间隔策略:

  1. 实现循环扫描机制:设置扫描时长+间隔时长,避免持续扫描触发系统限制
    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);
    }
    
  2. 防止重复启动扫描:在按钮点击事件中先判断扫描状态,再执行启动逻辑
    connectDevice.setOnClickListener(v -> {
        if (!isScanning) {
            startCyclicScan();
        }
    });
    

三、修复扫描页面闪退问题

页面闪退大概率是广播接收器注册/注销逻辑错误导致:

  1. 修正广播注销时机:不要在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);
    }
    
  2. 规范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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 19:44:57