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

Android 13后台BLE扫描失效问题求助(对比Android 9正常)

问题:Android 13后台BLE扫描无设备返回(Android 9正常)

我使用Jetpack WorkManager调度BLEScanWorker类,每15分钟在后台扫描BLE设备10秒并将设备存入列表。该逻辑在Android 9三星平板上,无论应用前台、后台、关闭甚至重启后都能正常返回所有BLE设备;但在Android 13三星S22手机上,仅应用前台时能正常返回设备,应用隐藏或关闭时扫描启动停止正常但无任何设备返回。

build.gradle中compileSdkVersion为33,已授予ACCESS_FINE_LOCATION、BLUETOOTH_SCAN和BLUETOOTH_ADMIN权限。

调度Worker代码

Constraints constraints = new Constraints.Builder()
                        .setRequiresCharging(false)
                        .setRequiresDeviceIdle(false)
                        .setRequiresBatteryNotLow(true)
                        .setRequiredNetworkType(NetworkType.NOT_REQUIRED)
                        .build();

PeriodicWorkRequest periodicWorkRequest = new PeriodicWorkRequest.Builder(
                        BLEScanWorker.class, 15, TimeUnit.MINUTES)
                        .setConstraints(constraints)
                        .build();

mWorkManager.enqueueUniquePeriodicWork(
                        SCAN_WORK_NAME,
                        ExistingPeriodicWorkPolicy.KEEP, //Existing Periodic Work policy
                        periodicWorkRequest //work request
                );

BLEScanWorker类代码

public class BLEScanWorker extends Worker {

     private static final String TAG = BLEScanWorker.class.getName();
     private static final long SCAN_PERIOD = 10000;
     List<BluetoothDevice> mBLEDeviceList= new ArrayList<>();
     private Context mContext;

     public BLEScanWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);

        mContext = context;
    }

    private ScanCallback mScanCallback = new ScanCallback() {

        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            super.onScanResult(callbackType, result);

            // save BLE device returned by result.getDevice() in mBLEDeviceList
            
        }

        @Override
        public void onBatchScanResults(List<ScanResult> results) {
            super.onBatchScanResults(results);
            for (ScanResult result : results) {
               // save BLE device returned by result.getDevice() in mBLEDeviceList
            }
        }

        @Override
        public void onScanFailed(int errorCode) {
            super.onScanFailed(errorCode);
            Log.e("onScanFailed", "Error code = " + errorCode);
        }
    };

    @NonNull
    @Override
    public Result doWork() {

        final BluetoothLeScanner bluetoothLeScanner;
        final ScanSettings settings;
        final List<ScanFilter> filters;

        BluetoothAdapter bluetoothAdapter;

        // It has been declared BLE required in the manifests.
        BluetoothManager bluetoothManager = (BluetoothManager) mContext.getSystemService (Context.BLUETOOTH_SERVICE);         
        if (bluetoothManager != null)
            bluetoothAdapter = bluetoothManager.getAdapter();
        else {
            Log.e(TAG, "failed to get BluetoothManager");
            return Result.failure();
        }

        try {
            bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
            settings = new ScanSettings.Builder()
                        .setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
                        .build();
            filters = new ArrayList<ScanFilter>();
        } catch (SecurityException se) {
            Log.e(TAG, se.getMessage());
            return Result.failure();
        }

        try {
            Log.d(TAG, Calendar.getInstance().getTime() + " " + "doWork Called");

            mBLEDeviceList.clear();

            try {
                bluetoothLeScanner.startScan(filters, settings, mScanCallback);
            } catch (SecurityException se) {
                Log.e(TAG, se.getMessage());
            }
            Log.i(TAG, Calendar.getInstance().getTime() + " start scan");

            Thread.sleep(SCAN_PERIOD); // sleep for 10 sec while scanning

            Log.i(TAG, Calendar.getInstance().getTime() + " stop scan");

            try {
                bluetoothLeScanner.stopScan(mScanCallback);
            } catch (SecurityException se) {
                Log.e(TAG, se.getMessage());
            }

            if (!mBLEDeviceList.isEmpty()) {
               // do something if the BLE device list is not empty
            }

            Log.i(TAG, "doWork finished");
            return Result.success();

        }
        catch (Throwable throwable)
        {
            Log.d(TAG, "Error Sending Notification " + throwable.getMessage());
            return Result.failure();
        }
    }
}

问题原因分析

  • Android 13后台BLE扫描权限限制:API 33新增了BLUETOOTH_SCAN的后台权限控制,仅申请前台权限时,应用后台无法获取扫描结果。当前权限配置未覆盖后台场景。
  • 扫描模式适配问题:ScanSettings.SCAN_MODE_LOW_POWER在Android 13后台环境中,系统会大幅限制扫描频率和结果返回,导致无法发现设备。
  • 后台任务优先级限制:Android 13对后台任务调度的管控更严格,WorkManager周期性任务在后台执行时优先级降低,BLE扫描资源被系统限制。

解决方案

1. 补充后台BLE扫描权限

在AndroidManifest.xml中添加后台权限声明:

<!-- Android 13及以上的后台BLE扫描权限 -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="always" />
<!-- 兼容低版本的权限声明 -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:maxSdkVersion="32" />

同时在代码中动态申请后台权限:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
    String[] permissions = {
        Manifest.permission.BLUETOOTH_SCAN,
        Manifest.permission.BLUETOOTH_SCAN + ".BACKGROUND"
    };
    if (ContextCompat.checkSelfPermission(mContext, permissions[0]) != PackageManager.PERMISSION_GRANTED ||
        ContextCompat.checkSelfPermission(mContext, permissions[1]) != PackageManager.PERMISSION_GRANTED) {
        ActivityResultLauncher<String[]> launcher = 
            registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), result -> {
                boolean scanGranted = result.getOrDefault(permissions[0], false);
                boolean scanBgGranted = result.getOrDefault(permissions[1], false);
                if (!scanGranted || !scanBgGranted) {
                    // 处理权限申请失败逻辑
                }
            });
        launcher.launch(permissions);
    }
}

2. 调整扫描模式

将扫描模式改为SCAN_MODE_LOW_LATENCY,提升后台扫描的设备发现概率:

settings = new ScanSettings.Builder()
            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
            .build();

3. 将Worker升级为前台服务

通过ForegroundInfo把Worker标记为前台任务,避免系统限制后台资源:

@NonNull
@Override
public Result doWork() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        // 创建通知渠道
        NotificationChannel channel = new NotificationChannel(
            "BLE_SCAN_CHANNEL", 
            "BLE扫描服务", 
            NotificationManager.IMPORTANCE_LOW
        );
        NotificationManager manager = (NotificationManager) getApplicationContext()
            .getSystemService(Context.NOTIFICATION_SERVICE);
        manager.createNotificationChannel(channel);
        
        // 创建前台通知
        Notification notification = new NotificationCompat.Builder(getApplicationContext(), "BLE_SCAN_CHANNEL")
            .setContentTitle("正在扫描BLE设备")
            .setSmallIcon(R.drawable.ic_scan)
            .build();
        
        // 设置前台服务
        setForegroundAsync(new ForegroundInfo(1001, notification));
    }
    
    // 原有扫描逻辑...
}

4. 检查位置后台权限(若需要)

如果扫描依赖位置信息,需补充ACCESS_BACKGROUND_LOCATION权限:

<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

动态申请时需将该权限加入申请列表。


内容的提问来源于stack exchange,提问作者user22020238

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 13:30:34