Service Creation and Destruction

Because you have only two lifecycle callbacks for a Service, this is where you should do most of the

initialization and cleanup. In the onCreate() method, you initialize new Handler objects, retrieve system

Services, register new BroadcastReceivers, and perform other initializations that are required for the

Service operations. Remember that this method is executed on the main thread, so you should still delegate

Chapter 6: Services and Background Tasks 113

any long-running and potentially blocking operations to a background thread using an AsyncTask, Handler

or one of the other methods described in Chapter 2.

You do all the cleanup for your Service in the onDestroy() method. You especially need to stop any

HandlerThreads that you started and unregister BroadcastReceivers that you registered earlier in

the Service. Again, it’s important to remember that what you do here is executed on the main thread, so

any cleanup that takes a long time may require you to launch a new thread where you perform that work. For

instance, you may need an AsyncTask to gracefully shut down a network server.

The onDestory() method is called when the system determines that the Service is ready to be shut down

and removed. This usually happens when the application of the Service is no longer in the foreground.

Starting Services

A Service can be started in two ways: Either it receives an Intent through the Context.startService()

method, or a client (either local or remote) binds to it using the Context.bindService() method. Both of

these methods will result in the Service moving to the “started” state.

When calling Context.startService(), an Intent is sent that must match the intent filter for

the Service. (It’s also possible to use an explicit Intent with the ComponentName of the Service without

having to define an intent filter.) This method doesn’t provide any reference to the Service for normal

synchronized method calls but it is useful for performing message-based triggering of an operation. This is

normally something you do as a result of a user interaction that could take an arbitrary amount of time, such as

uploading a photo or sending a status update to a server. It’s also a useful way of providing a simple interface

for other applications, as I describe in more detail in Chapter 7.

When you start a Service using Context.startService(), the onStartCommand() callback is

triggered on your Service. In this method, you receive the Intent that was sent to your Service, and you

return a constant telling the Android system how your Service should react if the system shuts it down. This

is one of the most complicated parts of how Services work and is easy to get wrong. You need to remember

three return values (there’s also a fourth value for compatibility reasons that I won’t cover in this book): START_

STICKY, START_NOT_STICKY, and START_REDELIVER_INTENT.

When you return START_STICKY, it signals that you want your Service to be restarted if the system

shuts it down for some reason (usually when running low on memory). However, when the system restarts

the Service, the onStartCommand() is called with the Intent parameters set to null, so you have to

take care of this in your code. A typical example of this return value is a music player where the Service is

always started in the same way. This means that you need to store the internal state of you Service when

onDestroy() is called.

When you return START_NOT_STICKY, your Service won’t restart after the system shuts it down. This

is probably useful when you send an Intent to the Service to perform a one-shot operation, such as

uploading something to a server. If the Service is shut down before completing its task, it shouldn’t try to

repeat the operation.

The third return constant, START_REDELIVER_INTENT, works like START_STICKY, except that the original

Intent is redelivered when the system restarts your Service.

114 Part II: Getting the Most Out of Components

Regardless of what you choose to return from onStartCommand() in your Service, consider not only how

the Service is started but also how it will be stopped. Choosing START_REDELIVER_INTENT or START_

STICKY could have unforeseen consequences if you’re not careful.

When you receive a call to the onStartCommand() in your Service, you have three parameters to

deal with. The first is the Intent, which can be null depending on what you returned in previous calls to

onStartCommand(). The second parameter is a flag indicating what this call represents. It can be either

0, START_FLAG_RETRY, or START_FLAG_REDELIVERY. The third parameter, named startId, can

sometimes be useful when you need to safely stop the Service and you have received multiple calls to

onStartCommand() during the lifecycle.

Because using Context.startService() can be considered as asynchronous (see Figure 6-1), you may

also need a way to signal back to the Activity that your operation is complete. One way of doing so is to use

a programmatic BroadcastReceiver (see the section “Asynchronous Messaging with Intents” later in this

chapter, where I briefly discuss this topic; see also Chapter 8, where I go into more detail).

Figure 6-1 Sequence diagram over an asynchronous Service interaction

Binding Services

The second way to start a Service is through Context.bindService(). When you bind to a Service, it

will continue operating until the last client disconnects. Binding to a Service in the same application process

is a simple way to access a reference to the Service object from another component and call methods directly

on it. This is called a local binder and is illustrated in Figure 6-2.

Chapter 6: Services and Background Tasks 115

Figure 6-2 Sequence diagram illustrating the use of the local binder pattern

The following code shows how to implement a local Binder for your Service that you use when it is

accessed only within your application. This approach doesn’t work if your Service will be accessed from other

applications. In those cases, you need to use an AIDL, Android’s method for serializing method calls across

process, which I describe in detail in Chapter 7.

public class MyLocalService extends Service {

private LocalBinder mLocalBinder = new LocalBinder();

public IBinder onBind(Intent intent) {

return mLocalBinder;

}

public void doLongRunningOperation() {

// TODO Start new thread for long running operation...

}

public class LocalBinder extends Binder {

public MyLocalService getService() {

return MyLocalService.this;

}

}

}

When your Service returns only null in the method onBind(), you can bind to it from another

component. When doing so in an Activity, you usually implement the binding and unbinding in the

onResume() and onPause() methods, as shown here:

public class MyActivity extends Activity implements ServiceConnection {

private MyLocalService mService;

@Override

116 Part II: Getting the Most Out of Components

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

}

@Override

protected void onResume() {

super.onResume();

Intent bindIntent = new Intent(this, MyLocalService.class);

bindService(bindIntent, this, BIND_AUTO_CREATE);

}

@Override

protected void onPause() {

super.onPause();

if (mService != null) {

unbindService(this);

}

}

@Override

public void onServiceConnected(ComponentName componentName,

IBinder iBinder) {

mService = ((MyLocalService.LocalBinder) iBinder).getService();

}

@Override

public void onServiceDisconnected(ComponentName componentName) {

mService = null;

}

}

In this Activity example, the call to bindService() is asynchronous, and the actual IBinder interface

that you return on onBind() is passed to the callback onServiceConnected(). The second callback,

onServiceDisconnected(), is triggered from the call to unbindService() in the onPause() method.

This design provides an efficient binding and unbinding to the Service, for when the Activity is active and

dismissed, respectively. You can also use the callback where you receive the actual reference to the Service,

onServiceConnected(), to update the UI and enable buttons or other components that require the Service

to be available. Once you have a reference to the actual Service object, you can treat it as any other Java object.

However, it’s important to always release any references between an Activity and a Service in onPause().

Staying Alive

When your Service starts and the application is running in the foreground (that is, when an Activity of

the application is shown) your Service will be the last one shut down by the system. However, as soon as the

user leaves your application, the Service will no longer be running in the foreground and may be targeted for

shutdown by the system. If you need to keep your Service in the foreground even if your application is not

the active one, you can do so by calling the method Service.startForeground()

Chapter 6: Services and Background Tasks 117

The system tries to keep Services alive and running for as long as possible. Only when it is running

out of resources, usually free RAM, does it start to stop Services. However, you should always

assume that the system may stop your Services at any given point.

In the following code, you see an example of the onStartCommand() method for a Service that uploads

a photo to an online server. In this method, also make sure that you’re running in the foreground and that you

provide the user with a notification that you have a background operation running.

@Override

public int onStartCommand(Intent intent, int flags, int startId) {

if (intent != null) {

String action = intent.getAction();

if (ACTION_SHARE_PHOTO.equals(action)) {

// Build the notification to be shown

Notification.Builder builder = new Notification.Builder(this);

builder.setSmallIcon(R.drawable.notification_icon);

builder.setContentTitle(getString(R.string.notification_title));

builder.setContentText(getString(R.string.notification_text));

Notification notification = builder.build();

// Start the service in the foreground

startForeground(NOTIFICATION_ID, notification);

// Perform out background operation…

String photoText = intent.getStringExtra(EXTRA_PHOTO_TEXT);

Bitmap photoBitmap =

intent.getParcelableExtra(EXTRA_PHOTO_BITMAP);

uploadPhotoWithText(photoBitmap, photoText);

}

}

return START_NOT_STICKY;

}

First, you create a notification that you will use for your background operation. Next, you call startForeground()

together with a unique ID and the notification you just created. The result is a new notification that appears in the

status bar until the Service is either stopped (see the next section) or you call stopForeground()with the

parameter true.

Although this is the recommended way of making sure your Service remains operating even though your

application is running in the background, don’t do so unless you really need to—because you could easily end

up wasting system resources. Also, when the operation is complete, make sure you exit everything properly so

that, again, you won’t waste system resources.

Stopping Services

Once your Service starts, it will remain running for as long as possible. Depending on how it was started, the

system will restart the Service if it’s shut down due to lack of resources.

118 Part II: Getting the Most Out of Components

Some unexpected results can occur when a Service suddenly restarts, even though the user didn’t

launch the application. So, it’s important to properly stop your Services once the user’s work is

complete.

If your Service started from Context.bindService(), it will be automatically stopped when the last

client disconnects (that is, called Context.unbindService()). The exception is if you also call Service.

startForeground() in your Service to keep it alive after the last client disconnects, which is why it’s

important to call Service.stopForeground() properly.

If you start your Service by using Context.startService(), then the only way to ensure that your

Service is stopped is by calling either Service.stopSelf() or Context.stopService(). This

signals to the system that it should stop and remove the Service. The only way to restart the Service

is with an explicit call to Context.startService() or Context.bindService(). Also, calling

Service.stopSelf() or Context.stopService() on a Service that was started with Context.

startService() will always stop it, regardless of how many times onStartCommand() was executed

(that is, calls to Context.startService() don’t stack).

The code example that follows shows a (very) simple Service for a music player. Its only feature is

adding tracks to a play queue. If the queue is empty when a new track arrives, it will start playing the track

immediately; otherwise, it will place the track at the end of the queue. Once a track is finished, the callback

onCompletion()is called, and a check is made to determine whether the queue is empty. If there are more

tracks, the MediaPlayer is prepared with the next track, and playback starts again; if the queue is empty, you

call Service.stopSelf(), which causes the Service to shut down and release the MediaPlayer.

public class MyMusicPlayer extends Service

