Weather Forecast applies MVVM combined with Clean Architecture
Hello this is my simple Weather Forecast project as a applicant's challenging assignment for applying for the Android developer position at Viet Nam Technology Center National Australia Bank Limited .
In this project I apply MVVM in combination with clean architecture and more...
MVVM is now one of the most loved patterns out there, as it has had plenty of time to mature. The web community adopted the pattern after the Microsoft team formalized it in 2005. It eventually made its way into every UI-based framework. One key advantage of MVVM is that it offers the right amount of decoupling. Another good thing is that the learning curve is similar to the other patterns.
MVVM has three main components: Model, View, and ViewModel.
Unlike MVP and MVC, there’s a fourth component: The Binder. This is the mechanism that links the Views to the ViewModels. The Binder is usually handled by the platform or a third party library, so the developer doesn’t have to write it. For Android, you have the DataBinding library at our disposal.
MVVM separates our view (i.e. Activities and Fragments) from our business logic. MVVM is enough for small projects, but when our codebase becomes huge, our ViewModels start bloating. Separating responsibilities becomes hard.
MVVM with Clean Architecture is pretty good in such cases. It goes one step further in separating the responsibilities of our code base. It clearly abstracts the logic of the actions that can be performed in our app.
-
our code is even more easily testable than with plain MVVM.
-
our code is further decoupled (the biggest advantage.)
-
The package structure is even easier to navigate.
-
The project is even easier to maintain.
-
our team can add new features even more quickly.
Presentation layer: is the most familiar layer to us, it contains activities, fragments and view models etc. This layer has two responsibilities: get triggered then request data and show data that comes from domain layer. Additionally this is the layer that system gives resources to run our app.
MVVM structure is a good way to make presentation layer reliable. Using view model is the key point, it reduces lifecycle effects and gives an coroutine scope easily (viewModelScope).
In our design the view model executes interactors in desired coroutine scope and post it to a live data as DataHolder. Then this live data observed in activity/fragment and necessary UI operations performed according to it’s success/fail/loading state.
By that way activity/fragment only knows and observes view model, that separates framework from inner circle.
Domain layer: is the center of the structure, it connects the data and presentation layer using interfaces and interactors. It is also completely independent and can be tested regardless of external components. Each domain layer has a unique use case, repository and business model.
Data layer: has all the repositories which the domain layer can use. Data layer handles all enterprise logic and produces data for our app. All complex logic must belong here. It must also be as independent as possible from platform. So it can be unit tested and modifiable. It mainly consists of 3 parts: Data Source, Repository Implementation and Mapper.
This project contains 5 main packages: presentation, domain, data, buildSrc and common
-
base package contains base classes such as BaseActivity, BaseViewModel, BaseAdapter,...
-
callback package contains interface callbacks
-
common package contains common classes such Contants,...
-
di package contains dependency injection classes using Hilt
-
extensions package contains classes defining kotlin extension functions used mostly by this layer.
-
feature package contains all features (screens) in the application. It has activities, viewmodels, popups, that call to usecases down to Domain layer
-
mapper package contains mapper classes that support to map objects between Domain and Presentation
-
model package contains ui-related models
-
util package contains helper classess
-
WeatherForecastApp class and of course the custom application.
-
di package contains dependency injection classes using Hilt
-
entity package package contains all of entities
-
repository package contains interfaces that will be implemented in the Data layer
-
useCase package contains usecases that will be called by ViewModels in the Presentation layer
-
di package contains dependency injection classes that use Hilt
-
entity package contains all of entities
-
extensions package contains classes defining kotlin extension functions used mostly by this layer.
-
local package contains database classes using Room
-
mapper package contains mapper classes that mapping objects between Data layer and Domain layer
-
remote package contains api define classes using Retrofit, Okhttp3
-
repository package contains the implementations of interfaces in Domain layer
-
secureapi package contains a JNI APIKeyLibrary object that is used to get sensetive data, secret keys, license keys,... from the .so file. (see Storing secret keys and API keys)
This module conatins constants, util classes which are shared to other modules.
This module contains nothing but a Dependencies.kt declaring all dependencies is using in entire project.
Security is the key requirement while building an application that’s dealing with APIs, Tokens received from a server, storing credentials in local DB, sending secure data onto the network, and many other scenarios. The developers must try their best to build a secure application to prevent it from being bypassed by a hacker.
And Weather Forecast is no exception, in spire of that this is just a really simple and tiny project. so in the project I build 3 senarios for securing secret keys and sensetive data.
Secret keys are stored in a .so file using Android NDK (Native Development Kit) to build. (see this project)
How do I retreive the keys? see RemoteModule#provideSSLCertificate
Runtime sensitive data is stored locally using EncryptedSharedPreferences an implementation of SharedPreferences that encrypts keys and values. see
The Secure Sockets Layer (SSL)—now technically known as Transport Layer Security (TLS)—is a common building block for encrypted communications between clients and servers.
How did you integrate HTTPS and SSL? see RemoteModule#provideOkHttpClient
-
preventing rooted devices from using the app.
-
Enabling proguard
Dark theme is available in Android 10 (API level 29) and higher. It has many benefits:
-
Can reduce power usage by a significant amount (depending on the device’s screen technology).
-
Improves visibility for users with low vision and those who are sensitive to bright light.
-
Makes it easier for anyone to use a device in a low-light environment.
In Weather Forecast, themes are structure as in the diagram:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- platform theme-->
<style name="Platform.Theme.LP" parent="Platform.V21.Theme.LP" />
<style name="Platform.V21.Theme.LP" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
</style>
<!-- end platform theme-->
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- platform theme-->
<style name="Platform.Theme.LP" parent="Platform.V23.Theme.LP" />
<style name="Platform.V23.Theme.LP" parent="Platform.V21.Theme.LP">
<!-- Attributes which are only available from API 23 -->
</style>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- base theme-->
<style name="Base.Theme.LP" parent="Platform.Theme.LP">
<!-- global fonts used in whole app.-->
<item name="android:textViewStyle">@style/MyTextAppearance</item>
<item name="android:buttonStyle">@style/MyButtonStyle</item>
</style>
<!-- end base theme-->
</resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.WeatherForecast" parent="Theme.LP.Light" />
<style name="Theme.LP.Light" parent="Base.Theme.LP">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryVariant">@color/colorPrimaryVariant</item>
<item name="colorOnPrimary">@color/colorOnPrimary</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/colorSecondary</item>
<item name="colorSecondaryVariant">@color/colorSecondaryVariant</item>
<item name="colorOnSecondary">@color/colorOnSecondary</item>
<!-- Status bar color. -->
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
<item name="colorSurface">@color/colorOnPrimary</item>
<item name="android:colorBackground">@color/colorOnPrimary</item>
</style>
</resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.WeatherForecast" parent="Theme.LP.Dark" />
<style name="Theme.LP.Dark" parent="Base.Theme.LP">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryVariant">@color/colorPrimaryVariant</item>
<item name="colorOnPrimary">@color/colorOnPrimary</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/colorSecondary</item>
<item name="colorSecondaryVariant">@color/colorSecondaryVariant</item>
<item name="colorOnSecondary">@color/colorOnSecondary</item>
<!-- Status bar color. -->
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
<item name="colorSurface">@color/colorPrimary</item>
<item name="android:colorBackground">@color/colorPrimary</item>
</style>
</resources>
-
Talkback: Weather Forecast has contentDescription for supporting the screen reader.
-
Scaling Text: Weather Foreast allows user to change the text size for entire app.
Local tests using junit4 and mockito see and see
Instrumented tests using Robolectric with AndroidJunit4 + AndroidX Test APIs see and see
Instrumented tests for Room Dao Object (database access object) see
Ui tests using Expresso + data binding + Hilt see
The project contains additional configuration for Jacoco that enables coverage report for Unit Tests (initially Jacoco reports cover Android Instrumentation Tests).
The configuration file could find here
To run tests coverage, from the project directory WeatherForecast execute:
./gradlew testDebugUnitTestCoverage
// or
./gradlew -stacktrace --info testDebugUnitTestCoverage
The coverage report can be found in WeatherForecast/build/coverage-report/index.html
- Clone the project down to your computer.
- Open it in Android Studio (Android Studio 4.0 or newer are recommended)
- To build debug version you just need to click on the play button on Android Studio.
- To build release version you need to use your signed key and modify the file keystore.properties respectively.
- Or just play with this apk
-
Programming language: Kotlin is required, Java is optional ✔
-
Design app's architecture (suggest MVVM) ✔
-
Apply LiveData mechanism ✔
-
UI should be looks like in the attachment ✔
-
Write UnitTests ✔
-
Acceptance Tests
-
Exception handling ✔
-
Caching handling ✔
-
Secure Android app from:
a. Decompile APK ✔
b. Rooted device ✔
c. Data transmission via network ✔
d. Encryption for sensitive information ✔
-
Accessibility for Disability Supports:
a. Talkback: Use a screen reader ✔
b. Scaling Text: Display size and font size: To change the size of items on your screen, adjust the display size or font size ✔
-
Entity relationship diagram for the database and solution diagrams for the components, infrastructure design if any ✔
-
Readme file includes:
a. Brief explanation for the software development principles, patterns & practices being applied ✔
b. Brief explanation for the code folder structure and the key Java/Kotlin libraries and frameworks being used ✔
c. All the required steps in order to get the application run on local computer ✔
d. Checklist of items the candidate has done. ✔
Last but not least, I would really apprciated you for taking time to review my project so far, this is just such a simple project having done in my spare time in 1 week (of course I have tasks at my company as well) can't express all my abilities and experiences, so I hope I can have chance to talk and and discuss with you about the Android development.
Thanks ❤️ and happy coding! 💻