There are three types of services in Android
- Foreground service
- Background service
- Bound service
Let’s look at the definition of service
A Service
is an application component that can perform long-running operations in the background.
For example playing music in background, doing network transaction and uploading large files to server etc.
Foreground service
It is a service which is visible to the user via notification. As the notification is displayed user knows that some operation is going in the background with the help of service. Playing music in the background while showing notification is common example of this.
Background service
Background service is a service, which is not directly visible to the user it run in the background. And user does not become aware of it unless user is notified explicitly about it. It can be used to compact the storage of app.
Bound service
Bounce service is a service which is connected/bound with another Android component mainly for inter-process communication. An activity can bind itself with a service which is uploading some photos to show the progress to the user. Let’s say an activity and a service are bound in this case, so the service can send the progress of photos being uploaded to the activity and which can display it to user.
Let’s see how can we create each one of these
How to create Background service ->
- Cerate a class extending Service class
- Override its method
- Do the work in onStartCommand method
- Register this service in Manifest file
- Start the service from the Activity
class BackgroundServiceExample : Service() {
override fun onBind(p0: Intent?): IBinder? {
return null
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
// Performing background work here
for (i in 0..5) {
TimeUnit.SECONDS.sleep(1)
Log.d("TAG", "Current thread name: ${Thread.currentThread().name} : " + i)
}
return super.onStartCommand(intent, flags, startId)
}
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
findViewById<Button>(R.id.buttonStartService).setOnClickListener {
// Start the service in activity
startService(Intent(this@MainActivity, BackgroundServiceExample::class.java))
}
}
}
// Register in AndroidManifest.xml
<service android:name=".BackgroundServiceExample" />
In case of background service when the service is running, by default it will run on main thread, so while the services running the UI thread will be busy and if you tried to perform any UI operations, the app will crash with the Error message- app not responding ANR
To do avoid this you can create new thread in onStartCommand method to do the work
After starting the service if you leave the application in the background, the service will continue running for some time in the background but it will be killed due to the system constraints. So the forgot service is usually recommended over background service. As it does not usually get killed until some exceptional resource crunch occurs.
How to create Foreground service ->
- Cerate a class extending Service class
- Override its method
- Start the notification and the work in onStartCommand method
- Register this service in Manifest file
- Create a class extending application class and create notification channel in it’s onCreate method
- Start the service from the Activity by calling startForegroundService
As we are extending Service class, the work will be done in Main thread only. So to avoid it create new thread to perform work.
class ForegroundServiceExample : Service() {
companion object {
val CHANNEL_ID = "channel_id"
}
override fun onBind(p0: Intent?): IBinder? {
return null
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.d("TAG", "Current thread name: ${Thread.currentThread().name}")
createNotification()
// TODO Perform work here
return START_STICKY
}
private fun createNotification() {
val notification: Notification = NotificationCompat.Builder(
this,
CHANNEL_ID
)
.setContentTitle("Foreground Service title")
.setContentText("Foreground Service text")
.setSmallIcon(R.drawable.ic_notification_overlay)
.build()
startForeground(11, notification)
}
}
create Notification Channel in Application class for notification to work in Oreo and above versions
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
createNotificationChannel()
}
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationChannel = NotificationChannel(
ForegroundServiceExample.CHANNEL_ID,
"Channel name",// which will appear Settings app against this service
NotificationManager.IMPORTANCE_HIGH
)
val notificationManager = getSystemService(NotificationManager::class.java)
notificationManager.createNotificationChannel(notificationChannel)
}
}
}
}
startForegroundService in MainActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
findViewById<Button>(R.id.buttonStartService).setOnClickListener {
// Start the service in activity
val serviceIntent = Intent(this@MainActivity, ForegroundServiceExample::class.java)
ContextCompat.startForegroundService(this@MainActivity, serviceIntent)
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/buttonStartService"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Start service"
android:textSize="32sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
- Add android:name=”.MyApplication” in manifest file
- Register service
<?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.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application
android:name=".MyApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Conceptssss"
tools:targetApi="31">
<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>
<service android:name="com.example.conceptssss.ForegroundServiceExample" />
</application>
</manifest>
How to create Bound service ->
- Cerate a class extending Service class
- Override its method
- Create inner class in it and extend it with Binder
- return service instance from it
- return the instance of this inner class from onBind method
- Register this service in Manifest file
- in activity- create object of ServiceConnection
- we will receive the object of service in it’s onServiceConnected method
- We can bind and unbind the service with the connection object created in 7th step
- in activity- Call the method of serving with help of service object
import android.app.Service
import android.content.Intent
import android.os.Binder
import android.os.IBinder
class BoundServiceExample : Service() {
private val binder = LocalBinder()
fun getDataFromService(): String {
return "secret dummy data"
}
/**
* Instance of this service is shared with client for communication via LocalBinder class
*/
inner class LocalBinder : Binder() {
// Return this instance of LocalService so clients can call public methods.
fun getServiceInstance(): BoundServiceExample {
return this@BoundServiceExample
}
}
override fun onBind(intent: Intent): IBinder {
return binder
}
}
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.IBinder
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
class BindActivity : AppCompatActivity() {
private lateinit var mService: BoundServiceExample
private var mBound: Boolean = false
/**
* Use this connection to get the instance of service,
* with which we can call public methods in service
* It's also used to bind and unbind the service from this activity */
private val connection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
// We've bound to LocalService, cast the IBinder and get LocalService instance.
val binder = service as BoundServiceExample.LocalBinder
mService = binder.getServiceInstance()
mBound = true
}
override fun onServiceDisconnected(arg0: ComponentName) {
mBound = false
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_bind)
}
override fun onStart() {
super.onStart()
// Bind this Activity to BoundServiceExample
val intent = Intent(this@BindActivity, BoundServiceExample::class.java)
bindService(intent, connection, Context.BIND_AUTO_CREATE)
}
override fun onStop() {
super.onStop()
unbindService(connection)
mBound = false
}
fun onButtonClick(v: View) {
if (mBound) {
val dataFromService = mService.getDataFromService()
Toast.makeText(this, "Data: $dataFromService", Toast.LENGTH_SHORT).show()
}
}
}
<application
android:name=".MyApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Conceptssss"
tools:targetApi="31">
<activity
android:name=".BindActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".BoundServiceExample" />
</application>
Read more from here- Android documents
Learn about Creating deep link in Android app