implements MediaPlayer.OnCompletionListener {

public static final String ACTION_ADD_TO_QUEUE =

“com.aptl.services.ADD_TO_QUEUE”;

private ConcurrentLinkedQueue<Uri> mTrackQueue;

private MediaPlayer mMediaPlayer;

public IBinder onBind(Intent intent) {

return null;

}

@Override

public void onCreate() {

super.onCreate();

mTrackQueue = new ConcurrentLinkedQueue<Uri>();

}

@Override

public int onStartCommand(Intent intent, int flags, int startId) {

String action = intent.getAction();

if (ACTION_ADD_TO_QUEUE.equals(action)) {

Uri trackUri = intent.getData();

addTrackToQueue(trackUri);

}

Chapter 6: Services and Background Tasks 119

return START_NOT_STICKY;

}

@Override

public void onDestroy() {

super.onDestroy();

if(mMediaPlayer != null) {

mMediaPlayer.release();

mMediaPlayer = null;

}

}

/**

* Add track to end of queue if already playing,

* otherwise create a new MediaPlayer and start playing.

*/

private synchronized void addTrackToQueue(Uri trackUri) {

if(mMediaPlayer == null) {

try {

mMediaPlayer = MediaPlayer.create(this, trackUri);

mMediaPlayer.setOnCompletionListener(this);

mMediaPlayer.prepare();

mMediaPlayer.start();

} catch (IOException e) {

stopSelf();

}

} else {

mTrackQueue.offer(trackUri);

}

}

// Track completed, start playing next or stop service...

@Override

public void onCompletion(MediaPlayer mediaPlayer) {

mediaPlayer.reset();

Uri nextTrackUri = mTrackQueue.poll();

if(nextTrackUri != null) {

try {

mMediaPlayer.setDataSource(this, nextTrackUri);

mMediaPlayer.prepare();

mMediaPlayer.start();

} catch (IOException e) {

stopSelf();

}

} else {

stopSelf();

}

}

}

This example illustrates how to use Service.stopSelf() to ensure that your Service doesn’t use any

more resources than necessary. A good Android application needs to always release as many resources as

possible, as soon as possible.

120 Part II: Getting the Most Out of Components

Running in the Background

A Service can be running when your application isn’t in the foreground, but this doesn’t mean that it

won’t be executing any work on the main thread. Because all components’ lifecycle callbacks are executed on

the application’s main thread, you need to make sure that any long-running operation you perform in your

Service is moved to a new thread. (Refer to Chapter 2, where I explain how you can use a Handler or an

AsyncTask to launch an operation on the main thread. In this section, I cover two additional methods for

executing things on the main thread.)

IntentService

Using a Handler together with a Service has proven so efficient that Google implemented a utility class

named IntentService that wraps the background thread handling of a Handler in a Service. All you

need to do is extend the class, implement the onHandleIntent() method, and add the actions you want

the Service to be able to receive, as illustrated here:

public class MyIntentService extends IntentService {

private static final String NAME = “MyIntentService”;

public static final String ACTION_UPLOAD_PHOTO =

“com.aptl.services.UPLOAD_PHOTO”;

public static final String EXTRA_PHOTO = “bitmapPhoto”;

public static final String ACTION_SEND_MESSAGE =

“com.aptl.services.SEND_MESSAGE”;

public static final String EXTRA_MESSAGE = “messageText”;

public static final String EXTRA_RECIPIENT = “messageRecipient”;

public MyIntentService() {

super(NAME);

// We don’t want intents redelivered

// in case we’re shut down unexpectedly

setIntentRedelivery(false);

}

/**

* This method is executed on its own thread, one intent at a time...

*/

@Override

protected void onHandleIntent(Intent intent) {

String action = intent.getAction();

if(ACTION_SEND_MESSAGE.equals(action)) {

String messageText = intent.getStringExtra(EXTRA_MESSAGE);

String messageRecipient =

intent.getStringExtra(EXTRA_RECIPIENT);

sendMessage(messageRecipient, messageText);

} else if(ACTION_UPLOAD_PHOTO.equals(action)) {

Bitmap photo = intent.getParcelableExtra(EXTRA_PHOTO);

uploadPhoto(photo);

}

}

private void sendMessage(String messageRecipient,

String messageText) {

Chapter 6: Services and Background Tasks 121

// TODO Make network call...

// TODO Send a broadcast that operation is completed

}

private void uploadPhoto(Bitmap photo) {

// TODO Make network call...

// TODO Send a broadcast that operation is completed

}

}

The preceding example shows a class extending IntentService that handles two different actions,

one for uploading a photo and one for sending a message. Each action needs to be added to the intent

filter for the Service in the manifest as well. If you want to trigger an action, you simply call Context.

startService() with an Intent carrying the specific action and extras. Multiple calls will result in them

being queued up by the internal handler, so this class ensures that only one Intent is processed at any given

time. A Service based on the IntentService will be kept in started state until no more operations are

queued for processing.

Parallel Execution

The specialized Service class just described is very useful for most situations in which you simply want to

spawn a background operation and don’t care too much about when it starts. If you send five Intents to an

IntentService, they will be executed in sequential order, one at a time. This is usually a good practice but

can sometimes cause problems. To ensure that each background operation you spawn is executed as soon as

possible, you need some kind of parallel execution. Because the IntentService is built around a Handler

that has only one thread, you need to use some other thread mechanism to handle this kind of situation.

As I describe in Chapter 2, you can set up an AsyncTask to use an Executor for spawning instances in

parallel. However, because AsyncTask is designed for operations running only a few seconds at most, you

may need to do some more work if your operations are running for a significant amount of time.

The following example shows the stub for a Service used for transcoding media to a new format (for instance,

WAV to MP3). I left out the actual transcoding step here and focused on the use of the ExecutorService

API for setting up parallel execution. In order to ensure that the Service is kept alive even if the application

is not in the foreground, you call Service.startForeground() on the Service. Because Service.

startForeground() and Service.stopForeground() don’t stack, you need to maintain an internal

counter of how many active jobs you have and call Service.stopForeground() once the counter reaches

0 again.

public class MediaTranscoder extends Service {

private static final int NOTIFICATION_ID = 1001;

public static final String ACTION_TRANSCODE_MEDIA =

“com.aptl.services.TRANSCODE_MEDIA”;

public static final String EXTRA_OUTPUT_TYPE = “outputType”;

private ExecutorService mExecutorService;

private int mRunningJobs = 0;

private final Object mLock = new Object();

122 Part II: Getting the Most Out of Components

private boolean mIsForeground = false;

public IBinder onBind(Intent intent) {

return null;

}

@Override

public void onCreate() {

super.onCreate();

mExecutorService = Executors.newCachedThreadPool();

}

@Override

public int onStartCommand(Intent intent, int flags, int startId) {

String action = intent.getAction();

if(ACTION_TRANSCODE_MEDIA.equals(action)) {

String outputType = intent.getStringExtra(EXTRA_OUTPUT_TYPE);

// Start new job and increase the running job counter

synchronized (mLock) {

TranscodeRunnable transcodeRunnable =

new TranscodeRunnable(intent.getData(), outputType);

mExecutorService.execute(transcodeRunnable);

mRunningJobs++;

startForegroundIfNeeded();

}

}

return START_NOT_STICKY;

}

@Override

public void onDestroy() {

super.onDestroy();

mExecutorService.shutdownNow();

synchronized (mLock) {

mRunningJobs = 0;

stopForegroundIfAllDone();

}

}

public void startForegroundIfNeeded() {

if(!mIsForeground) {

Notification notification = buildNotification();

startForeground(NOTIFICATION_ID, notification);

mIsForeGround = true;

}

}

private Notification buildNotification() {

Notification notification = null;

// TODO Build the notification here...

return notification;

}

Chapter 6: Services and Background Tasks 123

private void stopForegroundIfAllDone() {

if(mRunningJobs == 0 && mIsForeground) {

stopForeground(true);

mIsForeground = false;

}

}

private class TranscodeRunnable implements Runnable {

private Uri mInData;

private String mOutputType;

private TranscodeRunnable(Uri inData, String outputType) {

mInData = inData;

mOutputType = outputType;

}

@Override

public void run() {

// TODO Perform transcoding here...

// Decrease counter when we’re done...

synchronized (mLock) {

mRunningJobs--;

stopForegroundIfAllDone();

}

}

}

}

Because most of the boilerplate code for dealing with threads is done in the ExecutorService API, this

method for implementing parallel execution is recommended. Also, the ExecutorService shown in this

example won’t consume resources when not in use and will keep a cache of threads to minimize the creation of

new threads.

Communicating with Services

Once you know when to use a Service and how it’s executing, you need some way of communicating

with it from your other component. You can communicate with a Service in two ways: by using the

method Context.startService() or by using the Context.bindService() method. Context.

startService()delivers the Intent to the method Service.onStartCommand() where you

can trigger the background operation and later deliver the result back to the calling component through a

broadcast or some other means. Context.bindService()is a way to retrieve a Binder that can be used

to make synchronous method calls directly to the Service object. I start by describing how to communicate

with a Service through Intents.

Asynchronous Messaging with Intents

The IntentService example shown earlier in this chapter provides an easy-to-use one-way communication

between a component (usually an Activity) and the Service. But usually you want know the result of the

operation you start, so you need some way for the Service to report back once it completes its task. You can

124 Part II: Getting the Most Out of Components

do so several ways, but if you want to maintain the asynchronous behavior of the IntentService, the best

approach is to send a broadcast, which is just as simple as starting an operation in your IntentService.

You just need to implement a BroadcastReceiver that listens for the response. Figure 6-3 shows a simple

diagram of this sort of communication.

Figure 6-3 Diagram of asynchronous communication between an Activity, a Service, and a

BroadcastReceiver

The following code is the modified version of the uploadPhoto() method from the earlier example. Here

you send only a simple broadcast without any extras, but you’re free to use this method to send back a more

complex response as long as it can fit into an Intent.

private void uploadPhoto(Bitmap photo) {

// TODO Make network call...

sendBroadcast(new Intent(BROADCAST_UPLOAD_COMPLETED));

}

The advantage of this approach is that you have everything you need in Android and don’t need to build some

complex message handling system between your components. You just declare the actions that represent the

asynchronous messages and register them appropriately for each component. This approach works even if your

Service resides in a different application or runs in a separate process.

Chapter 6: Services and Background Tasks 125

The drawback to this solution is that you’re limited to what an Intent can carry. Also, you cannot use this

solution for multiple, fast updates between the IntentService and your Activity, such as progress

updates, because doing so will choke the system. If you need to do something like that, look at using a bound

Service instead (see the next section).

I go into the details of declaring and setting up BroadcastReceivers in Chapter 8.

Locally Bound Services

In an earlier example in this chapter, I showed how to bind to a Service in the same application using

the local binder pattern. This solution is very useful when your Service provides interfaces that are too

complicated to solve using only Intent messaging and where normal Java methods are easy to implement.

Another reason for binding to a local Service is that you can provide a more complex way of doing callback

from the Service back to the Activity. Because long-running operations still must be moved to a

background thread in the Service, most of the calls to the Service should be asynchronous in their design.

The actual call triggers a background operation and returns immediately. Once the operation is complete, the

Service uses a callback interface to notify the Activity about the result.

