The elegant solution I've found to work is based on self-routing messages. The idea is to make messages smart enough to know the path they have to follow (it can be a fixed path or one based on rules). This means that when an Activity completes, it delegates the next thing to do to the message itself. Paths can be described by extending the class ControlFlow, as this simple example illustrates:
public class TaxonFactSheetControlFlow extends ControlFlow
It should be readable enough to understand that it implements the flow #2 in our diagram. To start it, the originating Activity calls:
The specified ControlFlow not only calls the first Activity of the sequence, but it also binds itself to the Intent; in this way, it will be possible to retrieve it in the next steps.
Whenever an Activity completes, it calls:
ControlFlow.from(this).toNextStep(intent); // an intent can be used to carry extra information
The originally instantiated ControlFlow is retrieved and invoked to find the next Activity to activate.
In case of rule-based paths, it is possible to pass arguments to be evaluated by the ControlFlow; for instance, CountAndGenderActivity in flow#1 calls one of the two following code fragments reacting to the pressure of the "Ok" and "Add more" buttons:
The flow#2 is described by this class:
public class AddObservationControlFlow extends ControlFlow
private final Condition addMore = new Condition()
public boolean compute (final @Nonnull Object... args)
As you can see, each transition can be associated with a condition which is function, for instance, of the arguments passed to toNextStep().
This solution is very scalable, as I can reuse existing Activities as many times as I want, by just creating new subclasses of ControlFlow which describe the new sequences to implement.
The code can be checked out from the Mercurial repository at https://kenai.com/hg/bluebill-mobile~android-src, tag dzone20100517.