Building a Beautiful Disney MVVM Android Application (2): Jetpack + Architectures

Jaewoong Eum
ProAndroidDev
Published in
6 min readApr 5, 2020

--

In the last post, we found out about the motion systems in the new version of the material design. In this part, we’re going to find about the modern Android development tools and architectures.

Building a Beautiful Disney MVVM Android Application (1): Material Motion Systems.

DisneyMotions

I published an open-source project DisneyMotions, it is based on MVVM architecture and written in Kotlin. Check the full source code on this repository. 💗

Let’s deep dive into the project’s architecture!

MVVM

Nowadays, almost Android developers have heard of it at least once or already know about the MVVM architecture. After the Google Android team announced the ViewModel of Jetpack, it has been one of the most popular architectures between modern Android patterns like MVP, MVI, VIPER or etc.

To understand MVVM accurately, we need to know where it started and why it was made. Historically, the MVVM architecture originated from Microsoft.

MVVM@Microsoft

The MVVM helps to completely separate the business logic of the application from the UI. If the business logic is a very tightly coupled relationship with UI logic, maintenance would be difficult. Because it’s hard to reuse the business logic and writing unit test code is very difficult. Then We have to make a bunch of duplicated codes or complicated logics and they will be huge legacies a bit later.

The Jetpack’s ViewModel has some advantages like lifecycle observation and data can be maintained continuously even when the screen rotates. But the Jetpack’s ViewModel is closer to a retainer for LifecycleOwner’s lifecycle and data than the actual MVVM by Microsoft. If we want to implement the MVVM as intended by Microsoft, the most important key is the Data Binding.

Data Binding is actually another view hierarchy layer from the layout XML regardless of activities or fragments. So the View (XML) interacts continuously with ViewModel via the Data Binding layer.

The HomeFragment shows a grid list of Disney posters, they are fetched from the network or integrated from the database. But the code is quite simple because of using the Data Binding. If we want to change the network’s URL, data model, the way of fetching or saving data or whatever, we don’t need to change any codes in the HomeFragment. These codes demonstrate to having advantages of maintenance the project. What magic!

Then, is the MVVM architecture is always an answer for every project? The answer is No. There is no best architecture for all projects. It is important for us to build an architecture to suit the design of the project. If your project does not need to request data to the network or integrating data from the database, furthermore there are no interactions with models, creating MVVM architecture is a waste of your time and efforts to make many boilerplate codes.

Let’s see the basic example, MainViewModel. The ViewModel retains data until the MainActivity (LifecycleOwner) is destroyed (Except called by the screen rotation). And the LiveData has good synergy with the ViewModel. Because the LiveData holds fetched data from the repository and it can be observed by other components including the Data Binding layer. It also follows the activity or fragment’s lifecycle, and it will be unsubscribed when the MainActivity (LifecycleOwner) is destroyed.

Repository Pattern

Repository pattern is one of the most popular and widely applicable design patterns, defined by Eric Evens. A domain layer requests data to the repository interface, and the repository will get and integrate the requested data to the network and database. This pattern is more applicable to Clean Architecture or Domain Driven Design than the DisneyMotions use case.

Remote Data API Service is configured with service interfaces used by Retrofit. We can fetch original data from the network via those services, and responses will be stored in the local database. DisneyMotions uses Room as a local database. The Room provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite. So we don’t need to hard code the SQL queries and the Room compiler will check errors at the compile time.

Let’s look at the MainRepository. The loadDisneyPoster method will check data of posters are stored in the local database or not. If there are posters already in the database, it will be returned. If not, we need to request data to the network and integrate the responses into the local database. Then we don’t need to request data every time and waste the usage of the network communications.

Dependency Injection

Dependency injection is also one of the popular ways to loose coupling between business logic and UI related logic. Dependency injection is transferring the obligation to create an object out of the class. So the class doesn’t need to know about how an object is created. It makes to maintain consistency in the creation of the object in the project and we can manage the lifecycle of an object based on scope.

There are many options for implementing dependency injection in the Android project. Dagger, Koin, Kodeien or we can just implement it manually.
We can choose anything, but I used the Koin in the DisneyMotions project.

Actually, the Koin is closer to the service locater pattern than dependency injection. Because Koin makes you write the boilerplate of constructor injection. The Koin has an internal hashmap for cashing about the instances, and they will be constructed when we request injection. So If your project is very huge, it is more recommended a generating way in the compile time. Since DisneyMotions is a small project, I adopted the Koin for saving time.

The MainRepository module requests instances of DisneyClient and PosterDao when it is created. And the MainViewModel requests an instance of the MainRepository when it is created. So actually MainRepository, MainViewModel, and MainActivity are independent each other. Then we can mock an instance and write unit test code for each class regardless of the dependencies.

Extensions

I used some extensions to makes clean some codes. The library, WhatIf.

WhatIf is Kotlin extensions for expressing a single if-else statement, nullable and boolean. The library supports some expressions for nullable objects, WhatIfNot, WhatIfNotNull, WhatIfNotNullOrEmtpy or etc.

Here is a simple example of whatIfNotNull. We have to use unnecessary brackets or question marks to check the instance is null. Sometimes they make messy our codes or hard for us to read codes. So I published the nullable object’s scope expressions library WhatIf. However, I don’t recommend using it anywhere or making too many extensions using tricky functions. Before using them, discuss usability with your team members in advance and make rules.

Make your application’s design more beautiful

We looked around into some architectures including Android Jetpack components. And dependency injection, extensions.There are so many kinds of design patterns and architectures. It is important to find the best way to fit your project and design structure accordingly. 👍 Here is the full source code of the DisneyMotions repository.

If you prefer Marvel characters than Disney here is another sample Marvel heroes application based on MVVM architecture. 😆

--

--

Senior Android Developer Advocate @ Stream 🥑 • GDE for Android • OSS engineer. Love psychology, magic tricks, and writing poems. https://github.com/skydoves