April 24, 2014

Gettin’ On The (Message) Bus, Part Two

In our last episode, we discussed how the Android Intent systems functions, in effect, like a message bus, and how one can set up an IntentReceiver to serve as a consumer of bus messages. Today, let’s complete the picture, creating both a message consumer and producer, as two separate applications, so you can see how one Android application can send messages to another.

Since this post will be discussing two full applications, we’ll only be showing relevant bits of the code in the post itself. You can download the pair of Android projects here — the code is available under the Apache 2.0 License.

To set up messaging between two Android applications, you need to:

  1. Choose a name for the Intent, to distinguish it from other Intent names already in use
  2. Decide how you are going to package useful information into the Intent, such as by using putExtra()
  3. Set up an IntentReceiver, as in the previous post, to watch for an Intent of your chosen name
  4. Have your message producer create a properly-named Intent and call broadcastIntent(), so the Android underpinnings will deliver the Intent to your patiently-waiting receiver

Let’s take a look at the consumer side, implemented as a ListActivity:

[sourcecode language='java']public class ConsActivity extends ListActivity {
protected static final String BUS_MSG=
new String(“com.commonsware.android.intents.BUS_MSG”);
protected static final String BUS_MSG_BODY=
new String(“com.commonsware.android.intents.BUS_MSG_BODY”);
protected final IntentFilter filter=new IntentFilter(BUS_MSG);
private ConsIntentReceiver receiver=new ConsIntentReceiver();
private ArrayAdapter adapter;

@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);

adapter=new ArrayAdapter(this,
android.R.layout.simple_list_item_1);
setListAdapter(adapter);
registerReceiver(receiver, filter);
}

@Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(receiver);
}

class ConsIntentReceiver extends IntentReceiver {
@Override
public void onReceiveIntent(Context context, Intent intent) {
adapter.addObject(intent.getStringExtra(BUS_MSG_BODY));
}
}
}[/sourcecode]

The name of our magic Intent for inter-application communication is given the local name of BUS_MSG, but that’s just a static string ("com.commonsware.android.intents.BUS_MSG"). We create an IntentFilter to watch for such messages, and in onCreate() we use that filter to connect our ConsIntentReceiver to watch for these messages. The net is that ConsIntentReceiver#onReceiveIntent() will get called for each BUS_MSG our producer sends. In ConsIntentReceiver#onReceiveIntent(), we extract the body of the message (BUS_MSG_BODY) via getStringExtra() and pop that value into our list. We should get a new list entry for each message the producer produces.

So, without further ado, let’s see the producer:

[sourcecode language='java']public class ProdActivity extends Activity {
protected static final String BUS_MSG=
new String(“com.commonsware.android.intents.BUS_MSG”);
protected static final String BUS_MSG_BODY=
new String(“com.commonsware.android.intents.BUS_MSG_BODY”);
private EditText body;

@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
body=(EditText)findViewById(R.id.body);

((Button)findViewById(R.id.send))
.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
Intent i=new Intent(BUS_MSG);

i.putExtra(BUS_MSG_BODY, body.getText().toString());
broadcastIntent(i);
}
});
}
}[/sourcecode]

For simplicity of the demo, we re-declare BUS_MSG and BUS_MSG_BODY. In theory, those would be defined in some central source code spot that both projects could pull from.

The activity is just a field (EditText) and button (Button). When the user clicks the button, we take the contents of the field, pour that into a BUS_MSG Intent as the BUS_MSG_BODY “extra” (via putExtra()), then call broadcastIntent() to send that Intent on its merry way.

To see this in action, compile both projects and upload them to your Android emulator. Start up the BusConsumer activity, then start up the BusProducer activity. In the producer, type in some text and send it using the button. Switch back to the BusConsumer (click the Home button, then on the BusConsumer icon), and you will see your text in the list.

Note that we are running two completely separate applications here — an Intent can cross process boundaries with impunity. However, there is nothing to prevent you from using this technique within a single application (e.g., a service letting an activity know about newly-arrived information).

Our next series of Building ‘Droids posts will look at creating fancy lists — not just plain text, but list entries with fancier formatting, icons, and the like. Along the way, we’ll explore some dusty corners of the adapter framework, learn how to inflate views, and so on. Until then, happy coding!

Article Tags

Related Posts