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

Android多状态下AltBeacon检测失效问题排查与方案咨询

问题排查建议(针对AltBeacon方案失效)

1. 解决扫描频率超限问题

日志中的App 'com.testble.mobile' is scanning too frequently是核心诱因,Android 12+对后台BLE扫描频率有严格限制,应用进程杀死后系统会进一步收紧管控。调整AltBeacon的扫描周期参数,降低后台扫描频率:

// 前台扫描配置(应用可见时):扫描1秒,间隔1秒
beaconManager.setForegroundScanPeriod(1000)
beaconManager.setForegroundBetweenScanPeriod(1000)

// 后台/进程杀死后扫描配置:扫描1秒,间隔60秒(避免触发系统限制)
beaconManager.setBackgroundScanPeriod(1000)
beaconManager.setBackgroundBetweenScanPeriod(60000)

注意:后台扫描间隔建议设置为30秒以上,过短会被系统判定为过度扫描而禁止。

2. 验证IntentScanningStrategy配置完整性

确保AndroidManifest.xml中注册了AltBeacon所需的广播接收器:

<receiver android:name="org.altbeacon.beacon.BroadcastReceiver"
          android:exported="false">
    <intent-filter>
        <action android:name="org.altbeacon.beacon.service.SCAN_SCHEDULER"/>
        <action android:name="org.altbeacon.beacon.service.BEACON_MONITORING_NOTIFICATION"/>
        <action android:name="org.altbeacon.beacon.service.BEACON_RANGING_NOTIFICATION"/>
    </intent-filter>
</receiver>

同时开启后台服务适配:

beaconManager.enableForegroundServiceScanning(null, 0)

3. 检查电池优化与权限状态

  • 将应用从系统电池优化列表中排除:设置 > 电池 > 电池优化 > 目标应用 > 选择不优化
  • 确认位置权限为「始终允许」:Android 12+中,应用进程杀死后扫描位置相关的BLE信标,必须持有ACCESS_FINE_LOCATION的「始终允许」权限,否则系统会禁止后台扫描。

4. 验证Region配置正确性

确保region对象的UUID、Major、Minor参数与目标Beacon完全匹配,避免因Region不匹配导致检测失效:

val region = Region("unique-region-id", Identifier.parse("YOUR_BEACON_UUID"), null, null)

无需「始终允许」位置权限的替代实现方案

如果无法确保用户授予「始终允许」位置权限,可采用以下两种方案:

方案1:前台服务维持BLE扫描

利用前台服务将应用维持在前台状态,此时仅需「允许在使用中」的位置权限即可持续扫描Beacon:

  1. 创建前台服务并实现BLE扫描逻辑:
class BeaconScanService : Service() {
    private lateinit var bluetoothAdapter: BluetoothAdapter
    private lateinit var scanCallback: ScanCallback

    override fun onCreate() {
        super.onCreate()
        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
        scanCallback = object : ScanCallback() {
            override fun onScanResult(callbackType: Int, result: ScanResult) {
                super.onScanResult(callbackType, result)
                parseIBeacon(result)?.let { beacon ->
                    sendNotification("检测到Beacon:${beacon.uuid}")
                }
            }
        }
        // 启动前台服务,显示常驻通知
        val notification = NotificationCompat.Builder(this, "beacon_scan_channel")
            .setContentTitle("Beacon扫描中")
            .setSmallIcon(R.drawable.ic_scan)
            .build()
        startForeground(1, notification)
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        startScan()
        return START_STICKY
    }

    private fun startScan() {
        if (bluetoothAdapter.isEnabled) {
            val scanSettings = ScanSettings.Builder()
                .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
                .build()
            val filters = listOf(ScanFilter.Builder()
                .setManufacturerData(0x004c, byteArrayOf(0x02, 0x15)) // iBeacon厂商数据前缀
                .build())
            bluetoothAdapter.bluetoothLeScanner.startScan(filters, scanSettings, scanCallback)
        }
        // 定时重启扫描,避免持续扫描消耗电量
        Handler(Looper.getMainLooper()).postDelayed({
            bluetoothAdapter.bluetoothLeScanner.stopScan(scanCallback)
            startScan()
        }, 60000)
    }

