04 September 2013

Darker Corners of Android: BroadcastReceivers and Intents

Not all in Android-land is lightness and brightness. A few dark corners and cul de sacs await the unwary, ready to trap us in sticky tarbaby snares of mystery debugging. One such is a case we recently encountered in relation to BroadcastReceivers and the way Intents get delivered to them.

I've written previously about implicit Intents versus explicit Intents in the context of describing a useful code-pattern for preparing Intents. To briefly recap, explicit Intents name the class of the component they are aimed at whilst implicit Intents simply describe the desired action they intend to trigger and allow the Android intent-delivery system to find the best-matching component to handle the Intent. Indeed, a better naming scheme might have been "broadcast Intent" for implicit Intents and "unicast Intent" for explicit Intents.

BroadcastReceivers can be registered for receiving broadcast Intents in one of two ways, either through a declaration in the Android manifest file, or via a dynamic registration by another component.

If you register a BroadcastReceiver via a manifest declaration, then you must provide the class-name of the receiver implementation. To communicate with the receiver, you broadcast an Intent – either one that matches the receiver's intent-filter (if it was declared to have one) or an Intent that specifies the class-name of the receiver. In this latter case it's hardly a broadcast, really, since the Intent is directed at a specific target receiver. Nevertheless, this is a perfectly valid way to send events and information to a BroadcastReceiver.

If you dynamically register a BroadcastReceiver – by calling Context.registerReceiver() – you supply a BroadcastReceiver instance and an IntentFilter describing the sorts of Intents that should be passed to the receiver. And herein lies a small gotcha. You register an instance.

If you try to send an Intent to the receiver by naming the class of the BroadcastReceiver, it will never get delivered. Sending the Intent using something like

Intent i = new Intent( this, MessageReceiver.class );
sendBroadcast( i );

won't work. The Android system will not match the Intent you declared to the class of the BroadcastReceiver instance you registered. The only way to send Intents to dynamically registered BroadcastReceivers is by having them match the IntentFilter attached to the receiver when it gets registered.

Explicit Intent matching just doesn't work in this case.

I've put some example code on github that demonstrates all this. There's a simple BroadcastReceiver (the MessageReceiver class) that gets registered dynamically by the MainActivity. The MainActivity instance then tries to send Intents to this receiver instance using two different methods. First it sends an Intent that matches the Action declared in the receiver's intent-filter. That Intent gets through successfully. Then it tries to send an Intent that explicitly names the MessageReceiver class. It never arrives.

As much as I love the eventful nature of Android software architecture I view this as a bug (Come on guys... How hard would it be to do a getClass() on the instance that gets registered?) but it is what it is. It took us a day or so to figure out why Intents were mysteriously failing to be delivered to an otherwise perfectly ordinary BroadcastReceiver, so I thought I'd save you, Dear Reader, the trouble. It's pretty straightforward to cope with (rely on Intent actions rather than explicit Intents) once you know of this issue, but I have not seen it documented anywhere in the Android reference materialsε.

Share and Enjoy.

ε If anyone does spot a description of this in the official docs, please do let me know!

27 July 2013

Android Patterns: Manufacturing Intents

Activities, Services and BroadcastListeners are the basic components of application structure in Android. They are all started by firing an Intent that causes the Android system to activate them. There are two very different kinds of Intent that might activate an Activity: implicit Intents, and explicit Intents.

We present here a useful pattern for creating those Intents in a way that makes code more robust and easier to maintain.

For the purpose of simpler description we'll talk about starting Activities using Intents, though the same pattern makes most sense when used (with due care and discretion) for all components.

Implicit Intents


Implicit Intents do not name the specific Activity they trigger. Instead Android looks for the best match between an Intent's content and any Intent Filters declared by the Activity. This is what happens when your application's main Activity is started by an Intent that has its action set to ACTION_MAIN and its category set to CATEGORY_LAUNCHER.

Implicit Intents and their matching by IntentFilters is the key way that applications are able to interact with the entire ecosystem of applications and services in an Android system. It allows us to run an application that (for example) makes use of a web-browser or email client without having to embed all the functionality of a browser or email client in the application itself; we can merely assume that a suitable application exists in the ecosystem, and all our application has to do to leverage that capability is to fire the right Intent. This system of implicit Intents is one of the most powerful aspect of Android, and one which many applications fail to use well. How many applications have we seen that try to provide their own web-browsing capability, ending up providing some half-baked, crippled web-page fetcher-and-renderer, while we know perfectly well that the system almost certainly hosts one or more full-fledged, powerful web-browsers, if only they would just fire an appropriate Intent instead of trying to go it alone.

Explicit Intents


