I need to access location in the background when my app is running.
I successfully received location updates when my main activity implemented android.location.LocationListener
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
val criteria = Criteria()
val provider = locationManager.getBestProvider(criteria, false)
val location = locationManager.getLastKnownLocation(provider)
locationManager.requestLocationUpdates(provider, 5000, 20f, this)
setLocation(location)
} else {
/* ... */
}
However I attempted to add this code to a new service class (implementing both android.app.Service
and android.location.LocationListener
), and the onLocationChanged
function is only firing when the app is in view.
I believe this problem could be one of the following:
I think I read somewhere that I have to manually create a new thread inside the service so that it does not run on the same thread as the main activity. This could be the issue, but I haven't a clue how to do this. I'm new to android development and Kotlin.
I think I read somewhere that the location permissions I currently request aren't enough when accessing location in the background, but can't find out what to change.
I saw a solution somewhere where they create a timer that runs every few seconds and manually requests the last location from the location manager. I haven't tested this, but if it does work it doesn't satisfy my needs. The requestLocationUpdates
allows me to configure both a minimum time and minimum distance. Of course I could manually add in a distance check, but this could affect battery life and performance.
How do I fix this problem?
(I can probably change any Java code to Kotlin if anyone is not familiar with Kotlin)
Edit 1
My service is definitely running, as the location updates are received in the foreground
Here is the onStartCommand
code for my service:
override fun onStartCommand(intent: Intent, flags: Int, startid: Int): Int {
/* request location updates as above */
return START_STICKY
}
val serviceIntent = Intent(applicationContext, OnTheFlyLandmarkService::class.java)
startService(serviceIntent)
Edit 2
I also read that I need to display a notification to let users know that my service is running in the background. I tried the following code but this did not help:
val notification = NotificationCompat.Builder(this, CHANNEL_ID).setSmallIcon(R.drawable.messageicon).setContentTitle("My app").setContentText("Accessing location in background...").setAutoCancel(true).setPriority(NotificationCompat.PRIORITY_DEFAULT).build() with(NotificationManagerCompat.from(this)) { notify(id, notification) }
Edit 3
I tried what @Michael said: call startForeground
inside the service and pass in the notification. This did not work, and I didn't even see a notification.
override fun onStartCommand(intent: Intent, flags: Int, startid: Int): Int {
val notification = buildNotification("My app", "Accessing location in background...")
startForeground(NOTIFICATION_ID_SERVICE, notification)
/* request location updates */
return START_STICKY
}
For using a Service
that always runs you have some important things:
<manifest ... >
...
<application ... >
<service android:name=".ExampleService" />
...
</application>
</manifest>
START_STICKY
so as the system to restart it, in case it kills it. override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show()
// ...
// If we get killed, after returning from here, restart
return START_STICKY
}
SecurityException
. Plus the permission in your manifest<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
You already have the 1 and 2. So you have to do the 3.
Use a foreground notification in your Service OnCreate
method.
val pendingIntent: PendingIntent =
Intent(this, ExampleActivity::class.java).let { notificationIntent ->
PendingIntent.getActivity(this, 0, notificationIntent, 0)
}
val notification: Notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.messageicon)
.setContentTitle("My app")
.setContentText("Accessing location in background...")
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.build()
startForeground(ONGOING_NOTIFICATION_ID, notification)
// Caution: The integer ID that you give to startForeground() must not be 0.
Check for more details the official documentation:
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.