As a new mobile developer, one of the challenges with developing on Android is designing the architecture for an app. Do you choose MVC, MVVM, MVP, or evolve your own concept for the domain and problem you’re working on now?
This is why I’m excited by the new, opinionated approach that Google has to Android app architecture. If you haven’t seen it yet, I highly recommend watching the introduction talk from GoogleIO of this year. They explain the concepts much better than I will, so please start there. There are subsequent talks, code labs, and documentation to delve deeper into the topic, where the designers of the components further explain their decisions. The documentation and example repos have many code examples, and are worth checking out as a reference.
The new components include:
- Lifecycle aware interfaces
- LiveData - A reactive data object
- ViewModel - A configuration-independent data holder
- Room - An ORM for SQLite
Often you have a component that needs to do some cleanup based on the lifecycle of another component. An example would be a component that needs to remove a listener for a callback when the listener is stopped. This set of interfaces allows you to put that cleanup in the most appropriate place - the component with the reference to the listener. Implementing this encourages much better separation of concerns.
When data changes from an external source, or from elsewhere in the app, it’s pretty common to notify particular components using an event bus or similar system. Event buses are great, but are sometimes heavy-handed. LiveData allows you to observe on changes to a data object, allowing the same level of decoupling that an event bus grants you, but with little setup.
When you rotate the screen in Android, the system stops the current Activity and restarts it with the new configuration. This is fine, however it’s pretty common to do some data setup when starting an Activity, for example calling out to an API, pulling data from device storage, and data manipulation to get into into the needed state. ViewModels solve this problem by being a layer in your architecture that ignores such configuration changes. When you start an Activity, you fetch the appropriate ViewModel and start using it right away. It is up to the ViewModel to fetch the appropriate data from other layers and store references to it. This way, if you make a costly request, you can still use the results with the new Activity instance via the ViewModel.
I haven’t had a chance to really leverage Room yet, so I can only quote from the documentation.
Room provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite.
Notably this allows compile-time validation of your queries, a huge boon for complex queries.
These components are great not just because they solve real pain-points for developers, but because they all interoperate as much as you would like them to. ViewModels are lifecycle-aware, Room returns LiveData objects, and LiveData objects are lifecycle-aware too. You can opt-in to separate components as and when you like to. This means that you can start using these components in your project much sooner, and only where appropriate.
The other great benefit that these components provide, is that they fit into Google’s new lightweight recommended architecture.
The architecture has four layers:
- UI Controllers
- Data Sources
These are Activities and Fragments, and include all the interactions with the UI. This layer pulls data from ViewModels, and posts changes back to them.
These collect the data from Repositories that are appropriate for a particular UI, or section of UI. They are something like a join between your UI, and your domain.
These maintain and update global references to your LiveData objects, and provide a convenient API to the rest of your application. These are likely tied to a domain, and talk to Data Sources to find and collate data as appropriate.
These are the API libraries, device storage wrappers, model objects, or any other data sources.
If this architecture looks similar to what you have already, don’t be surprised. The concepts are loose on purpose, and full points to Google for listening to developers' needs when designing this. This architecture is intended to be easy to test, and intuitive to pick up. Each layer only talks to the layer below it, and has no knowledge of the layer above it. Therefore testing is as easy as injecting or mocking the layer below it, and the layer is tested in a more isolated fashion.
Good work Google
The new architecture and associated components are very exciting, and my initial usage of them has been positive. Having some opinions from Google developers allows you to start architecting your app with more confidence, without feeling restricted by the platform. I would love to hear about how you all are using the new components, and what YOU have found!