DEV Community

Cover image for Lets talk about Services in Android with Kotlin
Tristan Elliott
Tristan Elliott

Posted on

Lets talk about Services in Android with Kotlin

Table of contents

  1. What is a Services
  2. Why use a Service?
  3. What kind of Service
  4. Creating a Service

My app on the Google play store

Resources

A word of caution

  • As I was reading blog posts, I came to the realization that there is a common naming convention where developers will name there HTTP client SomethingService. For example, NetworkService or ClientService. These Services are only a service in name, they have no affiliation with the application component that is a Service.
  • If a class does not extend the abstract Service class, it is not a application component. This initially confused me and unfortunately this naming convention has polluted the google results pertaining to application Services.

What is a Services

  • The TLDR is, A Service is an application component that can perform long-running operations in the background. It does not provide a user interface. but if you want a deeper understanding, read on.
  • Following the Service Development Guide, we can define what a Service is NOT:

  • A Service is not a separate process. The Service object itself does not imply it is running in its own process; unless otherwise specified, it runs in the same process as the application it is part of.

  • A Service is not a thread. It is not a means itself to do work off of the main thread (to avoid Application Not Responding errors).

  • However, if that does not solidify your understanding of services. You can think of a Service as an Activity without the UI. Which, again, might sound even more confusing but here are 2 reasons why that comparison is valid:

1) manifest declaration: the Android system reads the application's manifest when the application is installed and it registers the Activities just as the Services.

2) Singular instance: Similar to an activity there is only one instance of the service and all the subsequent requests go to that Service.

Why use a Service?

  • full documentation on why you should use a Service, HERE
  • So right now your are probably saying to yourself, wait, can't most of this just be done in a thread? Or what is wrong with the background thread that I have created with coroutines in my ViewModel? You are right, you can do everything that a Service can do inside a background thread. However, the beauty of the Service is not the work it does in the background. It is the fact that it skips the Activity's lifecycle. Meaning, despite the Activity calling the onPause() method. Our Service will continue to do its work. Which is awesome because our work will continue no matter what state our application is in.
  • So should all my network requests be made through Services ? Technically yes and when reading Android Programming, Pushing the Limits by Erik Hellman, he states, I always, with no exception, perform network operations from a Service.
  • However, I have not done that to my personal application yet and it is currently on the app store. So know that it is 100% acceptable to make network requests from the ViewModel on a background thread.

What kind of Service

  • Full documentation of Service types, HERE
  • Technically there are 3 types of Services, Foreground,Background and Bound. However, this tutorial I am only going to be explaining the Background Service. As that is the only Service my application uses
  • As the Service documentation states: A background service performs an operation that isn't directly noticed by the user.
  • I am building a Network monitoring Service that will monitor the network and notify the user if there is no network. The background service will work perfectly for this application

Creating a Service

  • Full documentation on Service creation, HERE
  • When creating a Service there are are 4 steps we need to go through:

    1) Declaring the manifest

    2) Extending the Service class

    3) Starting the Service

    4) Stopping the Service

1) Declaring the manifest

  • We must declare all services in our application's manifest file. To declare your service, add a element as a child of the <application> element. Here is an example:
<manifest ... >
  ...
  <application ... >
      <service android:name=".services.NetworkMonitorService"
            android:exported="false"
            android:description="@string/network_monitor_service"
            />
  </application>
</manifest>

Enter fullscreen mode Exit fullscreen mode
  • The exported attribute in the Service’s manifest declaration controls whether or not it is visible and can be used from other applications. Setting android:exported="false" means this Service is only visible to our application. android:description="@string/network_monitor_service" is set incase the user is a power user and has developer options enabled. In which case they would be able to see the service running and our description would tell them what this service is doing.

2) Extending the Service class

class NetworkMonitorService: Service() {
override fun onCreate() {
        super.onCreate()
   // DO INITIALIZATION HERE
    }


    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        Log.d("NetworkMonitorService","STARTING")


        // If we get killed, after returning from here, restart
        return START_NOT_STICKY
    }


    override fun onDestroy() {
        Log.d("NetworkMonitorService","DESTROY")

    }

    override fun onBind(p0: Intent?): IBinder? {
        return null
    }

}

Enter fullscreen mode Exit fullscreen mode
  • Now, all initialization should be done inside of the onCreate(). This is done because when onCreate() is only called if the service is not created.
  • onBind() only gets called if we are using a binding Service, since we are not we have it return null. onDestroy() is called when the Service is destroyed(more on service termination later). onStartCommand() is called when the Service is created(more on that later as well).
  • What we really need to talk about is the START_NOT_STICKY. This is important because it is a constant and it describes how the system should continue the service in the event that the system kills it. By using START_NOT_STICKY we are telling the system to not recreate this service if it kills it.

3) Starting the Service

  • Full documentation on starting a Service, Here
  • To start the Service all we have to do is define an Explicit Intent and call the startService() method
  • My example is done inside of a AndroidViewModel, why? well so I can create and destroy it easily
class NetworkMonitorViewModel (
    application: Application
): AndroidViewModel(application) {
    fun startService(){
        val context:Application = getApplication()
        context.startService(Intent(context, NetworkMonitorService::class.java))

    }
    override fun onCleared() {
        super.onCleared()
        val context:Application = getApplication()
        context.stopService(Intent(context, NetworkMonitorService::class.java))
        Log.d("NetworkMonitorViewModel","cleared")
    }
}

Enter fullscreen mode Exit fullscreen mode
  • So anytime I want to create this Service I can just call startService() and it creates an explicit intent and starts my service.

4) Stopping the Service

  • Full documentation on stopping the Service, HERE
  • This is where things get interesting because there are 3 scenarios where our service can get stopped:

    1) The developer stops it

    2) The system stops it to reclaim memory

    3) The user nukes it from the developer settings

The developer stops it

  • We can stop the Service by calling stopSelf() or stopService(), the system destroys the service as soon as possible. If you noticed from the code chunk before, I am calling stopService() from the ViewModel's onCleared method which means when the viewmodel is destroyed so is the Service.

The system stops it to reclaim memory

  • If a user is using our application it is in the Foreground, meaning that our Service is given high priority by the Android system and will not be destroyed. However, if the user switches to another application. Our service is no longer in the foreground and the Android system will only wait 60 seconds until it destroys the Service. So we have to account for that

The user nukes it from the developer settings

  • If our user is a power user and has the developer settings turned on. They can see what Services are running on what applications. If they so choose so they can stop the Service that is running on our application

Dealing with all these situations

  • To deal with these situations I am creating my Service in both the onCreate() and onResume() method of a Fragment. I am not calling stopService() in a Fragment method, to avoid the destroying that happens on a orientation change. This is the first fragment my user visits when they open my application:
class HomeFragment : Fragment() {
private val networkMonitorViewModel: NetworkMonitorViewModel by activityViewModels()

override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

        networkMonitorViewModel.startService()
// rest of the code not shown here
}

override fun onResume() {
        super.onResume()
        networkMonitorViewModel.startService()
}

}

Enter fullscreen mode Exit fullscreen mode
  • It is worth pointing out that I will have to do this in every fragment of application because these similar scenarios can occur in every fragment.
  • The onResume() call ensures that if the system kills our service, then when the user revisits our application a new Service will be created

Conclusion

  • Thank you for taking the time out of your day to read this blog post of mine. If you have any questions or concerns please comment below or reach out to me on Twitter.

Top comments (0)