Android 中定期 WorkManager 的后台位置获取问题

Background Location Fetching Issue with Periodic WorkManager in Android

提问人:Kalpesh Doru 提问时间:11/10/2023 更新时间:11/10/2023 访问量:23

问:

我目前正在开发一个 Android 应用程序,我需要使用 Periodic WorkManager 在后台定期获取用户的位置。我已将 WorkRequest 设置为每 15 分钟触发一次,当应用程序位于前台时,它会成功检索用户的位置。

但是,当应用程序被终止或在后台时,我遇到了一个问题。尽管授予了所有必需的位置权限,但在这些情况下返回的位置为 null。

权限

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>

接口和实现

interface LocationClient {
    fun getLocationUpdates(context: Context, interval: Long): Flow<Location>
    suspend fun getLocation(context: Context, interval: Long): Location
    class LocationException(message: String) : Exception()
}

class LocationClientImpl @Inject constructor(
    private val client: FusedLocationProviderClient
) : LocationClient {

    @SuppressLint("MissingPermission")
    override fun getLocationUpdates(context: Context, interval: Long): Flow<Location> {
        return callbackFlow {
            if (!context.hasLocationPermission()) {
                throw LocationClient.LocationException("Missing location permission")
            }

            val locationManager =
                context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
            val isGpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
            val isNetworkEnabled =
                locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
            if (!isGpsEnabled && !isNetworkEnabled) {
                throw LocationClient.LocationException(message = "GPS is disabled")
            }

            val request = LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, interval)
                .setWaitForAccurateLocation(false)
                .build()


            val locationCallback = object : LocationCallback() {
                override fun onLocationResult(result: LocationResult) {
                    super.onLocationResult(result)
                    result.locations.lastOrNull()?.let { location ->
                        launch { send(location) }
                    }
                }
            }

            client.requestLocationUpdates(
                request,
                locationCallback,
                Looper.getMainLooper()
            )

            awaitClose {
                client.removeLocationUpdates(locationCallback)
            }
        }
    }

    @SuppressLint("MissingPermission")
    override suspend fun getLocation(context: Context, interval: Long): Location {

        val request = LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, interval)
            .setWaitForAccurateLocation(false)
            .build()

        return suspendCancellableCoroutine { continuation ->
            val locationCallback = object : LocationCallback() {
                override fun onLocationResult(result: LocationResult) {
                    super.onLocationResult(result)
                    result.locations.lastOrNull()?.let { location ->
                        client.removeLocationUpdates(this)
                        continuation.resume(location)
                    }
                }

                override fun onLocationAvailability(availability: LocationAvailability) {
                    super.onLocationAvailability(availability)
                    if (!availability.isLocationAvailable) {
                        client.removeLocationUpdates(this)
                        continuation.resumeWithException(LocationClient.LocationException("Location not available"))
                        Log.e(this::class.java.name, "onLocationAvailability: ${availability.isLocationAvailable}", )
                    }
                }
            }

            client.requestLocationUpdates(request, locationCallback, Looper.getMainLooper())
        }

    }
}

工人

@HiltWorker
class LocationWorker @AssistedInject constructor(
    @Assisted var context: Context,
    @Assisted parameters: WorkerParameters,
    private val deviceRepo: DeviceRepo,
    private val locationClient: LocationClient
) :
    CoroutineWorker(context, parameters) {
    private val TAG = "LocationWorker"
    override suspend fun doWork(): Result = withContext(Dispatchers.IO) {
        //deviceRepo.addDevice(context)
        val result = locationClient.getLocation(context, 1000)
        Log.d(TAG, "doWork: ${result.latitude}")
        Result.success()
    }
}

活动中的工作请求

val constraints = Constraints.Builder()
                        .setRequiredNetworkType(NetworkType.CONNECTED)
                        .build()
                    val workManager = WorkManager.getInstance(this@MainActivity)
                    val request: PeriodicWorkRequest =
                        PeriodicWorkRequestBuilder<LocationWorker>(
                            5,
                            TimeUnit.MINUTES
                        ).setConstraints(constraints).build()
                    workManager.enqueueUniquePeriodicWork(
                        "reminder_notification_work",
                        ExistingPeriodicWorkPolicy.KEEP,
                        request
                    )
                    workManager.getWorkInfosForUniqueWorkLiveData("reminder_notification_work")
                        .observe(lifecycleOwner) { workInfo ->
                            workInfo.forEach {
                                workManagerState.value = it.state.toString()
                            }
                        }
地理位置 后台工作者 Android-WorkManager

评论

0赞 Gabe Sechan 11/10/2023
您是否在运行时请求了后台位置的权限?用户是否授予了它?请记住,如果在较新的弹出窗口中,他们按“仅当应用程序处于活动状态时”,则后台位置将不起作用。
0赞 Kalpesh Doru 11/10/2023
是的,@GabeSechan,它始终被授予

答: 暂无答案