In the following example, I’ve modified the earlier example with a local Binder. I’ve added a callback interface

and a class implementing AsyncTask for performing the hypothetical background operation. The Service

returns a LocalBinder object in the onBind() method, from which the client can retrieve an object

reference to the Service object and call the method doLongRunningOperation(). This method creates

a new AsyncTask and executes it with the parameters sent from the client. During the run on the operation,

the callback instance is called to notify the client of the progress and eventually of the result.

public class MyLocalService extends Service {

private static final int NOTIFICATION_ID = 1001;

private LocalBinder mLocalBinder = new LocalBinder();

private Callback mCallback;

public IBinder onBind(Intent intent) {

return mLocalBinder;

}

public void doLongRunningOperation(MyComplexDataObject dataObject) {

new MyAsyncTask().execute(dataObject);

}

public void setCallback(Callback callback) {

mCallback = callback;

}

public class LocalBinder extends Binder {

public MyLocalService getService() {

return MyLocalService.this;

}

}

126 Part II: Getting the Most Out of Components

public interface Callback {

void onOperationProgress(int progress);

void onOperationCompleted(MyComplexResult complexResult);

}

private final class MyAsyncTask

extends AsyncTask<MyComplexDataObject, Integer,

MyComplexResult> {

@Override

protected void onPreExecute() {

super.onPreExecute();

startForeground(NOTIFICATION_ID, buildNotification());

}

@Override

protected void onProgressUpdate(Integer... values) {

if(mCallback != null && values.length > 0) {

for (Integer value : values) {

mCallback.onOperationProgress(value);

}

}

}

@Override

protected MyComplexResult doInBackground(MyComplexDataObject...

myComplexDataObjects) {

MyComplexResult complexResult = new MyComplexResult();

// Actual operation left out for brevity...

return complexResult;

}

@Override

protected void onPostExecute(MyComplexResult myComplexResult) {

if(mCallback != null ) {

mCallback.onOperationCompleted(myComplexResult);

}

stopForeground(true);

}

@Override

protected void onCancelled(MyComplexResult complexResult) {

super.onCancelled(complexResult);

stopForeground(true);

}

}

private Notification buildNotification() {

// Create a notification for the service..

return notification;

}

}

Chapter 6: Services and Background Tasks 127

Also, the AsyncTask implemented will call startForeground() and stopForeground(), respectively

to make sure the Service is kept alive until the long-running operation is finished, even if no clients are

bound to the Service. (It’s outside the scope of this example to keep a count of how many times the method

is called, as shown in the earlier example with parallel execution.)

The following code shows how the updated Activity looks. The noticeable change comes from

implementing the MyLocalService.Callback interface on the Activity. When you receive a reference

to the Service in onServiceConnected(), you can set the callback to the Service object so that you

receive notifications when a long-running operation is running. Here it becomes very important to remove the

callback (that is, set it to null) when the user leaves the Activity and onPause() is called, or you will leak

memory.

public class MyActivity extends Activity

implements ServiceConnection, MyLocalService.Callback {

private MyLocalService mService;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

}

@Override

protected void onResume() {

super.onResume();

Intent bindIntent = new Intent(this, MyLocalService.class);

bindService(bindIntent, this, BIND_AUTO_CREATE);

}

@Override

protected void onPause() {

super.onPause();

if (mService != null) {

mService.setCallback(null); // Important to avoid memory leaks

unbindService(this);

}

}

// Callback method assigned to onClick for the button in the UI

public void onTriggerLongRunningOperation(View view) {

if(mService != null) {

mService.doLongRunningOperation(new MyComplexDataObject());

}

}

@Override

public void onOperationProgress(int progress) {

// TODO Update user interface with progress..

}

128 Part II: Getting the Most Out of Components

@Override

public void onOperationCompleted(MyComplexResult complexResult) {

// TODO Show result to user...

}

@Override

public void onServiceConnected(ComponentName componentName,

IBinder iBinder) {

mService = ((MyLocalService.LocalBinder) iBinder).getService();

mService.setCallback(this);

// Once we have a reference to the service, we can update the UI

and

// enable buttons that should otherwise be disabled.

findViewById(R.id.trigger_operation_button).setEnabled(true);

}

@Override

public void onServiceDisconnected(ComponentName componentName) {

// Disable the button as we are losing the

// reference to the service.

findViewById(R.id.trigger_operation_button).setEnabled(false);

mService = null;

}

}

Also, in the onServiceConnected() and onServiceDisconnected() methods, you can update the

parts of the user interface that depend on the Service. In this case, you enable and disable the button that is

used to trigger the long-running operation.

If the user leaves the Activity (that is, presses Home or Back) before the operation is completed, the

Service still keeps running because startForeground() was called. If the Activity resumes before

a previously started operation is finished, it will receive the callbacks about the progress as soon as it is

successfully bound to the Service. This behavior makes it easy to separate a long-running task from the user

interface while still allowing your Activity to retrieve the current state once it resumes.

If your Service maintains some kind of internal state, it’s a good practice to allow clients (like Activities)

to retrieve the current state as well as subscribe (using a callback as just shown) to changes in the state because

the state may have changed when an Activity resumes and binds to the Service.

Summary

The Service class is a powerful component that can easily become very complicated to use. You discovered

two ways to start a Service and how the lifecycle is affected by the chosen method. You saw a few examples

of how to implement and control your Service to make sure that it can be safely disconnected and

reconnected with the user interface.

When it comes to Services in Android, my best advice is to have one Service for each type of operation

you want to perform that’s not related to the user interface. At times, you may start by implementing everything

Chapter 6: Services and Background Tasks 129

within an Activity and then later discover that doing so was a mistake that now requires a lot of work to

move all the operations to a Service in a less optimal way. I strongly recommend that you create Services

from the start in your project to avoid this problem. It’s easier to move operations from a Service to an

Activity than to do the opposite.

Also, remember to be careful about how you start your Services. When using Context.

startService(), you need to return the appropriate value from onStartCommand(). Returning the

wrong value can have unforeseen consequences when the system eventually shuts down your Service

because of lack of resources. Explicitly stopping your Service when it no longer has any operations to execute

is a good practice.

If you need bidirectional communication between your Service and an Activity, I recommend using

the local binder pattern and using a callback interface that your Activity can implement. Another way is to

use Intent messaging and let the Service send broadcasts when operations are done. The first approach

is more powerful and will allow you to do frequent progress updates from the Service to your Activity,

whereas the second method requires less code but isn’t as powerful.

Finally, remember that the lifecycle callbacks, like onCreate(), onStartCommand() and onDestroy(),

are all executing on the main thread of your application. This is the same main thread that your Activities

and other components use. Whenever you perform something that could potentially block the thread for more

than a few milliseconds, you should move it to a Handler or an AsyncTask.

Further Resources

Blogs

Google’s changes to the Service API at http://android-developers.blogspot.se/2010/02/

service-api-changes-starting-with.html

Dianne Hackborn at http://android-developers.blogspot.se/2010/04/multitasking-

android-way.html

Chapter 7

Android IPC

Android has a powerful feature that’s capable of communicating between two different applications. You can

set up this communication in many ways in your code, but there is one central mechanism behind the scenes

that handles all inter-process communication, the Binder IPC ( I nter- P rocess C ommunication).

The Binder in Android has a long history. It was originally developed as the OpenBinder at Be Inc. for the Be

Operating System (BeOS) under the leadership of Dianne Hackborn. It was ported and later rewritten for

Android in order to support IPC for applications. Basically, the Binder provides the features for binding functions

and data between one execution environment and another. Because each Android application runs in its own

Dalvik VM, which is an isolated execution environment, the Binder is very suitable for this purpose.

Back in 2009, there was a long debate in the Linux community about why Google chose to use

the Binder for IPC rather than the existing solution in the Linux kernel named dbus. The simplest

explanation is likely that Dianne Hackborn, one of the lead Android framework engineers, was also

the one leading the development of the OpenBinder at Be Inc. When Android was first developed,

this was their best choice for IPC, and today it’s an integral part of the Android system. The dbus

mechanism from Linux is also used on many Android devices, specifically for communication with the

Radio Interface Layer (RIL) and for Bluetooth up until Android 4.3. However, most IPC calls on Android

go through the Binder.

In addition to being used for communication between Android applications, the Binder is actually essential in

order for an application to communicate with the Android system. When you retrieve a system Service using

the Context.getSystemService() method, the Binder is working behind the scenes to provide your

application with a wrapper for a Service object. The Binder isn’t just used by Services, it also handles all

communication between Android components and the Android system.

Normally, an Android application doesn’t have to contend with the low-level details of the Binder because the

Android APIs provide nice wrappers that make it easy to perform IPC. In this chapter, I describe how the Binder

works and provide a few examples that show you how to build remote APIs for other applications.

The Binder Explained

As I mentioned in the introduction, the Binder in Android was originally designed under the name “OpenBinder

for BeOS” and not Linux, which is the kernel Android runs on. In earlier versions of Android, essentially the same

code used for OpenBinder was used for the Linux kernel driver that implemented the Binder for Android. This

was less than optimal because the architecture from BeOS is very different from the architecture found in Linux.

In later versions of Android, Google rewrote the implementation so that now it’s better suited for the Linux

kernel architecture.

132 Part II: Getting the Most Out of Components

When two applications communicate using the Binder IPC, they’re using this kernel driver to relay messages

(see Figure 7-1) between them. Besides the messaging function, the Binder provides additional functions such

as identifying the remote caller (process ID and user ID) and notifying when a remote process dies (called link

to death).

Conceptual

function call

Application 1

Application 2

Binder

communication

path

Binder Driver

Linux Kernel

Figure 7-1 Simple diagram illustrating communication

using the Binder IPC

For example, the system uses these additional functions in the Binder when the system Service, which

manages all windows in Android through the WindowManager, keeps a Binder reference to every

application and is notified through a link-to-death notification when an application’s window closes.

Communication using the Binder follows the client-server model. Clients use a client-side proxy to handle the

communication with the kernel driver. On the server-side, the Binder framework maintains a number of Binder

threads. The kernel driver delivers the messages from the client-side proxy to the receiving object using one

of the Binder threads on the server-side. This is important to remember because when you receive calls to a

Service through the Binder, they will not be executed on the main thread of your application. That way, a

client to a remote Service cannot block the Service application’s main thread.

You implement the Binder in Android by using the base class Binder and the interface IBinder. As I show

in Chapter 6, a Service can return a class implementing the IBinder interface in the method Service.

onBind(). When the Service publishes a remote API, you generally use an AIDL file to generate this

IBinder class, but as I describe next, other methods are available as well.

Binder Address

Communicating over the Binder requires that the client know the address of the remote Binder object.

However, the design of the Binder is such that only the implementation, like the Service you want to call,

