kotlin RequestLocationUpdates(),适用于Android 12 +,前台无位置更新

9vw9lbht  于 2023-03-09  发布在  Kotlin
关注(0)|答案(1)|浏览(156)

我目前正在使用FusedLocationProviderClient开发一个位置功能来跟踪设备位置。当应用程序可见时,跟踪功能在所有Android版本上都按预期工作。但当切换到前台(应用程序不可见)时,Android 12+上不再提供位置功能,我在logcat中遇到以下错误:LocationUpdateReceiver - LocationEngineResult == null。对于运行在Android 12以下的设备,我收到的位置信息比使用LocationRequest设置的maxInterval要少
我不知道我做错了什么,因为我遵循了不同的Android文档关于服务/位置。
我的AndroidManifest是这样的:

...
<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" />
...
   <service
       android:name=".app.services.RecordingService"
       android:foregroundServiceType="location"/>

我的位置存储库,处理跟踪位置:

class PlayServicesDataStore(private val context: Context, private val logger: Logger) : LocationDataStore {

    override val userLocationState: MutableStateFlow<PointZ?> = MutableStateFlow(null)

    private val fusedLocationProviderClient: FusedLocationProviderClient =
        LocationServices.getFusedLocationProviderClient(context)

    private val locationCallback = Callback()
    private inner class Callback : LocationCallback() {
        override fun onLocationResult(locationResult: LocationResult) {
            locationResult.lastLocation?.let { lastLocation ->

                logger.logMessage("New GPS location: $lastLocation")

                /**
                 * We want to keep only location that have an accuracy of [MAX_ACCURACY_METER]
                 */
                if (lastLocation.hasAccuracy() && lastLocation.accuracy <= MAX_ACCURACY_METER) {
                    userLocationState.update {
                        PointZ(lastLocation.latitude, lastLocation.longitude, lastLocation.altitude)
                    }
                }
            }
        }
    }

    

    override fun startListeningLocationUpdates() {
        val locationRequest = LocationRequest.create().apply {
            interval = TimeUnit.SECONDS.toMillis(1)
            fastestInterval = TimeUnit.SECONDS.toMillis(1)
            maxWaitTime = TimeUnit.SECONDS.toMillis(2)
            priority =  Priority.PRIORITY_HIGH_ACCURACY
        }
        try {
            fusedLocationProviderClient
                .requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper())
        } catch (exception: SecurityException) {
            logger.logException(exception, "Missing permission to request Location Updates")
        }
    }

    override fun stopListeningLocationUpdates() {
        try {
            fusedLocationProviderClient.removeLocationUpdates(locationCallback)
        } catch (exception: SecurityException) {
            logger.logException(exception, "Missing permission to remove Location Updates")
        }
    }

    private companion object {
        const val MAX_ACCURACY_METER = 20
    }
}

该处:

...

class RecordingService : LifecycleService(), HasAndroidInjector {

    ....

    private val notificationManager by lazy {
        applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    }

    private var started = false
    private var configurationChange = false
    private var serviceRunningInForeground = false
    private val localBinder = LocalBinder()

    override fun onCreate() {
        AndroidInjection.inject(this)
        super.onCreate()
    }

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        super.onStartCommand(intent, flags, startId)

        // This action comes from our ongoing notification. The user requested to stop updates.
        if (intent?.action == ACTION_STOP_UPDATES) {
            stopListeningLocationUpdates()
            generateRecordingNotification(...)
        }

        if (!started) {
            started = true
            lifecycleScope.launch {
                recordingInteractor
                    .recordingProgressState
                    .collect {
                        updateRecordingNotification(...)
                    }
            }
        }

        // Tells the system not to recreate the service after it's been killed.
        return START_NOT_STICKY
    }

    override fun onBind(intent: Intent): IBinder {
        super.onBind(intent)
        // AppActivity (client) comes into foreground and binds to service, so the service can
        // become a background services.
        stopForeground(STOP_FOREGROUND_REMOVE)
        serviceRunningInForeground = false
        configurationChange = false
        return localBinder
    }

    override fun onRebind(intent: Intent) {

        // AppActivity (client) returns to the foreground and rebinds to service, so the service
        // can become a background services.
        stopForeground(STOP_FOREGROUND_REMOVE)
        serviceRunningInForeground = false
        configurationChange = false
        super.onRebind(intent)
    }

    override fun onUnbind(intent: Intent): Boolean {

        // MainActivity (client) leaves foreground, so service needs to become a foreground service
        // to maintain the 'while-in-use' label.
        // NOTE: If this method is called due to a configuration change in AppActivity,
        // we do nothing.
        if (!configurationChange) {
            val notification = generateRecordingNotification(
                notificationTitle = getString(R.string.trail_recording_live_activity_recording_status_active),
                context = applicationContext,
                paused = false,
                recordingDuration = getDurationText(recordingInteractor.recordingProgressState.value.time),
            )
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                startForeground(NOTIFICATION_ID, notification, FOREGROUND_SERVICE_TYPE_LOCATION)
            } else {
                startForeground(NOTIFICATION_ID, notification)
            }
            serviceRunningInForeground = true
        }

        // Ensures onRebind() is called if AppActivity (client) rebinds.
        return true
    }

    override fun onConfigurationChanged(newConfig: Configuration) {
        super.onConfigurationChanged(newConfig)
        configurationChange = true
    }

    fun startListeningLocationUpdates() {
        // Binding to this service doesn't actually trigger onStartCommand(). That is needed to
        // ensure this Service can be promoted to a foreground service, i.e., the service needs to
        // be officially started (which we do here).
        startService(Intent(applicationContext, RecordingService::class.java))
        locationRepository.startListeningLocationUpdates()
    }

    fun stopListeningLocationUpdates() {
        stopSelf()
        locationRepository.stopListeningLocationUpdates()
    }

    /**
     * Class used for the client Binder. Since this service runs in the same process as its
     * clients, we don't need to deal with IPC.
     */
    internal inner class LocalBinder : Binder() {
        fun getService(): RecordingService = this@RecordingService
    }
}

不知道我错过了什么,使它正常工作,任何帮助将不胜感激,谢谢!

0pizxfdo

0pizxfdo1#

如果您的应用面向较新的Android版本,则必须确保声明后台权限。

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

并确保用户赠款应用程序后台权限。根据用户的Android版本,您必须将其发送到应用程序的设置中才能实现这一点。有关详细信息,请参阅请求后台位置权限的官方文档。

相关问题