我想创建一个可以在后台更新位置的Android应用程序.我翻阅了文档、文章和教程,但没有达到我的目标.当应用程序最小化时,它会暂停更新并再次启动它们.我返回到它.
我的 list 文件:
<service android:name=".LocationService" android:foregroundServiceType="location" />
...
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
我的活动代码启动定位服务:
val serviceIntent = Intent(applicationContext, LocationService::class.java).apply {
action = LocationService.ACTION_START
startService(this)
}
applicationContext.startService(serviceIntent)
我的定位服务:
import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.util.Log
import com.google.android.gms.location.LocationServices
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
class LocationService: Service() {
private val serviceScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
private lateinit var locationClient: LocationClient
override fun onBind(p0: Intent?): IBinder? {
return null
}
override fun onCreate() {
super.onCreate()
locationClient = LocationClient(
applicationContext,
LocationServices.getFusedLocationProviderClient(applicationContext)
)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
when(intent?.action) {
ACTION_START -> start()
ACTION_STOP -> stop()
ACTION_PAUSE -> pause()
ACTION_REFRESH -> refresh()
}
return super.onStartCommand(intent, flags, startId)
}
override fun onDestroy() {
super.onDestroy()
serviceScope.cancel()
}
private fun start() {
locationClient
.getLocationUpdates(1000L)
.catch { e -> e.printStackTrace() }
.onEach { location ->
Log.i("LOCATION", DistanceTracker.getInstance().fullDistance.toString())
DistanceTracker.getInstance().addDistance(location)
}
.launchIn(serviceScope)
}
private fun stop() {
stopSelf()
DistanceTracker.getInstance().resetDistances()
refresh()
}
private fun pause() {
stopSelf()
DistanceTracker.getInstance().clearLocation()
refresh()
}
companion object {
const val ACTION_START = "ACTION_START"
const val ACTION_STOP = "ACTION_STOP"
const val ACTION_PAUSE = "ACTION_PAUSE"
const val ACTION_REFRESH = "ACTION_REFRESH"
}
}
最后是我的定位客户:
import android.annotation.SuppressLint
import android.content.Context
import android.location.Location
import android.location.LocationManager
import android.os.Looper
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationCallback
import com.google.android.gms.location.LocationRequest
import com.google.android.gms.location.LocationRequest.PRIORITY_HIGH_ACCURACY
import com.google.android.gms.location.LocationResult
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.launch
class LocationClient(
private val context: Context,
private val client: FusedLocationProviderClient
): ILocationClient {
@SuppressLint("MissingPermission")
override fun getLocationUpdates(interval: Long): Flow<Location> {
val locationFlow = callbackFlow {
if(!context.hasLocationPermission()) {
throw ILocationClient.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 ILocationClient.LocationException("GPS is disabled")
}
val request = LocationRequest.create()
.setInterval(interval)
.setFastestInterval(interval)
.setPriority(PRIORITY_HIGH_ACCURACY)
val locationCallback = object : LocationCallback() {
override fun onLocationResult(result: LocationResult) {
val location = result.locations.minWith(Comparator.comparingDouble{it.accuracy as Double})
if(location.accuracy < 50) {
super.onLocationResult(result)
result.locations.lastOrNull()?.let { location ->
launch { send(location) }
}
}
}
}
client.requestLocationUpdates(
request, locationCallback, Looper.getMainLooper())
awaitClose {
client.removeLocationUpdates(locationCallback)
}
}
return locationFlow
}
}
我遗漏了什么?我想要跟踪我已经覆盖的距离,而不需要将我的应用程序永久放在前台.在这种情况下,真的很不方便.