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-
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.
“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-
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