In this tutorial, we will learn about foreground service and how to create a foreground service in an android application.
A foreground service is a service, which works exactly same as a normal service (background service) and the only difference is, it has a notification attached to it in the notification tray of the device.
The main advantage of having a foreground service is its higher priority than its background version. Android OS implemented restriction to background execution to save battery and memory usages, i.e., it kills least used apps (or processes) to retain the memory and battery.
With Foreground service, app gets following advantages,
- Higher priority hence less likely to be killed by OS
- Indication to user about important task under processing.
- Quick Access to the app like controls in music player apps.
Normally, apps run long tasks in services. In which some of them are urgent like sending or uploading files to server, such tasks are safe to run in a foreground service.
Let’s see how to create a foreground service and start it. The below code snippet will create a foreground service with a notification.
Notification will have a button to open app. When app opens, current timestamp value passed to the MainActivity and shows it on the UI.
Step 1: Create a Foreground Service
package com.devdeeds.foregroundservice import android.app.* import android.content.Context import android.content.Intent import android.graphics.Color import android.os.Build import android.os.Handler import android.os.IBinder import android.util.Log import androidx.core.app.NotificationCompat /** @author Jayakrishnan P.M * Created by Devdeeds.com on 21/3/18. */ class FService : Service() { private val mHandler: Handler? = Handler() private lateinit var mRunnable: Runnable override fun onCreate() { super.onCreate() startForegroundService() } override fun onBind(p0: Intent?): IBinder? { return null; } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { Log.d(TAG, "ON START COMMAND") if (intent != null) { when (intent.action) { ACTION_STOP_FOREGROUND_SERVICE -> { stopService() } ACTION_OPEN_APP -> openAppHomePage(intent.getStringExtra(KEY_DATA)) } } return START_STICKY; } private fun openAppHomePage(value: String) { val intent = Intent(applicationContext, MainActivity::class.java) intent.putExtra(KEY_DATA, value) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) startActivity(intent) } private fun createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val chan = NotificationChannel( CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT ) chan.lightColor = Color.BLUE chan.lockscreenVisibility = NotificationCompat.VISIBILITY_PRIVATE val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager service.createNotificationChannel(chan) } } /* Used to build and start foreground service. */ private fun startForegroundService() { //Create Notification channel for all the notifications sent from this app. createNotificationChannel() // Start foreground service. startFService() mRunnable = Runnable { notifyNextEvent() mHandler?.postDelayed(mRunnable, ONE_MIN_MILLI) //repeat }; // Schedule the task to repeat after 1 second mHandler?.postDelayed( mRunnable, // Runnable ONE_MIN_MILLI // Delay in milliseconds ) } private fun startFService() { val description = getString(R.string.msg_notification_service_desc) val title = String.format( getString(R.string.title_foreground_service_notification), getString(R.string.app_name) ) startForeground(SERVICE_ID, getStickyNotification(title, description)) IS_RUNNING = true } private fun getStickyNotification(title: String, message: String): Notification? { val pendingIntent = PendingIntent.getActivity(applicationContext, 0, Intent(), 0) // Create notification builder. val builder = NotificationCompat.Builder(applicationContext, CHANNEL_ID) // Make notification show big text. val bigTextStyle = NotificationCompat.BigTextStyle() bigTextStyle.setBigContentTitle(title) bigTextStyle.bigText(message) // Set big text style. builder.setStyle(bigTextStyle) builder.setWhen(System.currentTimeMillis()) builder.setSmallIcon(R.drawable.ic_stat_name) //val largeIconBitmap = BitmapFactory.decodeResource(resources, R.drawable.ic_alarm_on) //builder.setLargeIcon(largeIconBitmap) // Make the notification max priority. builder.priority = NotificationCompat.PRIORITY_DEFAULT // Make head-up notification. builder.setFullScreenIntent(pendingIntent, true) // Add Open App button in notification. val openAppIntent = Intent(applicationContext, FService::class.java) openAppIntent.action = ACTION_OPEN_APP openAppIntent.putExtra(KEY_DATA, "" + System.currentTimeMillis()) val pendingPlayIntent = PendingIntent.getService(applicationContext, 0, openAppIntent, 0) val openAppAction = NotificationCompat.Action( android.R.drawable.ic_menu_view, getString(R.string.lbl_btn_sticky_notification_open_app), pendingPlayIntent ) builder.addAction(openAppAction) // Build the notification. return builder.build() } private fun notifyNextEvent() { NotificationHelper.onHandleEvent( getString(R.string.title_gen_notification), getString(R.string.description_gen_notification), applicationContext ) } private fun stopService() { // Stop foreground service and remove the notification. stopForeground(true) // Stop the foreground service. stopSelf() IS_RUNNING = false } override fun onDestroy() { IS_RUNNING = false mHandler?.removeCallbacks(null) } companion object { const val TAG = "FOREGROUND_SERVICE" const val ACTION_STOP_FOREGROUND_SERVICE = "ACTION_STOP_FOREGROUND_SERVICE" const val ACTION_OPEN_APP = "ACTION_OPEN_APP" const val KEY_DATA = "KEY_DATA" private const val CHANNEL_ID: String = "1001" private const val CHANNEL_NAME: String = "Event Tracker" private const val SERVICE_ID: Int = 1 private const val ONE_MIN_MILLI: Long = 60000 //1min var IS_RUNNING: Boolean = false } }
Notifications generated by the app will be sending with an interval of 1 minute.
Step 2: Add Foreground Service In AndroidManifest XML File
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.devdeeds.foregroundservice"> <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <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"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name=".FService" android:enabled="true" android:exported="false" /> <receiver android:name=".AutoStartUpBootReceiver" android:enabled="true" android:exported="false"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.QUICKBOOT_POWERON" /> </intent-filter> </receiver> </application> </manifest>
Where android.permission.WAKE_LOCK is for waking the app screen on the event of notification.
android.permission.RECEIVE_BOOT_COMPLETED is for restarting the service on device reboot. Please refer complete source code for the reference.
Step 3: Start Foreground Service From Activity
package com.devdeeds.foregroundservice import android.content.Intent import android.os.Build import android.os.Bundle import android.widget.TextView import androidx.appcompat.app.AppCompatActivity class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) //Start service if (!FService.IS_RUNNING) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { startForegroundService(Intent(applicationContext, FService::class.java)) } else { startService(Intent(applicationContext, FService::class.java)) } } //Show data sent from service to view on click if (intent != null && intent.getStringExtra(FService.KEY_DATA) != null) { findViewById<TextView>(R.id.txtValueView).text = intent.getStringExtra(FService.KEY_DATA) } } }
In the activity, it receives data via intent and shows it in a TextView.
This example demonstrates how to communicate a foreground service with activity via a notification.