You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android 8.1.0屏幕关闭时BLE扫描停止问题求助

解决Android 8.1.0熄屏后BLE扫描停止的问题

我遇到过类似的场景,在Android 8.0+系统中,熄屏后应用会进入后台状态,系统为了节省功耗,会对后台应用的BLE扫描进行严格限制,这就是你遇到扫描停止问题的核心原因。结合你的代码,我整理了几个针对性的解决方案:

1. 切换到后台兼容的扫描模式

你当前使用的ScanSettings.SCAN_MODE_LOW_LATENCY是专为前台活跃场景设计的高功耗模式,当应用进入后台后,系统会强制降低扫描优先级甚至直接停止扫描。建议切换为后台友好的扫描模式:

修改你的ScanSettings构建代码:

final ScanSettings settings = new ScanSettings.Builder()
        .setScanMode(ScanSettings.SCAN_MODE_LOW_POWER) // 改用低功耗后台模式,平衡功耗与扫描频率
        .setReportDelay(0)
        .setUseHardwareBatchingIfSupported(false)
        .setUseHardwareFilteringIfSupported(false)
        .build();

如果需要更均衡的扫描频率,也可以选择ScanSettings.SCAN_MODE_BALANCED

2. 使用前台服务维持扫描(关键解决方案)

Android 8.0(API 26)开始,后台应用的位置权限和BLE扫描会被系统严格限制,而前台服务被系统认定为活跃进程,不会受到这些限制。你需要将BLE扫描逻辑迁移到前台服务中:

步骤1:实现前台服务类

public class BLEScanService extends Service {
    private static final int SCAN_NOTIFICATION_ID = 1001;
    private BluetoothLeScannerCompat scanner;
    private boolean mScanning = false;

    @Override
    public void onCreate() {
        super.onCreate();
        scanner = BluetoothLeScannerCompat.getScanner();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // 启动前台通知(Android 8.0+强制要求)
        Notification scanNotification = createScanNotification();
        startForeground(SCAN_NOTIFICATION_ID, scanNotification);
        
        // 启动BLE扫描
        startBLEScan();
        return START_STICKY; // 服务被杀死后尝试重启
    }

    private void startBLEScan() {
        if (!mScanning) {
            ScanSettings settings = new ScanSettings.Builder()
                    .setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
                    .setReportDelay(0)
                    .setUseHardwareBatchingIfSupported(false)
                    .setUseHardwareFilteringIfSupported(false)
                    .build();
            ScanFilter.Builder filterBuilder = new ScanFilter.Builder();
            filterBuilder.setServiceUuid(new ParcelUuid(THINGY_BASE_UUID));
            ScanFilter filter = filterBuilder.build();
            List<ScanFilter> filters = new ArrayList<>();
            filters.add(filter);
            
            scanner.startScan(filters, settings, scanCallback);
            mScanning = true;
        }
    }

    private final ScanCallback scanCallback = new ScanCallback() {
        @Override
        public void onScanResult(final int callbackType, final ScanResult result) {
            Log.e("BLEScanService", "扫描到设备: " + result.getDevice().getName());
            // 可通过广播、LiveData等方式将结果传递给Activity
        }

        @Override
        public void onBatchScanResults(final List<ScanResult> results) {
            Log.e("BLEScanService", "批量扫描结果数量: " + results.size());
        }

        @Override
        public void onScanFailed(final int errorCode) {
            Log.e("BLEScanService", "扫描失败,错误码: " + errorCode);
        }
    };

    private Notification createScanNotification() {
        // 适配Android 8.0+的通知渠道
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(
                    "BLE_SCAN_CHANNEL",
                    "BLE扫描服务",
                    NotificationManager.IMPORTANCE_LOW
            );
            NotificationManager notificationManager = getSystemService(NotificationManager.class);
            notificationManager.createNotificationChannel(channel);
        }

        return new NotificationCompat.Builder(this, "BLE_SCAN_CHANNEL")
                .setContentTitle("BLE扫描中")
                .setContentText("正在搜索Thingy设备")
                .setSmallIcon(R.drawable.ic_bluetooth)
                .setPriority(NotificationCompat.PRIORITY_LOW)
                .build();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // 停止扫描,释放资源
        if (mScanning) {
            scanner.stopScan(scanCallback);
            mScanning = false;
        }
    }
}

步骤2:在Manifest中注册服务

<service 
    android:name=".BLEScanService"
    android:foregroundServiceType="location" /> <!-- Android 12+需指定前台服务类型 -->

步骤3:从Activity启动前台服务

替换你原来的startScan()方法,改为启动前台服务:

void startScan() {
    Intent scanServiceIntent = new Intent(this, BLEScanService.class);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        startForegroundService(scanServiceIntent);
    } else {
        startService(scanServiceIntent);
    }
}

3. 确保位置权限的后台有效性

虽然Android 8.1没有引入ACCESS_BACKGROUND_LOCATION权限,但你需要确保用户已授予ACCESS_COARSE_LOCATIONACCESS_FINE_LOCATION权限,并且权限设置为允许所有时间(部分厂商系统会提供这个选项)。如果用户仅授予"仅在使用时允许",熄屏后应用进入后台,位置权限会被系统收回,导致扫描停止。


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

火山引擎 最新活动