    private fun parseIBeacon(result: ScanResult): BeaconData? {
        val manufacturerData = result.scanRecord?.manufacturerSpecificData?.get(0x004c) ?: return null
        if (manufacturerData.size < 25) return null
        val uuid = UUID.nameUUIDFromBytes(manufacturerData.copyOfRange(2, 18))
        val major = ByteBuffer.wrap(manufacturerData.copyOfRange(18, 20)).order(ByteOrder.BIG_ENDIAN).short.toInt()
        val minor = ByteBuffer.wrap(manufacturerData.copyOfRange(20, 22)).order(ByteOrder.BIG_ENDIAN).short.toInt()
        return BeaconData(uuid, major, minor)
    }

    data class BeaconData(val uuid: UUID, val major: Int, val minor: Int)

    override fun onBind(intent: Intent?): IBinder? = null
}
  1. 在Manifest中注册服务并声明权限:
<service android:name=".BeaconScanService"
         android:foregroundServiceType="location"/>

<uses-permission android:name="android.permission.BLUETOOTH_SCAN"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION"/>
  1. 启动前台服务:
startService(Intent(this, BeaconScanService::class.java))

方案2:WorkManager实现周期性扫描

通过WorkManager定期触发BLE扫描,无需前台通知,但扫描频率受系统限制(建议15分钟以上一次):

  1. 创建Worker类:
class BeaconScanWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
    private lateinit var bluetoothAdapter: BluetoothAdapter
    private lateinit var scanCallback: ScanCallback
    private val scanDuration = 10000L // 每次扫描10秒

    override fun doWork(): Result {
        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
        if (!bluetoothAdapter.isEnabled) return Result.success()

        scanCallback = object : ScanCallback() {
            override fun onScanResult(callbackType: Int, result: ScanResult) {
                super.onScanResult(callbackType, result)
                parseIBeacon(result)?.let { beacon ->
                    sendNotification(applicationContext, "检测到Beacon:${beacon.uuid}")
                }
            }
        }

        val scanSettings = ScanSettings.Builder()
            .setScanMode(ScanSettings.SCAN_MODE_BALANCED)
            .build()
        val filters = listOf(ScanFilter.Builder()
            .setManufacturerData(0x004c, byteArrayOf(0x02, 0x15))
            .build())
        bluetoothAdapter.bluetoothLeScanner.startScan(filters, scanSettings, scanCallback)

        Thread.sleep(scanDuration)
        bluetoothAdapter.bluetoothLeScanner.stopScan(scanCallback)

        return Result.success()
    }

    private fun parseIBeacon(result: ScanResult): BeaconScanService.BeaconData? {
        val manufacturerData = result.scanRecord?.manufacturerSpecificData?.get(0x004c) ?: return null
        if (manufacturerData.size < 25) return null
        val uuid = UUID.nameUUIDFromBytes(manufacturerData.copyOfRange(2, 18))
        val major = ByteBuffer.wrap(manufacturerData.copyOfRange(18, 20)).order(ByteOrder.BIG_ENDIAN).short.toInt()
        val minor = ByteBuffer.wrap(manufacturerData.copyOfRange(20, 22)).order(ByteOrder.BIG_ENDIAN).short.toInt()
        return BeaconScanService.BeaconData(uuid, major, minor)
    }
}
  1. 配置并启动周期性任务:
val scanRequest = PeriodicWorkRequestBuilder<BeaconScanWorker>(15, TimeUnit.MINUTES)
    .setConstraints(Constraints.Builder()
        .setRequiredNetworkType(NetworkType.NOT_REQUIRED)
        .setRequiresBatteryNotLow(true)
        .build())
    .build()
WorkManager.getInstance(this).enqueueUniquePeriodicWork("beacon_scan", ExistingPeriodicWorkPolicy.REPLACE, scanRequest)
  1. 权限配置:
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

注意:此方案无法实时检测Beacon的进入/离开事件,仅适合周期性巡检场景。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 15:37:33