knows its address. You address on the Android API level by using Intent resolution. The client constructs

an Intent object, using either an action String or a ComponentName and then uses that to initiate

communication with the remote application. However, an Intent is only an abstraction of the actual Binder

address and needs to be translated in order to set up the communication.

Chapter 7: Android IPC 133

A special Binder node called ServiceManager that is running inside the Android system server manages

all address resolution in Android. This is the only Binder node that has a globally known address. Because all

components in Android use the Binder for communication, they need to register using the ServiceManager,

which they reach through the well-known address (see Figure 7-2).

3. Service communication

Service

Client

1. addService()

1. getService()

Service

Manager

Figure 7-2 Diagram showing service registration and

lookup through the ServiceManager

Clients that want to communicate with a Service or other component query the ServiceManager,

implicitly through the Intent resolution, to receive the Binder address.

Binder Transactions

When one process sends data to another in Android, it’s called a transaction. You start transactions on the

Binder by calling IBinder.transact() on the client, and the Service receives the call on the method

Binder.onTransact(), as shown here:

public String performCustomBinderTransaction(IBinder binder, String arg0,

int arg1, float arg2)

throws RemoteException {

Parcel request = Parcel.obtain();

Parcel response = Parcel.obtain();

// Populate request data...

request.writeString(arg0);

request.writeInt(arg1);

request.writeFloat(arg2);

// Perform transaction

binder.transact(IBinder.FIRST_CALL_TRANSACTION, request, response, 0);

// Read the result from the response…

String result = response.readString();

// Recycle the objects

request.recycle();

response.recycle();

return result;

}

134 Part II: Getting the Most Out of Components

The method in the preceding example illustrates how from the client-side, once you have a valid IBinder reference,

you can perform a custom Binder transaction toward the Service. I explain the Parcel objects in detail in the

following example. They are used as simple data containers for the data you want to include in the transaction.

public class CustomBinder extends Binder {

@Override

protected boolean onTransact(int code, Parcel request,

Parcel response, int flags)

throws RemoteException {

// Read the data in the request

String arg0 = request.readString();

int arg1 = request.readInt();

float arg2 = request.readFloat();

String result = buildResult(arg0, arg1, arg2);

// Write the result to the response Parcel

response.writeString(result);

// Return true on success

return true;

}

private String buildResult(String arg0, int arg1, float arg2) {

String result = null;

// TODO Build the result

return result;

}

}

If you implement a custom Binder object in your Service without using an AIDL, you need to implement

the method Binder.onTransact() as just shown. Here you simply respond to the incoming transaction by

populating the second Parcel object with the relevant data.

The result is a synchronized two-way call through the Binder IPC. You can also perform a one-way call from the

client by setting the flag in the IBinder.transact() call to FLAG_ONEWAY, in which case, you can leave

the second Parcel argument as null. Doing so provides better performance for this call because it needs to

marshal and unmarshal only one Parcel object.

Using this low-level way of performing transactions between two applications is not recommended if you

intend to publish an API for other developers to use. However, when you need fine-grained control of how

data is sent between two applications, this can be an efficient method to use. I share it here to illustrate how

the Binder works on its basic level. Most of the time, you’ll use either AIDL or a Messenger as described in the

“Messenger” section, later in this chapter.

Parcel

A Binder transaction will usually carry some transaction data, as shown in the previous example. This data is

called a parcel, and there is an API for developers, which allows you to create a parcel for most Java objects.

Chapter 7: Android IPC 135

You can compare parcels in Android with serializable objects in Java SE. The difference is that you need to

implement the marshaling and unmarshaling of objects yourself using the Parcelable interface. This

interface defines two methods you need to implement for writing an object to a Parcel and also a static

final Creator object that implements the code for reading the object from a Parcel, as shown here:

public class CustomData implements Parcelable {

public static final Parcelable.Creator<CustomData> CREATOR

= new Parcelable.Creator<CustomData>() {

@Override

public CustomData createFromParcel(Parcel parcel) {

CustomData customData = new CustomData();

customData.mName = parcel.readString();

customData.mReferences = new ArrayList<String>();

parcel.readStringList(customData.mReferences);

customData.mCreated = new Date(parcel.readLong());

return customData;

}

@Override

public CustomData[] newArray(int size) {

return new CustomData[size];

}

};

private String mName;

private List<String> mReferences;

private Date mCreated;

public CustomData() {

mName = “”; // Defaults to empty string

mReferences = new ArrayList<String>();

mCreated = new Date(); // Defaults to now

}

@Override

public int describeContents() {

return 0;

}

@Override

public void writeToParcel(Parcel parcel, int flags) {

parcel.writeString(mName);

parcel.writeStringList(mReferences);

parcel.writeLong(mCreated.getTime());

}

@Override

public boolean equals(Object o) {

if (this == o) return true;

if (o == null || getClass() != o.getClass()) return false;

136 Part II: Getting the Most Out of Components

CustomData that = (CustomData) o;

return mCreated.equals(that.mCreated) && mName.equals(that.mName);

}

@Override

public int hashCode() {

int result = mName.hashCode();

result = 31 * result + mCreated.hashCode();

return result;

}

}

The preceding code shows an object that implements the Parcelable interface. Note the implementation of

the CREATOR field and how the createFromParcel() method uses the Parcel.readStringList()

method to read the entire List object without having to specify how long the list is (this is handled internally

by the Parcel object).

After you implement this interface, you can send objects of this class between applications through the Binder

IPC.

Link to Death

Another feature of the Binder in Android is that it allows clients to be notified when a Service is terminated.

As I mentioned earlier, this is called link to death, and it’s implemented through the Binder method IBinder.

linkToDeath(). When a client receives an IBinder object in the onServiceConnected() method, the

client can call linkToDeath() with a callback implementing the interface IBinder.DeathRecipient.

Because Android applications can be killed by the system when it’s running low on resources (available RAM,

and so on), it can be useful to register for these notifications in a client in case it wants to be notified when the

remote side is terminated. The following code shows how to register for link to death once you receive a valid

IBinder reference:

public class LinkToDeathSample extends Service {

private static final String TAG = “LinkToDeathSample”;

// Service methods exclude for brevity...

private void notifyRemoteServiceDeath(IBinder iBinder) {

try {

iBinder.linkToDeath(new MyLinkToDeathCallback(), 0);

} catch (RemoteException e) {

Log.e(TAG, “Error registering for link to death.”, e);

}

}

class MyLinkToDeathCallback implements IBinder.DeathRecipient {

@Override

public void binderDied() {

// TODO Handle death of remote binder...

}

}

}

Chapter 7: Android IPC 137

You can also check whether the process for the remote Binder is still alive by calling IBinder.

pingBinder(). If that call returns true, the process is alive and ready.

If you’re binding to a Service, this method is not necessary because you’ll always have the

ServiceConnection.onServiceDisconnected() callback to notify you when you lose your

connection. However, if you received a Binder object some other way, this method can be useful.

Designing APIs

Most applications will rarely need to implement an API for third-party applications because doing so is outside

the scope of their features. However, it does become relevant with the types of applications that provide a plug-

in mechanism. If you search for “plugin” at the Google Play Store, you’ll find tons of examples of these types of

applications. If your application fits this category, you’ll probably benefit from preparing an API for third-party

applications.

An API for third-party applications can be either implemented as a Service or as a ContentProvider. In

this section, I describe how to do so using a Service; I show how to use a ContentProvider in Chapter 9.

When implementing an API, you need to consider a number of things. Do you need to handle concurrent

requests, or is it enough to process one client request at a time? Will you publish only one or very few

operations, or is it a more complex set of API methods that clients can use? The answer to these questions will

determine the most appropriate method for implementing your remote API.

Another detail to consider is whether you’ll be sharing this API with other developers or if it will be used only by

your own applications (that is, only you will be publishing plug-ins). In the first case, consider building a library

project that wraps the client-side implementation in an easy-to-use Java API. If you’re the only user of the API, it

is probably safe to use either the AIDL or the Messenger directly as described in the next two sections.

If it’s enough that your API is one-way, you’re probably fine with using an IntentService as I describe in

Chapter 6. In that case, you just add the necessary permissions and make sure that the API is exported in the

manifest.

AIDL

In software engineering, the term Interface Definition Language (IDL) has become the generic term for a

specification language that describes the interface for a software component. In Android, the IDL is called

Android Interface Definition Language (AIDL) and is written in text files with a Java-like syntax. However, you

need to consider a number of differences between writing AIDL files and writing a Java interface.

First, for all non-primitive parameters, you need to specify one of three directional types: in, out, or inout.

The in type indicates that they are used only for input and that your client won’t see any changes that the

Service does to this object. The out type indicates that the input object contains no relevant data but will

be populated with data by the Service that’s relevant in the response from the method. The inout type

is a combination of both types. It’s very important to use only the type that’s needed because there’s a cost

associated with each type.

138 Part II: Getting the Most Out of Components

Another thing to remember is that for all custom classes used in communication, you need to create an AIDL file

that declares your class as a Parcelable.

The following code snippet is an example of an AIDL file with the name CustomData.aidl. It should be

placed in the same package as the Java class source file.

package com.aptl.sampleapi;

parcelable CustomData;

Finally, all custom classes you need for your API must be imported in the AIDL file for the API, as shown here:

package com.aptl.sampleapi;

import com.aptl.sampleapi.CustomData;

interface ApiInterfaceV1 {

/**

* Simple remote method for checking if a number is a prime.

*/

boolean isPrime(long value);

/**

* Retrieve all CustomData objects since timestamp.

* Will get at most result.length objects.

*/

void getAllDataSince(long timestamp, out CustomData[] result);

/**

* Stores the CustomData object.

*/

void storeData(in CustomData data);

}

This is an example of an AIDL file with three methods. Note: Primitives don’t need a directional tag (they’re

always called by value).

Remember, after you’ve implemented a client, you cannot change or remove any methods that you’ve put in

an AIDL. You can add new methods at the end of the file, but because of the way the AIDL compiler generates

the identifier for each method, you cannot change any of the existing methods without breaking backward

compatibility. When handling new versions of the API, the recommended way is to create a new AIDL file with

the new or changed methods. Doing so allows you to maintain backward compatibility with older clients. As

you can see by the name of the preceding AIDL file, you handle versioning of AIDL by appending V1 for the first

version of the file. When you add new methods to the API, you create a file by ending with V2, and so on.

This method for versioning is one of the drawbacks with using AIDL files. One way to manage this issue is to

provide a Java wrapper around the AIDL and you publish this either as a library project or as a JAR file that

developers can use. This way, a client won’t have to implement multiple AIDLs but can always download the

latest version of your wrapper and be sure that it’s compatible. I show an example of how to create such a

wrapper in section “Wrapping APIs with Library Projects,” later in this chapter.

