我正在尝试构建一个使用后台位置跟踪的应用程序。我需要每20分钟获取一次设备位置,并将其发送到后端服务器。我做了一个简单的服务:
class GeolocationService : Service() {
companion object {
val TAG: String = GeolocationService::class.java.simpleName
val INTERVAL: Long = 1000 * 60 * 5
var notificationId: Int = 100
fun isServiceRunning(): Boolean {
val activityManager = UpMenuApplication.appContext.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
for (service in activityManager.getRunningServices(Int.MAX_VALUE)) {
if (GeolocationService::class.java.name == service.service.className) {
Log.d(TAG, service.service.className)
Log.d(TAG, GeolocationService::class.java.name)
return true
}
}
return false
}
}
@Inject
lateinit var geolocationRepository: GeolocationRepository
private lateinit var providerClient: FusedLocationProviderClient
private lateinit var locationRequest: LocationRequest
private lateinit var mGoogleApiClient: GoogleApiClient
private var locationCallback: LocationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult?) {
if (locationResult == null) {
Log.d(TAG, "locationCallback - locationResult is null!")
providerClient.removeLocationUpdates(this)
return
}
for (location in locationResult.locations) {
val latitude = location?.latitude ?: 0.0
val longitude = location?.longitude ?: 0.0
Log.d(TAG, "Location: $latitude, $longitude")
Log.d(TAG, "Token: ${UserDataManager.SaveSharedPreference.getApplicationToken(baseContext)?:""}")
providerClient.removeLocationUpdates(this)
//showNotification(latitude, longitude, UserDataManager.SaveSharedPreference.getApplicationToken(baseContext)?:"")
updateCustomerPositionOnServer(latitude, longitude, UserDataManager.SaveSharedPreference.getApplicationToken(baseContext)?:"")
}
}
}
override fun onBind(intent: Intent?): IBinder {
return null!!
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId)
Log.d(TAG, "onStartCommand")
requestLocation()
return START_STICKY
}
override fun onCreate() {
super.onCreate()
Log.d(TAG, "onCreate")
mGoogleApiClient = GoogleApiClient.Builder(this)
.addApi(LocationServices.API)
.addOnConnectionFailedListener {
Log.d(TAG, "onConnectionFailed")
}
.addConnectionCallbacks(object : GoogleApiClient.ConnectionCallbacks {
override fun onConnectionSuspended(p0: Int) {
Log.d(TAG, "onConnectionSuspended")
}
override fun onConnected(p0: Bundle?) {
Log.d(TAG, "onConnected")
}
})
.build()
mGoogleApiClient.connect()
(application as UpMenuApplication).appComponent.inject(this)
}
override fun onDestroy() {
super.onDestroy()
Log.d(TAG, "onDestroy")
}
private fun requestLocation() {
providerClient = LocationServices.getFusedLocationProviderClient(UpMenuApplication.appContext)
locationRequest = createLocationRequest()
try {
val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
if (!locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
providerClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper())
} else {
providerClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper())
}
} catch (e: SecurityException) {
e.printStackTrace()
}
}
private fun updateCustomerPositionOnServer(lat: Double, lng: Double, token: String) {
val location = object {
var latitude: Double = lat
var longitude: Double = lng
}
geolocationRepository.updateCustomerPosition(token, location).enqueue(object: Callback<Void> {
override fun onFailure(call: Call<Void>?, t: Throwable?) {
Log.d(TAG, "updateCustomerPosition failed")
}
override fun onResponse(call: Call<Void>?, response: Response<Void>?) {
Log.d(TAG, "updateCustomerPosition success")
}
})
}
private fun showNotification(lat: Double, lng: Double, token: String) {
val intent = Intent(baseContext, GeolocationService::class.java)
val pIntent = PendingIntent.getActivity(baseContext, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT)
val n = NotificationCompat.Builder(baseContext, "GeolocationService channel")
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(GeolocationService::class.java.simpleName)
.setContentText("${LocalDateTime.now()} - lat: $lat, lng: $lng")
.setStyle(NotificationCompat.BigTextStyle().bigText("${LocalDateTime.now()} - lat: $lat, lng: $lng. Token: $token"))
.setContentIntent(pIntent)
.setVibrate(longArrayOf(1000, 1000, 1000))
.setLights(Color.RED, 3000, 3000)
.setSound(Settings.System.DEFAULT_NOTIFICATION_URI)
.setAutoCancel(true)
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.notify(notificationId++, n.build())
}
private fun createLocationRequest(): LocationRequest {
val locationRequest = LocationRequest()
locationRequest.interval = 1500
locationRequest.fastestInterval = 1500
locationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
return locationRequest
}
}
我这样开始这项服务:
private fun checkAndStartGeolocation() {
if (!GeolocationService.isServiceRunning()) {
Log.d("GeolocationService", "Started all services")
val intent = Intent(this, GeolocationService::class.java)
val pendingIntent = PendingIntent.getService(this, 0, intent, 0)
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), GeolocationService.INTERVAL, pendingIntent)
}
}
服务每20分钟运行一次(虽然不完全准确,但准确度已足够),并且设备位置已正确获取且网络正常运行。当手机被锁定时,问题就开始了,因为在每个HTTP请求中,我都会收到此错误:
java.net.UnknowHostException:无法解析主机“ api.somehost.com”:没有与主机名关联的地址
在不唤醒设备的情况下是否可以通过后台服务联网?