Android Beacon Library:如何在不使用内部JobScheduler机制的情况下在后台扫描iBeacon? (我用自己的 WorkManager 代替)

问题描述 投票:0回答:1

大约一周以来,我一直在尝试使用 WorkManager 在后台实现 iBeacon 芯片扫描功能(定期,每 15 分钟一次)。

我在应用程序打开(前台)和关闭(使用 WorkManager 在后台)时实现了 iBeacon 芯片扫描。

当应用程序打开(前台)时,芯片会被快速扫描,大约在 100-1000 毫秒内。

但是,当应用程序关闭时,扫描行为不一致。有时(很少)它可能会非常快地扫描芯片,在 500-1000 毫秒内,但大多数时候需要超过 30-60 秒(相当长)或者可能根本检测不到芯片,即使芯片始终开启。

这是我实现的用于扫描的

Util
代码:

object BeaconScanUtil {
    private lateinit var applicationContext: Context
    private val uuid = Identifier.parse("39ED98FF-2900-441A-802F-9C398FC199D2")
    private var region = Region("all-beacons", uuid, null, null)
    
    var currentBluetoothId: Int? = null

    private val rangingObserver by lazy {
        Observer<Collection<Beacon>> { beacons ->
            val beacon = beacons.firstOrNull()
            if (beacon != null) {
                val majorId = beacon.id2.toInt()
                currentBluetoothId = majorId

                stopScanning()
            }
        }
    }

    // I call this method from the Application.onCreate()
    fun initialize(context: Context) {
        applicationContext = context.applicationContext
        BeaconManager.getInstanceForApplication(applicationContext).apply {
            // Since we don't care about AltBeacon, we clear it from the defaults
            beaconParsers.clear()

            val parser = BeaconParser().apply {
                // Layout for iBeacon
                setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24")
                setHardwareAssistManufacturerCodes(arrayOf(0x004c).toIntArray())
            }
            beaconParsers.add(parser)

            setEnableScheduledScanJobs(false)

            getRegionViewModel(region).rangedBeacons.observeForever(rangingObserver)
        }
    }

    suspend fun scanForBeacons(maxDurationMs: Long, backgroundMode: Boolean): BeaconScanResult? {
        val minDurationMs = 500L // 0.5s
        val intervalMs = 100L // 0.1s
        var durationMs = 0L

        startScanning(backgroundMode)

        try {
            while (durationMs < maxDurationMs) {
                if (durationMs >= minDurationMs) {
                    val currentBluetoothId = currentBluetoothId
                    if (currentBluetoothId != null) {
                        return BeaconScanResult(currentBluetoothId, durationMs)
                    }
                }

                durationMs += intervalMs
                delay(intervalMs)
            }
            val currentBluetoothId = currentBluetoothId
            return if (currentBluetoothId != null) {
                BeaconScanResult(currentBluetoothId, durationMs)
            } else {
                null
            }
        } finally {
            stopScanning()
        }
    }

    private fun startScanning(backgroundMode: Boolean) {
        resetBeaconsInfo()
        val beaconManager = BeaconManager.getInstanceForApplication(applicationContext)
        beaconManager.setBackgroundModeInternal(backgroundMode)
        beaconManager.startRangingBeacons(region)
    }

    private fun stopScanning() {
        val beaconManager = BeaconManager.getInstanceForApplication(applicationContext)
        beaconManager.stopRangingBeacons(region)
    }

    private fun resetBeaconsInfo() {
        currentBluetoothId = null
    }
}

data class BeaconScanResult(val bluetoothId: Int, val timeSpentOnScanMs: Long)

这里是使用WorkManager在后台扫描芯片的代码:

class ScanBeaconWorker(
    context: Context,
    param: WorkerParameters
) : CoroutineWorker(context, param) {
    override suspend fun doWork(): Result {
        return withContext(Dispatchers.IO) {
            val hasAllConditionsToScanForBeacon = ... 

            if (!hasAllConditionsToScanForBeacon) {
                return@withContext Result.failure()
            }

            val timeToFindBeacon = BeaconScanUtil.scanForBeacons(
                maxDurationMs = 60_000L, 
                backgroundMode = true
            )
            showAutoCheckInCheckInNotification(
                context = applicationContext,
                title = "Time: ${timeToFindBeacon?.timeSpentOnScanMs ?: 60_000}",
                description = "Beacon ID: ${timeToFindBeacon?.bluetoothId}"
            )

            return@withContext Result.success()
        }
    }

    private fun showAutoCheckInCheckInNotification(
        context: Context,
        title: String,
        description: String,
    ) {
        // show notification
    }
}
  1. 您能帮我理解这段代码可能有什么问题吗?

  2. startRangingBeacons(region)
    在后台工作吗?或者我应该使用
    startMonitoring(region)
    来代替? (我需要
    major
    信标值)

  3. 或者您能否提供正确的代码示例来扫描信标而不使用内部 JobScadular 机制? (因为我使用自定义 WorkManager 在后台执行扫描)

  4. 或者我应该使用另一种方法在后台扫描信标?

任何帮助将不胜感激。

提前非常感谢您!

我使用这个库: https://altbeacon.github.io/android-beacon-library/index.html

android bluetooth-lowenergy android-bluetooth altbeacon android-ibeacon
1个回答
0
投票

您可以通过调用以下命令来禁用 Android Beacon 库对 Android Job Scheduler 的内部使用:

beaconManager.setEnableScheduledScanJobs(假)

禁用时,该库使用常规 Android 服务来处理扫描。 当允许工作管理器处于活动状态时,操作系统应该允许这样做。

测距和监控在后台运行良好。

显示的代码通过调用库的私有内部方法来打开和关闭库后台模式,这是不推荐的,并且可能会导致问题。 该库在内部管理后台模式,并根据操作系统的需要根据检测到的前台/后台状态调整扫描。 默认情况下,库还会将扫描调整为在后台每隔 5 分钟进行一次。

如果您想直接控制扫描周期,以便根据您的命令立即进行扫描,只需调整库的扫描周期和扫描周期之间即可。 扫描周期定义了库在暂停扫描以节省电池之前花费的扫描时间(以毫秒为单位)。 较长的扫描周期(后台默认为 30000 毫秒)将节省电量,但会延迟检测。 如果工作管理器代码已经以编程方式限制扫描,您可能需要将其设置为 0:

// 默认值如下所示 beaconManager.setScanPeriod(1100) / 毫秒 beaconManager.setBetweenScanPeriod(0) / 毫秒 // 这些是更改而不是默认值 beaconManager.setBackgroundScanPeriod(1100) / 毫秒 beaconManager.setBackgroundBerweenScanPeriod(0) / 毫秒

在开始测距或监控之前调用以上代码。 在测距和监控已经开始后动态更改这些将产生不确定的效果。

了解该库被设计为最容易用于告诉它通过开始测距或监视来开始寻找信标的程序,然后将其保持打开状态。 如果您想在自己的代码中使用它动态启动和停止,这当然是允许的,但它使其不太容易使用。

© www.soinside.com 2019 - 2024. All rights reserved.