Chapter 7: Android IPC 139

When you have an AIDL file ready, you need to implement it on both the service-side and the client-side, as

shown here:

public class AidlService extends Service {

private ArrayList<CustomData> mCustomDataCollection;

@Override

public void onCreate() {

super.onCreate();

mCustomDataCollection = new ArrayList<CustomData>();

// TODO Populate the list with stored values...

}

public IBinder onBind(Intent intent) {

return mBinder;

}

public static boolean isPrimeImpl(long number) {

// Implementation left out for brevity...

return false;

}

private void getDataSinceImpl(CustomData[] result, Date since) {

int size = mCustomDataCollection.size();

int pos = 0;

for(int i = 0; i < size && pos < result.length; i++) {

CustomData storedValue = mCustomDataCollection.get(i);

if(since.after(storedValue.getCreated())) {

result[pos++] = storedValue;

}

}

}

private void storeDataImpl(CustomData data) {

int size = mCustomDataCollection.size();

for (int i = 0; i < size; i++) {

CustomData customData = mCustomDataCollection.get(i);

if(customData.equals(data)) {

mCustomDataCollection.set(i, data);

return;

}

}

mCustomDataCollection.add(data);

}

private final ApiInterfaceV1.Stub mBinder

= new ApiInterfaceV1.Stub() {

@Override

public boolean isPrime(long value) throws RemoteException {

return isPrimeImpl(value);

}

140 Part II: Getting the Most Out of Components

@Override

public void getAllDataSince(long timestamp, CustomData[] result)

throws RemoteException {

getDataSinceImpl(result, new Date(timestamp));

}

@Override

public void storeData(CustomData data) throws RemoteException {

storeDataImpl(data);

}

};

}

The preceding example shows the implementation of the AIDL stub in the end on the Service. This object is

also what is returned to clients that bind to the Service in the onBind() method. Note that each call to the

API in the Service will be running on its own thread because the Binder provides a pool of threads on which

it executes calls from clients. This means that a client cannot block the main thread of the Service it is calling

when you’re using this method.

The following Activity shows how to bind to a remote Service and retrieve the interface for

ApiInterfaceV1. This is the preferred solution if you’re the sole user of the remote API and can manage the

versioning on both sides (or on the same development team).

public class MyApiClient extends Activity implements ServiceConnection {

private ApiInterfaceV1 mService;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

}

@Override

protected void onResume() {

super.onResume();

bindService(new Intent(“com.aptl.sampleapi.AIDL_SERVICE”),

this, BIND_AUTO_CREATE);

}

public void onCheckForPrime(View view) {

EditText numberToCheck = (EditText) findViewById(R.id.number_

input);

long number = Long.valueOf(numberToCheck.getText().toString());

boolean isPrime = mService.isPrime(number);

String message = isPrime ?

getString(R.string.number_is_prime, number)

: getString(R.string.number_not_prime, number);

Toast.makeText(this, message, Toast.LENGTH_SHORT).show();

}

Chapter 7: Android IPC 141

@Override

protected void onPause() {

super.onPause();

unbindService(this);

}

@Override

public void onServiceConnected(ComponentName componentName,

IBinder iBinder) {

mService = ApiInterfaceV1.Stub.asInterface(iBinder);

}

@Override

public void onServiceDisconnected(ComponentName componentName) {

mService = null;

}

}

Callbacks with AIDL

Clients can also implement an AIDL that can be used as a callback interface by the Service, which is useful

if you want to register clients to receive callbacks when something happens on the Service—for instance,

when data is updated from an online server that the Service is communicating with.

In the following example, you can see the new AIDL file for the callback interface, note the keyword oneway

that tells the AIDL compiler that this interface is only a one-way communication. No response back to the caller,

in this case the Service, is needed. This will give you a slight performance boost.

package com.aptl.sampleapi;

import com.aptl.sampleapi.CustomData;

oneway interface AidlCallback {

void onDataUpdated(in CustomData[] data);

}

Next, you create an instance of this interface in your client, shown as follows. In this case, you simply show a

Toast when you receive a callback from the Service:

private AidlCallback.Stub mAidlCallback = new AidlCallback.Stub() {

@Override

public void onDataUpdated(CustomData[] data) throws RemoteException {

Toast.makeText(MyApiClient.this, “Data was updated!”,

Toast.LENGTH_SHORT).show();

}

};

In the AIDL for the Service shown earlier, you add one more line for registering the callback:

void addCallback(in AidlCallback callback);

142 Part II: Getting the Most Out of Components

Finally, you implement the addCallback() method on the Service. Here, you also use the

linkToDeath() method to receive a notification in case the client Binder died.

@Override

public void addCallback(final AidlCallback callback) throws

RemoteException {

mCallbacks.add(callback);

callback.asBinder().linkToDeath(new DeathRecipient() {

@Override

public void binderDied() {

mCallbacks.remove(callback);

}

}, 0);

}

Normally, you should have both an addCallback() and a removeCallback() method, but I’m

leaving that as an exercise for you to explore.

The previous example shows how to create callback interfaces between applications. It also shows how

you can transfer a Binder object between two applications without having to register it through the

ServiceManager. Because only the client and the Service know the address for this Binder, it can

effectively be used as a security mechanism when doing IPC.

Messenger

Another way of providing a remote interface is through the Messenger class. This class is useful when you

have a Service where you don’t need to support concurrent operations to clients. The Messenger class uses

a Handler to execute each incoming message, so all client calls will run on the same thread in serial order. You

also get rid of the problems with AIDL files and can more easily provide an asynchronous message-based API

for clients. Although not as powerful, this class can be more efficient at times because you’ll get much easier

implementation, both for clients and Services.

The following example shows how to use the Messenger class to provide an asynchronous API. The

onBind() method returns the Binder object from the Messenger created in onCreate(). When

the Messenger receives a message, it can reply to the client using a Messenger object stored in the

replyTo field.

public class MessengerService extends Service {

private Handler mMessageHandler;

private Messenger mMessenger;

@Override

public void onCreate() {

super.onCreate();

HandlerThread handlerThread = new HandlerThread(“MessengerService”);

Chapter 7: Android IPC 143

handlerThread.start();

mMessageHandler = new Handler(handlerThread.getLooper(),

new MyHandlerCallback());

mMessenger = new Messenger(mMessageHandler);

}

public IBinder onBind(Intent intent) {

return mMessenger.getBinder();

}

@Override

public void onDestroy() {

super.onDestroy();

mMessageHandler.getLooper().quit();

}

private class MyHandlerCallback implements Handler.Callback {

@Override

public boolean handleMessage(Message message) {

boolean delivered = false;

switch (message.what) {

case MessageAPI.SEND_TEXT_MSG:

delivered = sendTextMessage((String) message.obj);

break;

case MessageAPI.SEND_PHOTO_MSG:

delivered = sendPhotoMessage((Bitmap) message.obj);

break;

}

Message reply = Message.obtain(null,

MessageAPI.MESSAGE_DELIVERED_MSG,

delivered);

try {

message.replyTo.send(reply);

} catch (RemoteException e) {

Log.e(“MessengerService”,

“Error sending message reply!”, e);

}

return true;

}

}

// Return true when delivered

private boolean sendPhotoMessage(Bitmap photo) {

// Implementation left out for brevity

return true;

}

// Return true when delivered

private boolean sendTextMessage(String textMessage) {

// Implementation left out for brevity

return true;

}

}

144 Part II: Getting the Most Out of Components

The following example shows a client that first binds to the Service and then constructs a new Messenger

object with the IBinder as a parameter. This now acts as a proxy for the Messenger running in the remote

Service. When you send a message to the Service, you can also set the replyTo field of the Message object.

public class MyMessengerClient extends Activity

implements ServiceConnection {

private ApiInterfaceV1 mService;

private Messenger mRemoteMessenger;

private Messenger mReplyMessenger;

private Handler mReplyHandler;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

HandlerThread handlerThread = new HandlerThread(“ReplyMessenger”);

handlerThread.start();

mReplyHandler = new Handler(handlerThread.getLooper(),

new ReplyHandlerCallback())

mReplyMessenger = new Messenger(mReplyHandler);

}

@Override

protected void onResume() {

super.onResume();

bindService(new Intent(“com.aptl.sampleapi.MESSENGER_SERVICE”),

this, BIND_AUTO_CREATE);

}

public void onSendTextPressed(View view) {

String textMessage = ((EditText) findViewById(R.id.message_input))

.getText().toString();

Message message = Message.obtain();

message.what = MessageAPI.SEND_TEXT_MSG;

message.obj = textMessage;

message.replyTo = mReplyMessenger;

try {

mRemoteMessenger.send(message);

} catch (RemoteException e) {

// Remote service is dead...

}

}

@Override

protected void onPause() {

super.onPause();

unbindService(this);

}

@Override

protected void onDestroy() {

super.onDestroy();

Chapter 7: Android IPC 145

mReplyHandler.getLooper().quit();

}

@Override

public void onServiceConnected(ComponentName componentName,

IBinder iBinder) {

mRemoteMessenger = new Messenger(iBinder);

}

@Override

public void onServiceDisconnected(ComponentName componentName) {

mRemoteMessenger = null;

}

private class ReplyHandlerCallback implements Handler.Callback {

@Override

public boolean handleMessage(Message message) {

switch (message.what) {

case MessageAPI.MESSAGE_DELIVERED_MSG:

// TODO Handle async reply from service

break;

}

return true;

}

}

}

This method is very similar to using the IntentService as I describe in Chapter 6, but instead of working

with Intent objects, here you’re utilizing the Message class used for triggering operations on a Handler,

as I describe in Chapter 2. Also, using a Messenger provides a convenient way of implementing asynchronous

communication without having to use BroadcastReceivers.

Wrapping APIs with Library Projects

Regardless of whether you use AIDL or the Messenger class to implement your remote API, it’s a good idea to

extract all the API-specific classes and interfaces to a library project and create a pure Java wrapper for clients

to use. Because you probably want to support your complex objects in your API, providing only an AIDL file for

your API is usually not enough. You also need to provide these custom classes to clients. As I describe in Chapter

1, when it comes to distribution and versioning, setting up an Android library project for your API is a simple

and efficient way of handling all the problems related to remote APIs. You can also package the compiled

wrapper code into a JAR file that is easily distributed as a third-party library. I recommend using an Android

library project, uploading it to an online version control service like GitHub, and letting other developers simply

use that code to integrate with your application.

The easiest way to set up a library project for your remote API is to move all AIDL files and Parcelable classes

to a library project that you reference in the application that implements your remote API. However, if you have

several AIDLs (new versions, client callbacks, and so on), it can easily become quite complicated, so it’s also a

good practice to wrap everything in a more easy-to-use Java class, as shown here:

