Mobile Development and 3D Graphics - Part 3

Introduction to Services and Broadcasts

Introduction - Leaving tasks running when the app is shut down

Frequently in an app we need to perform a task when the activity has been completely shut down. For example, in a music player, we probably want the music to continue to play when the user has closed the player's main activity - and we want the user to be able to pause or rewind the same music when they relaunch the activity. Another example might be a mapping application in which the user would like to record their walking route using GPS. We want the recording to continue even if the user closes the activity - and allow the user to stop the recording if they re-launch the activity.

Services

Difference between a service and a thread

Implementing a Service

Service lifecycle methods

Services, like activities, have lifecycle methods including:

Starting a service

Using onStartCommand() to start the service

Stopping a service and overriding onDestroy()

Binding a service

Binding a service - example

To bind a service, we need two more components, a Binder (as we have seen) and a ServiceConnection, which is used to provide a connection between the activity and the service. Here is an example. (Note that IBinder is an interface which Binder implements).

class MusicService: Service() {
    inner class MusicServiceBinder(val musicService: MusicService): android.os.Binder()

    override fun onBind(intent:Intent) : IBinder {
        return MusicServiceBinder(this)
    }
}
Note the following:

The ServiceConnection

In our activity, we must create a ServiceConnection object, to obtain a connection to the service. Here is an example:

 val serviceConn = object: ServiceConnection {
     override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
        service = (binder as MusicService.MusicServiceBinder).musicService
     }

     override fun onServiceDisconnected(name: ComponentName?) {
     }
 }
It is an instance of an anonymous class (a class with no name, which inherits from the abstract class ServiceConnection and overrides the required methods on-the-fly. These methods are:

Using bindService() to actually bind the service

Finally, In your main activity, you bind the service using bindService() which, like startService() takes an Intent for the Service.

val bindIntent = Intent(this, MusicService::class.java);
bindService(bindIntent, serviceConn,  Context.BIND_AUTO_CREATE)
This will bind the activity to the service and trigger the onBind() method in the MusicService. The Context.BIND_AUTO_CREATE flag will "automatically create the service as long as the binding exists" (see here), without this flag, you will also need to call startService() to start the service.

Unbinding

Starting and binding

Broadcasts

BroadcastReceiver

Example of sending a broadcast

val broadcast = Intent().apply {
    action = "sendTime"
    putExtra("time", System.currentTimeMillis())
}
sendBroadcast(broadcast)

Receiving a broadcast

Unregistering a broadcast receiver

Exercises

  1. Start with this GitHub repository:
    https://github.com/nwcourses/ServicesStarter.git
    which is another version of the mapping application. It features a map plus three buttons: "Start GPS", "Get GPS location" and "Stop GPS". Using a service, complete the app as follows. The functionality associated with each button should only run if the permissionsGranted boolean is true. Test by using the emulator and sending mock locations -GPS is unlikely to work indoors.
  2. Modify the app to use broadcasts as follows: