Foreground Service in Android
For this article we will see the setup for the foreground services in Android and specific we will see the setup for the foreground service that requested for the user background location.
First of all, we have to declare the foreground permissions in the Manifest file, the mandatory foreground service permission and the permission(s) depend on the foreground service type. For our case, we have a service that requests the location, so, declared the follow location permission in the Manifest file.
<!--Mandatory permission independently with type-->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!--Permission depend of the foreground type-->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
But if we have a service for phone call then we have to declare the follow permission in the Manifest file.
<!--Mandatory permission independently with type-->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!--Permission depend of the foreground type-->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL" />
The Manifest file in our sample project:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<!--Location Permissions-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<!--Foreground Service Permissions-->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
<application
<!--Other Code Here-->
</application>
</manifest>
Second, Create the service, in our case is the LocationService class, request for background location.
Important note: when you start a foreground service, you have to create a notification that will notify the user that the service is running in the background.
class LocationService : Service(), LocationListener {
companion object {
private const val locationChannelId = "locationChannelId"
private const val channelName = "locationName"
private const val minTimeLocationUpdateInMillisecond = 10000L
private const val minDistanceLocationUpdateInMeter = 1000F
}
override fun onCreate() {
notificationService()
super.onCreate()
}
/**
* The Notification is mandatory for background services
* */
private fun notificationService() {
Notification.Builder(this, locationChannelId).apply {
setContentTitle(getString(R.string.location_service))
setOngoing(true)
setContentText(getString(R.string.running_service_to_find_you_location))
setSmallIcon(R.drawable.ic_notifcation_icon)
val importance = NotificationManager.IMPORTANCE_DEFAULT
NotificationChannel(locationChannelId, channelName, importance).apply {
description = getString(R.string.running_service_to_find_you_location)
with((this@LocationService.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager)) {
createNotificationChannel(this@apply)
}
}
//need core 1.12 and higher and SDK 29 and higher
ServiceCompat.startForeground(
this@LocationService, 1, this.build(),
ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION
)
//this@LocationService.startForeground(1, this.build())
}
}
/**
* Main process for the service - find the background location and print it with Toast Message
* */
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (!checkIfLocationPermissionIsGrande()) return START_NOT_STICKY
val locationManager = getSystemService(LOCATION_SERVICE) as LocationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
findTheLocationForSdkBiggerThan30(locationManager = locationManager)
} else {
findTheLocationForSdkLowerThe31(locationManager = locationManager)
}
return START_STICKY
}
private fun findTheLocationForSdkBiggerThan30(locationManager: LocationManager) {
locationManager.requestLocationUpdates(
LocationManager.FUSED_PROVIDER,
minTimeLocationUpdateInMillisecond,
minDistanceLocationUpdateInMeter,
this@LocationService
)
}
private fun findTheLocationForSdkLowerThe31(locationManager: LocationManager) {
/**
* this code is deprecated from the SDK 34 but we need it for lower than SDK 34
* */
Criteria().apply {
accuracy = Criteria.ACCURACY_COARSE
powerRequirement =
Criteria.POWER_LOW
val provider = locationManager.getBestProvider(
this,
false
)
if (provider != null) {
locationManager.requestLocationUpdates(
provider,
minTimeLocationUpdateInMillisecond,
minDistanceLocationUpdateInMeter,
this@LocationService
)
}
}
}
/**
* Mandatory override when extend the Service()
* */
override fun onBind(intent: Intent?): IBinder? {
return null
}
/**
* Call back to get the update coordination
* */
override fun onLocationChanged(location: Location) {
Toast.makeText(
this,
"${location.latitude} ${this.getString(R.string.and)} ${location.longitude}",
Toast.LENGTH_LONG
).show()
}
private fun checkIfLocationPermissionIsGrande() = ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_COARSE_LOCATION
) == PackageManager.PERMISSION_GRANTED
}
Third, we have to declare the service inside the Manifest file. From SDK 34 and above we have to set the service type android:foregroundServiceType=”location” (in our example) based on the process that the service does. There are many foreground service types, and some of them according to the official Android Documentation are location, camera, mediaPlayback, phoneCall etc. (Check the references to see all the types).
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
<!--Other Code Here-->
<application
<!--Other Code Here-->
<!--Service-->
<service
android:name=".service.LocationService"
android:enabled="true"
android:exported="false"
android:foregroundServiceType="location" />
<!--other code here-->
</application>
</manifest>
The last step, Start the service from the Main Activity,
context.startService(Intent(context, LocationService::class.java))
Stop the service
context.stopService(Intent(context, LocationService::class.java))
Additional/Bonus Section (Optional)
In this section we will see the setup how you can restart the service after the mobile device rebooted.
First of all, you have to create a broadcast receiver class and add your code there as bellow.
Note: I handled it with secure share preferences if the service is running (optional — you can handle it as you want)
class RestartServiceBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val secureSharePreferences = context?.let { SecureSharePreferences(context = it) }
if (intent?.action != null && intent.action == Intent.ACTION_BOOT_COMPLETED) {
val shouldStartTheService = secureSharePreferences?.getBooleanValue(RESTART_SERVICE)
if (shouldStartTheService == true) {
context.startService(Intent(context, LocationService::class.java))
}
}
}
}
Last part, you have to declare the broadcast receiver inside the Manifest file.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
<!--Other Code Here-->
<!--Restart Service After Reboot the Mobile Device-->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
<!--Other Code Here-->
<!--Broadcast Receiver-->
<receiver
android:name=".broadcast_receiver.RestartServiceBroadcastReceiver"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<!--other code here-->
</application>
</manifest>
Check my sample project in my GitHub account to the follow link for more information:
If you interested, you can check an example project with foreground service in Flutter, tested only in Android.
I hope you find the article and sample project useful. Let me know your opinion under the comments, feel free to report any issue. I will appreciate it if you let a clap, so, if I have your support to continue writing.
References: