Tutorial: How to use a foreground service
Below we explain how to use a foreground service to fetch location updates when running in background. The foreground service uses Android's notification mechanism to notify the user on background activities. It also allows a user to interact with the service.
Note
A full implementation can be seen in the PositioningWithBackgroundUpdates example app you can find on GitHub.
Step 1: Create a subclass of Service
Extend the Service
class and override the necessary methods. In this tutorial we called the class ForegroundService
- a full implementation of this class can be found on GitHub.
public class ForegroundService extends Service {
private static final String CHANNEL_ID = "ForegroundServiceChannel";
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
String input = intent.getStringExtra("inputExtra");
createNotificationChannel();
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Foreground Service")
.setContentText(input)
.setSmallIcon(R.drawable.ic_stat_name)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
return START_NOT_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
Step 2: Create a notification channel
For creating a notification channel, add the below code to our ForegroundService
class:
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel serviceChannel = new NotificationChannel(
CHANNEL_ID,
"Foreground Service Channel",
NotificationManager.IMPORTANCE_DEFAULT
);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(serviceChannel);
}
}
Step 3: Insert location handling
In order to handle the user's consent, more code needs to be injected as shown on GitHub or in the Find Your Location section. This part is always required - even if you do not want to enable background updates.
Also, you definitely want to implement methods such as startLocating()
to receive location updates and stopLocating()
when an app gets disposed. These methods are always required - even if you do not want to enable background updates. The code can be found on GitHub or in the Find Your Location section.
Similarly, you need to implement a LocationListener to handle location updates. For this you can use the same code as for non-background updates.
Step 4: Start the foreground service from your Activity
In order to enable getting background updates, call startService()
. The service can be stopped by calling stopService()
, for example via dedicated buttons. The following two methods can be added to your Activity
:
public void startService() {
Intent serviceIntent = new Intent(this, ForegroundService.class);
serviceIntent.putExtra("inputExtra", "Foreground Service Example in Android");
ContextCompat.startForegroundService(this, serviceIntent);
}
public void stopService() {
Intent serviceIntent = new Intent(this, ForegroundService.class);
stopService(serviceIntent);
}
Step 5: Declare the service and add permissions
In the AndroidManifest
you need to add the name of your Service
class and type of foreground service.
Note we also need to explicitly ask the user to allow background updates by adding the ACCESS_BACKGROUND_LOCATION
permission. In addition, we need the FOREGROUND_SERVICE
and the POST_NOTIFICATIONS
permissions.
Below you can find an example, together with all other permissions that are required by HERE Positioning:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.foregroundservice">
<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_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<service
android:name=".ForegroundService"
android:enabled="true"
android:exported="true"
android:foregroundServiceType="location" />
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
That's it - now your app will be fully operable in background and can receive location events - even if the device is locked. Note that handling of location updates in background can be a sensitive topic, so make sure to inform your users why you need this feature - and be aware that the Android OS will notify the user on the lock screen that your app is running and active. Consider to use less frequent location updates to avoid draining the battery when not necessary, for example, by setting a less-frequent LocationAccuracy
, like for example, HUNDREDS_OF_METERS
.