Android Background Processing

Sep 23 2022 · Kotlin 1.6, Android 12, Android Studio Chipmunk 2021.2.1

Part 3: Use Android Services

16. Use Android Service

Episode complete

Play next episode

Next
About this episode
Leave a rating/review
See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 15. Introduction Next episode: 17. Use IntentService

Get immediate access to this and 4,000+ other videos and books.

Take your career further with a Kodeco Personal Plan. With unlimited access to over 40+ books and 4,000+ professional videos in a single subscription, it's simply the best investment you can make in your development career.

Learn more Already a subscriber? Sign in.

Notes: 16. Use Android Service

The student materials have been reviewed and are updated as of SEPTEMBER 2022.

Transcript: 16. Use Android Service

The first type of Services you’ll implement is the unbound background Service! Let’s see how to implement one, to download an image.

Create a package called service. Then add a new class called the DownloadService, to the package:

class DownloadService : Service() {

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

  override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
  }
}

The class extends the Service class, and as such has to override two functions - onBind() and onStartCommand(). onBind() is used when you want a bound service, which you won’t be needing, so you return null.

onStartCommand(), however, is triggered when you start a service the regular way, and it receives an Intent with necessary parameters. You’ll use this to start the download image work. You have to declare every service you implement in the AndroidManifest.xml file, so open it, and add the following code:

<service android:name=".service.DownloadService" />

You need to declare your services, otherwise the app will crash if you try to start them unannounced.

Now that you have the baseline of the Service, you need to add some meat to the bone. Add the following code, to start the download operation:

class DownloadService : Service() {

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

  override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    val imagePath = intent?.getStringExtra("image_path")
    if (imagePath != null) {
      downloadImage(imagePath)
    } else {
      Log.d("Missing image path", "Stopping service")
      stopSelf()
    }

    return START_NOT_STICKY
  }
}

The first part is simple, you try to get the imagePath from the extra arguments, and if it is not null, you call downloadImage(). You’ll build this function in a moment.

If the image path is null, however, you log that to the console, and you call stopSelf(), to stop the service from within, and free up resources.

Finally, you have to return a start mode for the Service, for the function, and in this case, you return START_NOT_STICKY.

There are different modes for each service, and this one states that if the originating process is killed while the service is being started, the service will be removed from the started state.

It’s a good practice, because you then have to explicitly start the service yourself, and you don’t waste any resources. You could’ve also started the service in the STICKY mode, and it would continue to run until you told it to stop. That’s useful for things like playing music in the background, but not as much for downloads.

Now that you’ve implemented the start command for the service, implement the downloadImage() function, like so:

class DownloadService : Service() {

  ...

  private fun downloadImage(imagePath: String) {
    Thread(Runnable {
      val file = File(applicationContext.externalMediaDirs.first(), imagePath)

      FileUtils.downloadImage(file, imagePath)
    }).start()
  }
}

Using the predefined FileUtils class and the functions from previous episodes, you can download the image in two lines of code! :] The key thing to notice here is the new Thread(Runnable {}) code. Services in Android start on the main thread by default, and you have to move heavy operations to the background manually.

Now that you’ve implemented the download service, you can proceed to start it. Open ImagesFragment.kt, comment out the current download code, and add the following code to start the service:

override fun onImageDownload(imageUrl: String) {
  val intent = Intent(activity, DownloadService::class.java)
  intent.putExtra("image_path", imageUrl)

  activity?.startService(intent)
}

Starting a service is as simple as that! Now every time you call startService with the DownloadService as its target, you will trigger onStartCommand(), and it will proceed to download the image from the path you’ve provided.

This means you don’t have to restart the service every single time you want to download an image, you will just trigger it multiple times, but only start it once!

Run the project, and download the image using the service!

Because this service is started in the MainActivity, you need a way to stop the service from the Activity too. Otherwise, it may end up hanging, and consuming resources for no reason. Open the MainActivity.kt file, and add the following code:

  override fun onStop() {
    val intent = Intent(this, DownloadService::class.java)
    stopService(intent)

    super.onStop()
  }

By calling stopService(), with an Intent that targets the DownloadService once again, you will free up resources taken by the service, when you stop the MainActivity.

Now open the DownloadService.kt file, and add the following code, to make sure you’ve stopped the service:

override fun onDestroy() {
  applicationContext?.toast("Stopping service!")
  super.onDestroy()
}

Once the service is stopped, and destroyed, it will show a Toast message saying so. Run the project once more, attempt to download an image, and then press home, to see the service is stopped.