Blog

Foreground Service in Android

Services are very useful application component of Android. When you have to perform a long-running task and UI is not necessary for that, you can use Service. It can be started and stopped by any component of Android. Service can run even if you switch to another application.
Android application can have multiple services but it is not recommended to have because the Android system can kill the services if it is consuming lots of memory.

Based on the behavior of Services they are categorized in three types:

• Background
• Foreground
• Bound

Background: The service which does not notify user while running, called Background Service.
Foreground: The service which is noticeable to the user by notification.
Bound: When a service is bound to an application component like Activity by using bind Service(), it is called a bound service.

Need of Foreground Service

When we use services and perform long running operations, it requires some resources of the device that are limited like RAM. If an application is performing the resource-intensive task like watching videos, processing images, playing games etc, then using background service can lead to the bad user experience.

To resolve such types of issues Android Oreo (API level 26) imposes some limitations on the tasks app can do in the background. While an app is in the foreground, it can create and run both foreground and background services freely. When an app goes into the background, it has a window of several minutes in which it is still allowed to create and use services. At the end of that window, the app is considered to be idle. At this time, the system stops the app's background services, just as if the app had called the services' Service.stopSelf() methods.

To overcome this issue you can create a foreground service by showing a notification to the user when takes is completed or service is destroyed, notification must be cleared so that user can know that service is not running anymore.

How to Create Foreground Service?

To create a foreground service you have to create a class that will extends Service class and you have to override some methods like onCreate(), onStartCommand(Intent, int, int) etc.
After that you must have to build a notification to make it noticeable to the user, this notification must be shown whenever service has started (it’s better to launch notification from onCreate() method of Service).

After creating notification you must have to call startForeground(int notificationId, Notification) to launch ongoing notification.

Now you can start this service from your Activity/Fragment, you would see a notification in notification bar like below:

I have created a demo application in which I have created a service MyForegroundService in which I build a notification with action button “Stop Service” and displayed it.
I have created an activity ServiceInteractionActivity in which I have created two buttons to start and stop service. In this Activity, I have used LocalBroadcastManager to register a Broadcast Receiver MyServiceReceiver for getting an intent when “Stop Service” action button is clicked. Based on the action of that intent I have stopped the service. Below is the code snippet:

MyForegroundService

public class MyForegroundService extends Service {
	private NotificationManagerCompat mNotificationManager;
	private final static int NOTIFICATION_ID = 95;
	private final static String CHANNEL_ID = "service_notification_id";
	public final static String ACTION_STOP = "action_stop";
	
	public MyForegroundService() {	}
	
	@Override
	public void onCreate() {
		super.onCreate();
		mNotificationManager = NotificationManagerCompat.from(this);
		// Creating notification channel
		createNotificationChannel();
		Intent intent = new Intent(this, AnotherActivity.class);
		// Use System.currentTimeMillis() to have a unique ID for the pending intent
		PendingIntent pIntent = PendingIntent.getActivity(this, (int) System.currentTimeMillis(), intent, PendingIntent.FLAG_UPDATE_CURRENT);
		// Building notification here
		NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this, CHANNEL_ID);
		mBuilder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher));
		mBuilder.setSmallIcon(R.mipmap.ic_launcher_round);
		mBuilder.setContentTitle("MyForegroundService");
		mBuilder.setContentText("The Service is currently running");
		mBuilder.setAutoCancel(false);
		mBuilder.setContentIntent(pIntent);
		mBuilder.setPriority(NotificationCompat.PRIORITY_DEFAULT);
		// Creating Intent and Pending Intent for actions
		Intent bdIntent = new Intent(this, MyServiceReceiver.class);
		bdIntent.setAction(ACTION_STOP);
		PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, bdIntent, PendingIntent.FLAG_UPDATE_CURRENT);
		// Adding action to notification builder
		mBuilder.addAction(android.R.drawable.ic_menu_close_clear_cancel, getString(R.string.stop_service_action_title), pendingIntent);
		// Launch notification
		startForeground(NOTIFICATION_ID, mBuilder.build());
	}
	
	@Override
	public IBinder onBind(Intent intent) {
		// TODO: Return the communication channel to the service.
		throw new UnsupportedOperationException("Not yet implemented");
	}
	
	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		super.onStartCommand(intent, flags, startId);
		return START_STICKY;
	}
	
	@Override
	public void onDestroy() {
		mNotificationManager.cancel(NOTIFICATION_ID);
		super.onDestroy();
	}
	
	/**
	* -------------------------- Private Methods ------------------------------------------------
	* */
	private void createNotificationChannel() {
		// Create the NotificationChannel, but only on API 26+ because
		// the NotificationChannel class is new and not in the support library
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
		CharSequence name = getString(R.string.channel_name);
		String description = getString(R.string.channel_description);
		int importance = NotificationManager.IMPORTANCE_DEFAULT;
		NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
		channel.setDescription(description);
		// Register the channel with the system; you can't change the importance
		// or other notification behaviors after this
		NotificationManager notificationManager = getSystemService(NotificationManager.class);
		notificationManager.createNotificationChannel(channel);
	}	
}

MyServiceReceiver

public class MyServiceReceiver extends BroadcastReceiver {

	@Override
	public void onReceive(Context context, Intent intent) {
		if (!TextUtils.isEmpty(intent.getAction()) && intent.getAction().equals(ACTION_STOP)) {
		context.stopService(new Intent(context, MyForegroundService.class));
		}
	}
}

ServiceInteractionActivity

public class ServiceInteractionActivity extends AppCompatActivity {
	private Button mStartService;
	private Button mStopService;
	private TextView mTextView;
	private LocalBroadcastManager mLocalBroadcastManager;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_service_interaction);
		mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);
		mStartService = findViewById(R.id.button_start);
		mStopService = findViewById(R.id.button_stop);
		mTextView = findViewById(R.id.textView);
		
		mStartService.setOnClickListener(new View.OnClickListener() {		
			@Override
			public void onClick(View v) {
				startService(new Intent(ServiceInteractionActivity.this, MyForegroundService.class));
			}
		});
		
	mStopService.setOnClickListener(new View.OnClickListener() {
		@Override
		public void onClick(View v) {
			pService(new Intent(ServiceInteractionActivity.this, MyForegroundService.class));
		}
	});

		mLocalBroadcastManager.registerReceiver(new MyServiceReceiver(), new IntentFilter("custom-event-name"));
	}
	
	@Override
	protected void onPause() {
		super.onPause();
	}
	
	@Override
	protected void onDestroy() {
		mLocalBroadcastManager.unregisterReceiver(new MyServiceReceiver());
		super.onDestroy();
	}
}