146 Part II: Getting the Most Out of Components

public class ApiWrapper {

private Context mContext;

private ApiCallback mCallback;

private MyServiceConnectionV1 mServiceConnection =

new MyServiceConnectionV1();

private ApiInterfaceV1 mServiceV1;

public void release() {

mContext.unbindService(mServiceConnection);

}

public ApiWrapper(Context context, ApiCallback callback) {

mContext = context;

mCallback = callback;

mContext.bindService(new Intent(“com.aptl.sampleapi.AIDL_

SERVICE”),

mServiceConnection, Context.BIND_AUTO_CREATE);

}

public void getAllDataSince(long timestamp, CustomData[] result) {

if (mServiceV1 != null) {

try {

mServiceV1.getAllDataSince(timestamp, result);

} catch (RemoteException e) {

// TODO Handle service error

}

}

}

void storeData(CustomData data) {

if (mServiceV1 != null) {

try {

mServiceV1.storeData(data);

} catch (RemoteException e) {

// Handle service error

}

}

}

private class MyServiceConnectionV1 implements ServiceConnection {

@Override

public void onServiceConnected(ComponentName componentName,

IBinder iBinder) {

mServiceV1 = ApiInterfaceV1.Stub.asInterface(iBinder);

try {

mServiceV1.setCallback(mAidlCallback);

} catch (RemoteException e) {

// Handle service error...

}

mCallback.onApiReady(ApiWrapper.this);

}

Chapter 7: Android IPC 147

@Override

public void onServiceDisconnected(ComponentName componentName) {

mServiceV1 = null;

if(mCallback != null) {

mCallback.onApiLost();

}

}

}

private AidlCallback.Stub mAidlCallback = new AidlCallback.Stub() {

@Override

public void onDataUpdated(CustomData[] data)

throws RemoteException {

if(mCallback != null) {

mCallback.onDataUpdated(data);

}

}

};

public interface ApiCallback {

void onApiReady(ApiWrapper apiWrapper);

void onApiLost();

void onDataUpdated(CustomData[] data);

}

}

The preceding code shows how to create a wrapper for the AIDL examples shown earlier in this chapter. This

method creates a much easier interface for your Service to the client. You can even manage AIDL callbacks by

wrapping them in ordinary Java interfaces as shown with the preceding ApiCallback.

This method lets you use the standard Java approach for version-controlling your API. You can add the @

deprecated tag to methods, and you can add new methods to your wrapper that handle the versioning

of the API behind the scenes. Clients will not have to worry about these details, and you can easily maintain

backward compatibility.

You can implement different versions of your API on the Service by returning different IBinder objects

depending on the contents of the Intent used in Context.bindService(), as shown here:

public IBinder onBind(Intent intent) {

int apiVersionRequested = intent.getIntExtra(EXTRA_VERSION_TAG, 1);

switch (apiVersionRequested) {

case 1:

return mBinderV1;

case 2:

return mBinderV2;

case 3:

return mBinderV3;

default:

return null;

}

}

148 Part II: Getting the Most Out of Components

The preceding example shows how you can retrieve an int from the Intent to decide which version of the

API to return. This method allows you to create new AIDL files for updates to your API. Your wrapper will now

bind to each version and keep one local reference for every binding.

Securing Remote APIs

Security should always be a priority when you’re designing Android applications, regardless of what you’re

doing. When providing APIs between applications security becomes even more important. (I go into security

for Android applications in more detail in Chapter 12.) Luckily, securing your published Services, and other

components, is quite easy, as shown here:

<?xml version=”1.0” encoding=”utf-8”?>

<manifest xmlns:android=”http://schemas.android.com/apk/res/android”

package=”com.aptl.sampleapi”>

<permission android:name=”com.aptl.sampleapi.CALL_SERVICE”

android:protectionLevel=”normal”/>

<uses-sdk

android:minSdkVersion=”17”

android:targetSdkVersion=”17”/>

<application

android:icon=”@drawable/icon”

android:label=”@string/app_name”>

<service

android:name=”.AidlService”

android:exported=”true”

android:permission=”com.aptl.sampleapi.CALL_SERVICE” >

<intent-filter>

<action android:name=”com.aptl.sampleapi.AIDL_SERVICE”/>

</intent-filter>

</service>

</application>

</manifest>

This XML is an example of how the AndroidManifest.xml file might appear for a Service that you

publish. The important areas are shown in bold. First, you need to set the attribute android:exported to

true. The default value for this attribute depends on how you define the intent-filter for the Service.

If you don’t include an intent-filter, the Service is only for internal use (addressed through its

component name), and it won’t be exported. If you define an intent-filter, the Service is exported

by default. I highly recommend that you always define this attribute and set the value according to your needs,

whether or not it’s an exported Service.

If you’re exporting a Service, the most important part is to set up permissions. I go into detail about defining

permissions in Chapter 12, but the previous example shows the simplest form. You define the permission above

the application tag and give it a protectionLevel. Next, you set the android:permission attribute for

the Service to declare that clients for this Service must declare this permission in their manifest.

Chapter 7: Android IPC 149

It’s usually enough to declare permissions as shown in the previous code block, but sometimes you need to go

beyond Android’s permission management. In Chapter 12, I discuss more advanced methods for securing your

application that also apply to APIs that you’ll publish.

Summary

In this chapter, you discovered how to use the Service component in Android to provide a remote API for

other applications to use. You are now familiar with how the Binder IPC works in Android and the choices you

have when it comes to implementing a remote API.

As I discussed, AIDL is a powerful but complicated method that requires more consideration when designing. It

allows you to do normal synchronous Java method calls across applications in different processes, but you need

to carefully consider how to design your API and think about versioning.

Also, using the Messenger class is an easy way to create an asynchronous remote API, but it’s also limited

because all client calls will be running on a single thread, as opposed to the AIDL approach where you have one

thread for every client. That said, the message-based approach will usually perform better than AIDL, so many

times this approach is preferable.

In addition, I recommended that you provide an Android library project that wraps your remote API in a more

easy-to-use set of Java classes, especially if you will use the AIDL approach. Doing so also makes it easier for you

to handle new versions of the API while maintaining backward-compatibility with older clients.

Finally, pay extra attention to securing your remote API. Declare permissions properly and make sure that only

the components that should be published have the android:exported flag set to true in the manifest.

Further Resources

Websites

“Android Interprocess Communication” by Thorsten Schreiber at www.nds.rub.de/media/

attachments/files/2012/03/binder.pdf

“Android IPC Mechanism” by Jim Huang at http://0xlab.org/~jserv/android-binder-ipc.

pdf

“Deep Dive into Android IPC/Binder Framework” by Aleksandar Gargenta at http://marakana.

com/s/post/1340/Deep_Dive_Into_Binder_Presentation.htm

Android Developers Blog. “Service API Changes Starting with Android 2.0” by Dianne Hackborn at

http://android-developers.blogspot.se/2010/02/service-api-changes-

starting-with.html

A summary of using Binder by Dianne Hackborn at https://lkml.org/lkml/2009/6/25/3

Chapter 8

Mastering

BroadcastReceivers and

Configuration Changes

An Android-based smartphone is a very powerful device with lots of different hardware components. Many of

these components affect the state of the smartphone in different ways. The accelerometer detects the current

physical orientation of the device (portrait or landscape), the Wi-Fi discovers new available networks and

notifies the system when its connection to a network changes, the light-sensor controls the brightness of the

screen, and the hardware buttons on the device trigger interrupts that generate some event in the system.

At the same time, the Android system always tries to consume as little power as possible in order to make

the battery last as long as possible. However, because Android applications have a relatively high degree of

freedom as to how much they can control the device, it’s very important for you, as a developer, to react to the

different events and changes to the device; otherwise, they could cause unnecessary power drain. By mastering

the different system events and device changes, you can make your application more robust and work more

smoothly with the overall system. A badly written application that eats up a user’s batteries will receive bad

reviews, and ultimately users will seek better alternatives.

Also, most of these events are not directly related to battery consumption or performance. However, you need

to be aware of them in order to anticipate changes that will affect how your device is working. For instance,

if the device loses its Wi-Fi connection and switches to the much slower EDGE connection, you may want to

reduce the number of network calls or pick a smaller version of pictures you’re downloading. By watching for

events indicating if the screen changes from on to off (or vice versa), you can make additional assumptions

about a user’s activity and take appropriate actions based on that (for instance, screen off would probably mean

that the user isn’t actively looking at the device).

As an application developer, you can also send broadcast, either defined by the application as a new Intent

action or by using some action defined by the Android APIs or third-party application. Doing so can be very

useful when you want to dispatch events in the background, either within your own application or between

two different applications.

Although an application can listen to numerous events, I cover only a few of them in this chapter

because there are so many defined by the Android platform. For more information about broadcast

events, go to the official Android documentation site: http://d.android.com, and check out the

relevant APIs. For instance, events related to telephony features are usually found in the android.

telephony package.

152 Part II: Getting the Most Out of Components

There are also different ways to listen for these events. Some are sent as broadcast Intents and received

by BroadcastReceivers; others require you to implement some Java callback. In this chapter, I provide

examples of these methods and how you can efficiently implement them.

BroadcastReceivers

The most common way that events are broadcast on Android is through Intent objects sent to

BroadcastReceivers using the Context.sendBroadcast() methods. Many of the standard system

events are defined as action strings and can be found in the API documentation for the Intent class. For

instance, if your application needs to be notified whenever the user connects or disconnects the charger to the

smartphone, two broadcast actions defined in the Intent class do that: ACTION_POWER_DISCONNECTED

and ACTION_POWER_CONNECTED.

The following code shows a simple BroadcastReceiver that receives an Intent object whenever the user

connects or disconnects the power. In this method, the only thing you do is call Context.startService()

to delegate the event to a service that performs the actual work.

public class ChargerConnectedListener extends BroadcastReceiver {

public void onReceive(Context context, Intent intent) {

String action = intent.getAction();

if (Intent.ACTION_POWER_CONNECTED.equals(action)) {

context.startService(

new Intent(MyService.ACTION_POWER_CONNECTED));

} else if (Intent.ACTION_POWER_DISCONNECTED.equals(action)) {

context.startService(

new Intent(MyService.ACTION_POWER_DISCONNECTED));

}

}

}

The default method for implementing BroadcastReceivers is to declare them in the manifest.

Consequently, it’s possible for the BroadcastReceiver to notify your service even though the user hasn’t

started your application. This factor is especially useful for applications that should start on certain system

events without user interaction. You could use the following approach to be notified whenever connectivity to

Wi-Fi changes. For example, when the user comes home and connects her device to the home Wi-Fi, you could

design your application so that it starts to sync data with other devices she has connected to the same network,

without her having to manually trigger this action.

