Flurry’s engineers work with multiple, large scale custom systems. In an effort to further give back to the engineering and app development communities, we will periodically publish posts that cover more technical issues. Ali Mehrpour, one of our lead Android SDK developers, wanted to share some of his findings in rebuilding Flurry’s mobile app using the latest developments in Android technology.
In Part 1, we discussed the earlier version of Flurry’s mobile app and some of the issues we encountered with the Model-View-Presenter (MVP) design pattern. In Part 2, we’ll share some findings we learned while migrating to a new architecture.
Moving to a New Architecture
Over the years Android architecture evolved to support complex, robust, production-quality apps on any scale. Recently, Google made Kotlin a first-class language for writing Android apps and introduced Jetpack, which is a suite of libraries, tools, and guidance to help developers write high-quality apps easier. We benefited from all of these new technologies while developing the new Flurry mobile app.
Flurry Mobile App Architecture - MVVM
From MVP/MVC to MVVM
We’ve chosen MVVM (Model-View-ViewModel) for the new version of Flurry’s mobile app. We found several resources online that explain MVVM well, but why did we choose MVVM?
- In MVVM, the view only depends on the ViewModel to get data and directly observes changes in the model using data-binding. Therefore, the view gets completely decoupled.
- In MVC, there is a triangular relationship between the components (the Controller owns the View and the Model) but in MVVM, think of this triangle flattening out with each component only knowing about one another in the chain. That is: View -> ViewModel -> Model. The Model is unaware of anything up the stack. The ViewModel is only aware of the Model. The View is only aware of the ViewModel - it is unaware of the Model.
- In MVVM, the presentation logic is handled by the ViewModel, meanwhile, in MVC or MVP, this responsibility is barely clear.
- With MVVM, we are basically distributing responsibilities in a better way to reduce dependencies and make the code easier to test and debug.
Android Architecture Components
These components Google released a couple of years ago make our life easier when it comes to implementing MVVM patterns on Android, as well as achieving more robust, maintainable and easier to test apps:
- LiveData. LiveData is a lifecycle-aware observable data holder class. It respects the lifecycle of the app component such as activity or fragment and makes sure only to notify app component observers when they are in the active state
- ViewModel. It stores and manages UI-related data in a lifecycle conscious way. It exposes data through LiveData observed from the view. The ViewModel class allows data to survive configuration changes such as screen rotations
- Room is an ORM (Object Relational Mapping) library that converts SQLite tables to Java/Kotlin objects automatically. It allows SQL validation in compile-time and returns RxJava, Flowable and LiveData observable
- Repository handles data operations. It provides a clean API so that the rest of the app can retrieve data easily. Only the Repository knows where and how to get the data (encapsulated). Data can be fetched from a remote service, local database or cache.
The new app is completely written in Kotlin. There are a lot of benefits in using Koltin, plus we saw an opportunity for the team to try Koltin in a real application.
Dependency Injection is a programming technique that makes a class independent of its dependencies. It achieves that by decoupling the usage of an object from its creation and provides reusability of code, ease of refactoring, and ease of testing. It also reduces the cold start time since there is no need to create objects in the app startup, they can be created lazily whenever needed for the first time.
It’s well suited for Android development. The available popular libraries for Android are RoboGuice, ButterKnife, Dagger 2, and Koin. We’ve used Dagger and Butterknife for object and view injections respectively. This post compares these libraries.
RxJava simplifies development because it raises the level of abstraction around the threading. That is, as a developer you don’t have to worry too much about the details of how to perform operations on different threads.
In the new app, we’ve used RxJava for interacting between ViewModel and Model. Repositories return Observable and ViewModel subscribes itself to get data. As you can imagine, ViewModel can subscribe to multiple Repositories and combine their results into one aggregated LiveData object. The important part is that we should dispose of all Observables in ViewModel’s onCleared() since we don’t need the response when ViewModel is going to be destroyed.
Also, we’ve used RxAdapter with Retrofit which each API returns an Observable/Flowable.
In order to address the product manager’s concern (the ability to update the app behavior on the fly), we came up with a config-driven architecture. In other words, the app reads a JSON config file on cold start and lays out the app UI and features (dashboard, metric, dimension) based on this config.
This config file gets updated periodically by using WorkManager (a component of Android Jetpack)
- Timber. Timber is a lightweight and extendible logging library on top of the Android normal Log class. You can install a different Tree for each build type (e.g. we log all error logs in the Flurry SDK only for release build).
- MPAndroidChart. We’ve used MPAndroidChart library, a powerful Android chart library that supports line, pie, bar and many other charts.
- Material Components. We’ve designed the app based on Material design guidelines and used available Material components such as FloatingActionButtom, Chips, and BottomSheet.
- Retrofit. We’ve used the most popular networking library.
- ThreeTenABP. This library is a lightweight adaption of the JSR-310 for Android and makes working with Date and Time a lot easier. E.g. converting a date from one time zone to another, and calculate the duration between two dates, etc.
Now that we have successfully moved the new architecture, we will begin to address additional opportunities to optimize Flurry customers’ mobile experience, including offline mode and custom dashboard, as well as additional features that align with the Flurry web app. If you want to give the new app a try, you can download the app from the Play Store or App Store. Login using your existing Flurry account, or use our demo account with the username “email@example.com” and password “chateriffic”.