Something that is always difficult for newcomers to both iOS and Android is the idea of the . The idea that your App will often be completely killed or interrupted without a moment’s notice is a problem that has to be fully understood in order to write a responsive and robust app.Android provides clear guidelines as to when and how these interruptions are communicated to the app and how your app should handle them. But it’s easier said than done. The classic case is making a HTTP request to a server, which can take up to say 60 seconds, and when the response comes back, the user has given up waiting and moved on to a different app. 60 seconds is a lifetime for a user on a smartphone. In the meantime, Android has cleaned up your view and the application goes into a strange state, or worse, crashes because you tried to update a view that is long gone.
How do we work with this?
The key to understanding the ‘EventBus’ solution is in the separation of your view layer from your business and communications layer. If the view keeps absolutely no strong references to the business/communication layer, it can be safely and efficiently cleaned up by garbage collection at any time. But how do you let the view know when a IO process has completed? In our example of logging in, the login screen has to know whether the login with the server was successful or not. How does the service let the view know it’s result if it doesn’t have a reference to it?
This is where the EventBus comes in. The EventBus itself (by greenrobot for Android) is not a new concept in software, it’s simply a pub/sub mechanism internal to your app. The greenRobot implementation does some cool things, but in a nutshell, it’s a publisher subscriber library. It allows any object within the process to publish to an event of any kind, and any object can then subscribe to this event.
If that last sentence makes you nervous, good. It should. An app that has any number of objects publishing and subscribing to any number of events is incredibly non-deterministic, messy, confusing and impossible to debug. Being able to just post a message and respond to it in a distant part of your application sounds great at first but it encourages all sorts of bad practices and introduces insane amounts of non-determinism that makes debugging an absolute nightmare. The problem with an EventBus is that it inherently separates the publisher from the subscriber: a view can talk to a manager, a presenter can talk to a model, a model can make service requests… all kinds of rules get broken. However, that separation is very useful, if you use it correctly.
There are two mechanisms this architecture employs to enforce/encourage the rules. The first and most important is convention. Convention involves explaining to new developers the ways they should use the EventBus and using Pull Requests to ensure that they are sticking to these conventions. The second and more practical is architecture abstraction. Using inheritance and abstraction to discourage usage of the EventBus directly.
Understanding the architecture is paramount. It essentially involves splitting your app horizontally into two parts: the ‘top’ of your app, lets call it The Activity (fragments, activities, presenters) and the ‘bottom’ of your app, lets call it the The Application (controllers, managers, service requests, database requests, preferences).
Android, as it turns out, does this already! The Activity lifecycle is independent of the Application lifecycle. So when the Activity is created, we connect the Activity to the Application by registering it with the EventBus. When the Activity is destroyed, we disconnect the Activity from the Application by unregistering it from the Eventbus.
Lets take a closer look at each of the components in the figure above.
Model Fragment View Presenter
A slight variation on the traditional MVP concept, each new fragment requires the following files to be created, lets call it a login screen:
Explaining this setup is it’s own blog post, but in short, the LoginFragment class should have no knowledge of the models (User, Group, Driver, Session for example) and simply deals with anything UI related such as animations. It should be simple and definitely not contain any kind of logic. If you find yourself writing an ‘if’ statement in this file, it’s time to pause and reflect. Conversely the Presenter should have no knowledge of UI Controls (Button, Switch, Fragment, EditText for example) and simply translates any changes in the Application into commands for the UI, possibly via some business logic.
You can perform quick checks in your Pull Request Review to monitor these conventions with a quick scan of the import statements of each file. The Fragment should have no import statements from the business packages (eg com.myapp.model).And the Presenter should have no import statements from the android UI packages (eg android.view.View). Also have a look at the View interface – this is how the presenter commands the view, the method names should be just that: commands, and their implementations should strictly perform exactly what the method implies and nothing more (eg showBusy, hideBusy, setUsername). The last thing to check is the public methods of the Presenter, this is how the Fragment informs the Presenter of any UI changes/events. They should not be commands and they should not include UI names (eg. loginButtonPressed should be onLoginRequested). The Fragment creates an instance of the Presenter and it implements the interface LoginView. The presenter takes an instance of LoginView in it’s constructor and uses this interface to communicate with the ‘View’. This makes the Presenter extremely easy to test.
The Registry is a member of the Application class, created in the onCreate method. It does a few important things: it creates the managers, then it creates the controllers, passing the managers into the controllers where necessary, it then registers the controllers with the EventBus making them ready to start accepting events from the Activity layer, or send out events of their own in response to any changes in the Application layer. The registry is simply a class that holds the references to alll the managers and controllers and since it is a member of the Application class it, and all it’s contents are created and destroyed with the Application’s lifecycle.
Manager or controller? These terms are used interchangeably depending on who you talk to… for the purposes of this architecture, controllers are very discrete classes, very thin and are created just once in the lifecycle of the application. They stick as close as possible to the single responsibility principle and are the only objects that should communicate with the Eventbus in the Application layer of the app. As much as possible, one Controller will react to one event only and fire off one
A manager is also only created once in the lifecycle of the application, but it is shared amongst the controllers. Generally each manager takes care of a logical section of a REST api. eg. UserManager, ReservationManager, SettingsManager and provides a single entry point for interacting with the webservice. It might also provide a logical section of the database or local storage eg. UserPreferenceManager, SqlUpgradeManager.
Presenters and Controllers
The architecture encourages the idea of separation by making the Eventbus available to any subclass of a Presenter or a Controller. Essentially Presenters (who are part of the Activity) communicate with the Controllers (part of the Application). They are the gatekeepers. They send and respond to events. Incidentally, this makes the presenters and controllers extremely easy to test. They are almost perfectly ready for Black Box Testing.
Let’s run through an example of the typical flow of control through the architectural components.
- The user enters their username/password and presses Login.
- The LoginFragment receives the OnClick event, and calls the LoginPresenter. The LoginPresenter tells the LoginView to showBusy() and the calls post(new LoginDoAuthenticateEvent()).
- The LoginController receives the LoginDoAuthenticateEvent which contains the username/password and calls the AuthenticationManager with the username and password and a OnAuthenticationComplete interface with the good old onSuccess and onFailure methods.
- The AuthenticationManager makes the call on the network and responds on the appropriate method (onSuccess/onFailure), lets assume its a success.
- The LoginController’s anonymous interface is invoked and it call post(new LoginAuthenticationSuccessEvent())
- If the LoginPresenter is still listening (ie if the Activity still exists) it receives the LoginAuthenticationSuccessEvent, calls hideBusy() and takes the user to the next screen. If the LoginPresenter is not listening, the message is simply lost in the ether.
This is an overview of the components of the Eventbus Architecture. I have purposely tried to leave out any code, but the coding conventions around the individual components is incredibly clever. I will endeavor to include them in a future post.