Explicit Intents are Intents that name a specific component – an Activity, Service or BroadcastReceiver – as the thing to be activated using the components class name. As soon as an Intent names a specific component, all other forms of Intent matching against filters is abandoned, and Android activates only the specific component, without any regard for the other bits of data the Intent may carry.

In terms of Activities, explicit Intents are used to implement in-application navigation between Activities, and many of the target Activities will not have any associated IntentFilter at all, so there is no other way to activate them other than with explicit Intents fired form other parts of the application.

Pattern


Each Activity, Service or BroadcastReceiver should provide a factory that returns template Intents for starting that Activity, Service or BroadcastReceiver. In the simplest case – say an Activity started by an explicit Intent – this might be a simple factory method:


public class ListManagerActivity extends Activity {

    public static Intent getStartIntent( Context context ){
        return new Intent( context, ListManagerActivity.class );
    }
    ...
}

In this most elementary form such a factory method is not, by itself, very useful. It becomes ''much'' more compelling when the Activity in question expects or requires certain additional data to be present in its starter Intent:


public class ListManagerActivity extends Activity {
  public static Intent getStartIntent( Context context, UserInfo userData ){
    checkNotNull( userData );
    final Intent startIntent = new Intent( context, ListManagerActivity.class );
    startIntent.putExtra( EXTRA_USERINFO, userData );
    return startIntent;
  }
  ...
}

In this case we achieve two worthy objectives with this simple factory method:

  1. We make sure that the Intent that starts the ListManagerActivity always contains the userInfo extra that (presumably) the activity absolutely requires in order to behave correctly, provided, of course, that we only ever manufacture start Intents for ListManagerActivity using this method, and never by calling new Intent(...) – something that can quite easily be checked using various code-quality audit tools.
  2. We keep the code that builds start Intents (with their extra data) close to the code that consumes those Intents (and that extra data) so that, if we change the notion of what data an Activity requires, we're already in the right place in the code to make the corresponding fixes to the manufacture of those Intents, rather than having to hunt around all over our codebase.


There might be more complex cases where components get started using a variety of different Intents. Perhaps Intents might carry different extra objects or flags affecting the component's behaviour. Perhaps the component might be activates using implicit intents with varying options for their data URI. Indeed, a common mistake when passing a data URI in an implicit Intent is forgetting to correctly set the mime-type for the data URI, in which case matching the Intent against an IntentFilter mysteriously fails.

In such complex cases it may be appropriate to supply several Intent-factory methods in the class that gets activated. It may even be desirable to provide an Intent Builder class:


public class ListManagerActivity extends Activity {
    public static class Builder {
        private Intent startIntent;
        public Builder( Context context, Foo mandatoryData ){
            startIntent = new Intent( ListManagerActivity.class );
            startIntent.addExtra( EXTRA_FOO, mandatoryData );
            startIntent.addFlags( FLAG_ALLOW_NEW_ITEMS );
        }
        public Builder addVitamins( final Vitamin nutrient ){
            startIntent.addExtra( EXTRA_VITAMIN_ID, nutrient.getId() );
            return this;
        }
        ...
        public Intent build(){
            validateIntent();
            final Intent constructedIntent = startIntent;
            startIntent = null;
            return constructedIntent;
    }
}

TL;DR:


Don't create start Intents for Activities, Services and BroadcastListeners willy-nilly in myriad and random places all over your codebase. Rather have each such component class provide factory methods or builders to manufacture those start Intents.

This ensures that
  • the Intents carry all the necessary data that the component expects, 
  • that the data is added in close proximity to the places where it is consumed, so that changes in the requirement are easily mirrored in the corresponding changes in the creation and structuring of that data, and
  • mandatory data required by the component can be reflected by mandatory arguments in the signature of factory methods, ensuring that required data cannot be easily forgotten.

21 May 2013

Netbeans EE Dependency Madness

Dredging up an ancient persona-time web-project and dusting it off caused me to reinstall the Base-EE module for Netbeans - long my IDE of choice. To my dismay, Netbeans pops up the following list of dependent modules:
This is madness!

I will never be using JSF, IceFaces, PrimeFaces or Struts. There will be frost on the ground in Hades before I ever use any part of Spring Framework without being severely and repeatedly bludgeoned with a blunt instrument. And if I ever do choose to host a webapp "in the cloud", Oracle is probably going to be my very last choice, though I can understand them being desperate to punt their wares in an IDE that they do, after all, fund in some measure.

So why are these listed as required dependencies? It might be that the EE Base module uses some features from those modules, in which case, why have those not been broken out as separate modules, this being one of the huge strengths of the Netbeans Platform?

Something is very smelly in the nation of Netbeans!
Related Posts Plugin for WordPress, Blogger...