<receiver android:name=”.ChargerConnectedListener”>

<intent-filter>

<action

android:name=”android.intent.action.ACTION_POWER_CONNECTED” />

<action

android:name=”android.intent.action.ACTION_POWER_DISCONNECTED” />

</intent-filter>

</receiver>

Chapter 8: Mastering BroadcastReceivers and Configuration Changes 153

BroadcastReceivers can also be registered programmatically within Activities and Services. Some

broadcast Intents can only be registered programmatically, and some only work if you declare them in your

manifest. Check the official Android API documentation for details on each action.

When you register a BroadcastReceiver programmatically, as shown in the following example, you must

also unregister it in the matching callback. In the this example, the receiver is registered for the two actions in

onResume() therefore, you unregister it in onPause().

public class MyActivity extends Activity {

private ChargerConnectedListener mPowerConnectionReceiver;

@Override

protected void onResume() {

super.onResume();

IntentFilter intentFilter = new IntentFilter();

intentFilter.addAction(Intent.ACTION_POWER_CONNECTED);

intentFilter.addAction(Intent.ACTION_POWER_DISCONNECTED);

mPowerConnectionReceiver = new ChargerConnecedListener();

registerReceiver(mPowerConnectionReceiver, intentFilter);

}

@Override

protected void onPause() {

super.onPause();

unregisterReceiver(mPowerConnectionReceiver);

}

}

You’ll usually want to programmatically register BroadcastReceivers when you’re only interested in the

events while your application is running and active. This way, your application will consume fewer resources

than if you declare them in your manifest and they start every time the event occurs. When your application

needs to receive system events, always consider the difference between a BroadcastReceiver registered in

the manifest or through Context.registerReceiver() so that you avoid using any more resources than

necessary.

Local BroadcastReceivers

If you want to send and receive broadcast only within your own application’s process, consider using the

LocalBroadcastManager instead of the more generic Context.sendBroadcast() method.

This approach is more efficient because no cross-process management is included and you don’t have to

consider the security issues normally involved with broadcasts. This class is not part of the standard APIs but

is contained in the support APIs. The following code exemplifies how to send a local broadcast using the

LocalBroadcastManager.

public void sendLocalBroadcast(Intent broadcastIntent) {

LocalBroadcastManager localBroadcastManager =

LocalBroadcastManager.getInstance(this);

localBroadcastManager.sendBroadcast(broadcastIntent);

}

154 Part II: Getting the Most Out of Components

To receive a local broadcast, you use the LocalBroadcastManager shown in the preceding code. The following

example shows a simple Activity where you register and unregister for local broadcasts for a certain action.

public class LocalBroadcastDemo extends Activity {

public static final String LOCAL_BRODCAST_ACTION = “localBroadcast”;

private BroadcastReceiver mLocalReceiver;

@Override

protected void onResume() {

super.onResume();

LocalBroadcastManager localBroadcastManager =

LocalBroadcastManager.getInstance(this);

IntentFilter intentFilter = new IntentFilter(LOCAL_BRODCAST_

ACTION);

mLocalReceiver = new BroadcastReceiver() {

@Override

public void onReceive(Context context, Intent intent) {

// TODO Handle local broadcast...

}

};

localBroadcastManager.registerReceiver(mLocalReceiver,

intentFilter);

}

@Override

protected void onPause() {

super.onPause();

LocalBroadcastManager localBroadcastManager =

LocalBroadcastManager.getInstance(this);

localBroadcastManager.unregisterReceiver(mLocalReceiver);

}

}

Local broadcast is a convenient way of broadcasting messages and states within your application. Local

broadcast is more efficient than standard global broadcasts and is more secure because you can be certain that

no data sent this way will leak outside your application. Remember to always unregister these local receivers in

the same way you do normal receivers, or you may leak memory.

Normal and Ordered Broadcasts

Broadcasts are divided into two categories, normal and ordered. The normal broadcasts are sent to all receivers

asynchronously and are received in an unspecified order, as shown in Figure 8-1. This method is more efficient

but lacks some of the advanced features found for ordered broadcasts. No feedback can be sent to the

broadcaster with normal broadcasts.

An ordered broadcast is delivered to the registered receivers one at a time in a specified order (see Figure 8-2).

You can control the order in which the broadcasts are received by setting the android:priority attribute

for the relevant intent-filter tag in the manifest. Another feature of ordered broadcasts is that by using

abortBroadcast(), setResultCode() and setResultData(), a receiver can set a result that is

delivered back to the broadcaster or abort the broadcast so that the Intent won’t be propagated to the next

receiver in the queue.

Chapter 8: Mastering BroadcastReceivers and Configuration Changes 155

Broadcaster

Receiver A

Receiver B

Receiver C

Receiver D

Figure 8-1 Diagram illustrating the asynchronous normal

broadcasts to multiple receivers

Broadcaster

Receiver A

Receiver B

Receiver C

Receiver D

Figure 8-2 Diagram illustrating the sequential propagation

of ordered broadcasts

The following code shows the implementation of a receiver that expects an ordered broadcast. You start by

checking that the broadcast is in fact ordered and then assign the result code, result data and any extras you

want to deliver to the broadcaster. After the onReceive() method returns, the response will be sent back to

the broadcaster automatically.

public class OrderedReceiver extends BroadcastReceiver {

public void onReceive(Context context, Intent intent) {

if(isOrderedBroadcast() {

setResultCode(Activity.RESULT_OK);

setResultData(“simple response string”);

// Get current response extras, or create new if null.

Bundle resultExtras = getResultExtras(true);

// Set our component name for the extras response...

resultExtras.putParcelable(“componentName”,

new ComponentName(context,getClass()));

}

}

}

In the following code, you see how to send an ordered broadcast and how responses are handled.

You can register for responses by passing a BroadcastReceiver object to the method Context.

sendBroadcast(). This receiver will then get a call to onReceive() for every receiver of the original

ordered broadcast.

public void sendOrderedBroadcastAndGetResponse() {

Intent intent = new Intent(ACTION_ORDERED_MESSAGE);

// The broadcast receiver that will handle responses

BroadcastReceiver responseReceiver = new BroadcastReceiver() {

156 Part II: Getting the Most Out of Components

@Override

public void onReceive(Context context, Intent intent) {

String resultData = getResultData();

Bundle resultExtras = getResultExtras(false);

if (resultExtras != null) {

ComponentName registeredComponent = resultExtras.

getParcelable(“componentName”);

}

// TODO Handle response

}

};

sendOrderedBroadcast(intent, responseReceiver, null,

RESULT_OK,null,null);

}

You rarely need to send an ordered broadcast in your own applications, but they can be helpful if you’ll be

communicating with other applications (for instance, plug-ins can have a great use for this). In the Android

system, the most common example of ordered broadcasts is when an application listens for incoming SMS

messages, which is part of the hidden APIs. I describe this in further detail in Chapter 15.

Sticky Broadcasts

A variation of the normal broadcast is a sticky broadcast, which works a little bit differently. The difference is

that the Intent sent with Context.sendStickyBroadcast() will “stay around” after the broadcast is

complete, allowing future registrations for the matching Intent to receive the same broadcast as well.

One example of such a broadcast is Intent.ACTION_BATTERY_CHANGED, which is used to indicate

changes in the battery level for the device. Another example is Intent.ACTION_DOCK_EVENT, which

indicates whether a device is placed in a dock. Check the Android API documentation for other examples of

sticky broadcasts. The following code shows an example of a receiver that listens for battery changes. It will also

notice whether the sticky broadcast is new or not.

public class BatteryChangeListener extends BroadcastReceiver {

public void onReceive(Context context, Intent intent) {

String action = intent.getAction();

if(Intent.ACTION_BATTERY_CHANGED.equals(action)) {

if(isInitialStickyBroadcast()) {

// This is an old event from the “sticky” cache

} else {

// This is a new event that just occurred...

}

}

}

}

Chapter 8: Mastering BroadcastReceivers and Configuration Changes 157

This method is especially useful for signaling system-wide states (such as battery status). In order for

an application to send this type of broadcast, it must hold the permission android.permission.

BROADCAST_STICKY and send sticky broadcasts using Context.sendStickyBroadcast().

Use sticky broadcasts with care from your own application, because it will put an additional load on

the available system resources.

Directed Broadcasts

Another variation of the normal broadcasts is directed broadcasts. These broadcasts use a feature of the

intent-filter with which you can explicitly specify the receiver by setting the ComponentName

in the broadcasted Intent. This is the combination of the class- and package-name of the registered

BroadcastReceiver, as shown in the following code :

public void sendDirectedBroadcast(String packageName, String className,

String action) {

Intent intent = new Intent(action);

intent.setComponent(new ComponentName(packageName, className));

sendBroadcast(directedBroadcastIntent);

}

The result of the previous code is that you send a normal broadcast that will be received only by the specified

receiver, even if other receivers are registered for the same Intent action. Note: You must know both the

package-name and the class-name of the receiver for this to work.

The directed broadcast approach can be very useful for applications that provide plug-in functionality. When

a plug-in is registered (installed), it can signal to the main application the relevant information for a directed

broadcast.

Enabling and Disabling Receivers

When the broadcast you want to listen for is available only for receivers declared in the manifest, you can

reduce the impact on the system’s load another way. With the PackageManager, you can enable and disable

components in your application, which is helpful if you have a receiver that you want to be inactive unless the

user performs a specific action (for instance, changes a setting).

The two methods that follow illustrate how you can programmatically enable and disable a specific component

based on its ComponentName. You just set android:enabled=”false” in the manifest as the default

value for that component and later use the following code to change the value to true.

public void enableBroadcastReceiver() {

PackageManager packageManager = getPackageManager();

packageManager.setComponentEnabledSetting(

new ComponentName(this, ChargerConnecedListener.class),

158 Part II: Getting the Most Out of Components

PackageManager.COMPONENT_ENABLED_STATE_ENABLED,

PackageManager.DONT_KILL_APP);

}

public void disableBroadcastReceiver() {

PackageManager packageManager = getPackageManager();

packageManager.setComponentEnabledSetting(

new ComponentName(this, ChargerConnecedListener.class),

PackageManager.COMPONENT_ENABLED_STATE_DISABLED,

PackageManager.DONT_KILL_APP);

}

Notice the use of PackageManager.DONT_KILL_APP as the last parameter to

setComponentEnabledSetting(). This will prevent the platform from killing the application, which is

otherwise the default behavior when changing this property.

You can use this method for enabling and disabling components for Activities as well (and

also Services and ContentProviders). It’s an efficient way to toggle the visibility of your

application’s icons in the launcher (also called, home-screen application tray). You could, for instance,

show only the icon for the setup Activity after installation and later hide it using this method after

the setup is complete. You can also use this method to show the icon for an Activity that shouldn’t

be visible until the user completes the setup.

System Broadcast Intents

The Android API defines many different broadcast actions that relate to different system events. In earlier code

examples in this chapter, I’ve shown a few of these, like changes in battery level or when the power to the

devices was connect and disconnected. Because these events are spread out in different places relating to their

respective function, finding a broadcast for a specific event can be difficult, even with the Android developer

site’s search function. Maybe you don’t even know that a certain event exists, or perhaps you get too many

irrelevant matches. Also, some very useful broadcast Intent actions aren’t publically specified in the API,

and finding them requires some understanding of the hidden Android APIs. (I go into the details of the hidden

Android APIs in Chapter 15.)

In this section, you will find some of the most commonly used system events, along with some examples of

when to use them. Several others are available, and the only way to find them is to know what you’re looking

for and search the official Android APIs. A good place to start when looking for their definitions is in the

Intent class.

Auto-Starting Your Application

One of the frequent questions Android application developers ask me is how they can make their applications

start automatically. The short answer is that you can’t, not directly. However, you can register for certain events

that eventually will be triggered and which you can use to start your application. There is also an event that is

Chapter 8: Mastering BroadcastReceivers and Configuration Changes 159

sent to an application after it’s upgraded from an earlier version (usually after an update is downloaded and

installed from Google Play).

The following code shows the declaration of a receiver in the manifest that listens to the Intent.ACTION_

BOOT_COMPLETED and Intent.ACTION_MY_PACKAGE_REPLACED broadcasts. Note: The receiver

is disabled by default. This is a good practice, especially when listening for Intent.ACTION_BOOT_

COMPLETED; otherwise your application will start every time the device is booting up, possibly waste your

system’s resources.

<receiver android:name=”.StartupListener” android:enabled=”false”>

<intent-filter>

<action android:name=” android.intent.action.BOOT_COMPLETED” />

<action android:name=” android.intent.action.MY_PACKAGE_REPLACED”

/>

</intent-filter>

</receiver>

Enable receivers for these broadcasts only when doing so is necessary, such as after the user changes

a setting in your application or enables certain features—for example, an alarm clock application in

which the receiver is left disabled until the user schedules an alarm. Use the code from the earlier

example for enabling (and disabling) this receiver as needed.

User Presence and Screen State

When a user locks a device (that is, presses the power button to turn off the device), the current Activity

receives a call to onPause(), signifying that it has lost focus. Similarly, the Activity receives a call to

onResume() when it regains focus after the lock screen is disabled. Normally, applications don’t need

additional information, but what if you have a Service that needs to be notified every time the user unlocks

the device or when the screen goes on or off? Luckily, there are broadcasts for these events as well, as shown in

the following code block.

<receiver android:name=”.UserPresentListener”>

<intent-filter>

<action android:name=” android.intent.action.SCREEN_OFF” />

<action android:name=” android.intent.action.SCREEN_ON” />

<action android:name=” android.intent.action.USER_PRESENT” />

</intent-filter>

</receiver>

Intent.ACTION_SCREEN_ON and Intent.ACTION_SCREEN_OFF are sent as the device screen goes on

and off, respectively. Intent.ACTION_USER_PRESENT is sent when the user deactivates the lock screen. A

simple diagram showing how these actions are broadcast is shown in Figure 8-3.

160 Part II: Getting the Most Out of Components

Send Intent.ACTION_USER_PRESENT

Screen on

Screen off

Power button

Send Intent.ACTION_SCREEN_OFF

and device

and device

pressed

unlocked

locked

Power button pressed

Screen on

Send Intent.ACTION_SCREEN_ON

User unlocks

and device

device

locked

Figure 8-3 A simple diagram illustrating when broadcasts are sent for the events when

the screen goes on and off and when the user disables the lock screen

Network and Connectivity Changes

For many Android applications, one of the most important things to keep track of is the state of the network

and what kind of connectivity the device currently has. It’s good practice to limit your application’s use of the

network according to the available bandwidth.

Most Android devices have two types of networks, cellular and Wi-Fi. If your application operates heavily on

a network, you may want to defer transfers until the device is connected to a Wi-Fi; otherwise, you may incur

considerable cost if you transfer data for users of mobile networks such as 3G or LTE. These events can also be

useful for detecting when a user connects to a well-known Wi-Fi, like a corporate intranet, where it’s safe to

transfer sensitive data.

Connectivity and network broadcast actions are handled by different parts of the Android API. The

action ConnectivityManager.CONNECTIVITY_ACTION is broadcast whenever a general change

in network connectivity occurs, such as switching from Wi-Fi to mobile data. When this is received, the

ConnectivityManager service can be retrieved using Context.getService() which lets you gain

more detailed information about the current network.

However, to get more fine-grained information about the current network, you also need to listen for broadcast

actions from TelephonyManager (for cellular mobile data network events) and WifiManager (for Wi-Fi–

related events). TelephonyManager lets you query the type of mobile data connection, and WifiManager

gives you access to retrieval of the state of the Wi-Fi connection and the different IDs related to a Wi-Fi (SSID

and BSSID).

The following code is a simplified example of how to detect when a device enters a preconfigured “home” Wi-Fi,

which is effective for applications configured to talk to servers or for media centers that are available only on a

specific Wi-Fi.

public class CheckForHomeWifi extends BroadcastReceiver {

public static final String PREFS_HOME_WIFI_SSID = “homeSSID”;

public void onReceive(Context context, Intent intent) {

SharedPreferences preferences =

Chapter 8: Mastering BroadcastReceivers and Configuration Changes 161

PreferenceManager.getDefaultSharedPreferences(context);

String homeWifi = preferences.getString(PREFS_HOME_WIFI_SSID, null);

if(homeWifi != null) { // Only check if home WiFi is set

NetworkInfo networkInfo =

intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);

if(networkInfo != null &&

networkInfo.getState().equals(NetworkInfo.State.CONNECTED))

{

WifiInfo wifiInfo =

intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO);

if(wifiInfo != null

&& homeWifi.equals(wifiInfo.getSSID())) {

// Success - We’re on out home WiFi!

} else {

// Fail - We’re on some other WiFi!

}

}

}

}

}

In this following example, you listen for changes from the ConnectivityManager and determine whether

you’re on a mobile data network. If you receive mobile data, you make an additional check using the

TelephonyManager to see if you’re on a 3G or LTE network.

public class WhenOn3GorLTE extends BroadcastReceiver {

public void onReceive(Context context, Intent intent) {

String action = intent.getAction();

if (ConnectivityManager.CONNECTIVITY_ACTION) {

boolean noConnectivity = intent.

getBooleanExtra(ConnectivityManager.

EXTRA_NO_CONNECTIVITY, false);

if(noConnectivity) {

// No network at all.. :(

} else {

int networkType = intent.

getIntExtra(ConnectivityManager.

EXTRA_NETWORK_TYPE,

ConnectivityManager.TYPE_DUMMY);

if(networkType == ConnectivityManager.TYPE_MOBILE) {

checkfor3GorLte(context);

}

}

}

}

162 Part II: Getting the Most Out of Components

private void checkfor3GorLte(Context context) {

TelephonyManager telephonyManager = (TelephonyManager) context.

getSystemService(Context.TELEPHONY_SERVICE);

switch (telephonyManager.getNetworkType()) {

case TelephonyManager.NETWORK_TYPE_HSDPA:

case TelephonyManager.NETWORK_TYPE_HSPA:

case TelephonyManager.NETWORK_TYPE_HSPAP:

case TelephonyManager.NETWORK_TYPE_HSUPA:

case TelephonyManager.NETWORK_TYPE_LTE:

// Yay - we got fast enough mobile data! :)

break;

default:

// Slow mobile network - notify user...

break;

}

}

}

Device Configuration Changes

Whenever you turn an Android device from portrait to landscape orientation, an event is triggered by the

system causing a configuration change. Several related triggers all cause this event, such as changing the UI

mode on the device or when keyboard visibility changes.

Configuration changes are a little tricky to manage on Android. For an Activity, the default behavior

is to restart, which means that onPause(), onStop(), and onDestroy() are called and your

Activity instance is lost, including all its data. To avoid having the Activity restart when a certain

configuration change occurs, you can declare these events in the android:configChanges attribute of

the Activity tag. As a result, the method Activity.onConfigurationChanged() is called with the

new Configuration object, instead of your Activity restarting. Use this method only as a last resort—

for instance, if you have a full-screen game or application with custom handling of changes in the device’s

orientation.

The standard way of handling configuration changes for an Activity is through the default behavior: Let the

system restart it. This works well for most situations, but in some cases, you may want to avoid restarting your

Activity because restarting it could have a negative impact on performance and user experience.

For the Service, ContentProvider, and Application components, you can also use the

onConfigurationChanged() method. The difference is the Activity class is called for all configuration

changes without you having to add attributes in the manifest. Also, for these components, configuration

changes will not force them to restart (which is good because otherwise Services would restart when you

rotate your device). This allows your Service, or some other background component, to detect when the user

rotates the device or when another application changes the UI mode.

Chapter 8: Mastering BroadcastReceivers and Configuration Changes 163

Summary

In this chapter, I introduced advanced concepts of the BroadcastReceivers that Android developers might

not be familiar with. These receivers are powerful tools when it comes to reacting to system-wide events and

also for communicating within your application, using the LocalBroadcastManager, or between multiple

applications.

I explained the difference between normal, ordered, sticky, and directed broadcasts and when and how to

apply them. Next, I showed how to enable and disable receivers, and other components, in your code to ensure

that they’re activated only when necessary. This powerful feature in Android should be used more often in

applications to reduce their load on the overall system.

Many different broadcast Intent actions are defined by the Android platform. I discussed a few of the most

useful broadcasts and gave a few examples of when and how to use them. Most of them are documented in the

official Android APIs, but some are part of the hidden APIs that I cover in more detail in Chapter 15.

Finally, I covered how to detect configuration changes of the device, including screen orientation, keyboard

state, and UI mode. By default, your Activity is restarted when a configuration change occurs, but you can

override this using the android:configChanges attribute in the manifest. You can also listen for these

changes by overriding the method onConfigurationChanged() in your Service components, which

can help with receiving notifications about state changes in devices that lack a system broadcast.

BroadcastReceivers and configuration changes are often overlooked in Android applications, but as a

skilled developer, you can use them to create a smooth user experience and to react to changes that could

otherwise cause unexpected behavior in an application.

Further Resources

Documentation

For handling runtime configuration changes, go to http://developer.android.com/guide/

topics/resources/runtime-changes.html

Chapter 9

Data Storage and

Serialization Techniques

This chapter is all about storing and representing data on a local device. Storing data is an important aspect

of all software applications. Developers usually use the term persistence when talking about storing data, and