Android App Architecture MVVM Databinding Save

A simple but complete project (in both Java & Kotlin) to demonstrate the Android application architecture with MVVM pattern, a client app for The Movie DB Web API. Dagger2 is used for dependency injection and RxJava is used for RFP (Reactive Functional Programming).

Project README

Android App Architecture Demo - MVVM with databinding (Java version) [Kotlin version] [中文版]

This is a sample project to demonstrate the Android application architecture with MVVM pattern, a simple client for The Movie DB web API.

Note: you will need an API key if you would like to run the application, see Get started.

Table of Contents

Why MVVM?

For client application development, MVVM is better than other MV* patterns like MVC or MVP.

Why? Because a view model, as the extra abstraction of the view's data and behavior, has a higher abstraction level than other MV* patterns. Furthermore, view models are totally decoupled from views through databinding. The higher abstraction level and more complete decoupling lead to a cleaner architecture.

Any benefit comes with a cost, the abstraction and separation are not free. For projects that are complex enough, these initial cost will pay off soon. But for simple applications, it may be overkill.

For this demo project, using MVVM seems a little bit over engineering. But my purpose here is to demonstrate with a working example that is not too simple, and dealing with real problems in a real project. Furthermore, for a project whose goal is a full featured application like the client for "The Movie DB", this will be a good start.

Screenshots

Now Playing Page Movie Details Page Favorites

Application Introduction

The application has the following features:

  • The main page has three tabs: Now Playing, Favorite and Settings.
  • The Now Playing tab displays the movies now playing.
    • Each movie is shown as a poster picture in the grid, with rating stars and favorite state icon on top of it.
    • You can pull down the list to refresh and pull up at the end to load the next page.
  • The Favorite tab shows the local favorite list and has the same functionality as the Now Playing tab.
  • Clicking on the movie poster will navigate to the movie details page, in which additional details like backdrop image, tagline and similar movies will be shown.
    • The float action button in the page shows a progress circle animation while loading and the favorite state icons will show after loading. You can click the favorite icon to add to or remove from local favorite movie list.
    • You can pull up the similar movies list to load the next page.
  • In settings page, you can clear the cached HTTP responses, images, as well as local favorites.

Application Architecture

Architecture Layers

  • Dependency
    • Unidirectional dependency from top-down. Lower layers communicate with upper layers through various notification means:
      • View Models notify UI through databinding.
      • Models notify View Models through observable notification events.
      • Repository layer just returns values through RxJava event sources to Models.
    • Dependencies are injected by the DI (dependency injection) framework. View Models layer and Models layer components are all unit testable.
  • View models layer should have no dependency on UI components, and depend on Android as less as possible. (Current dependencies are the Android databinding framework, AndroidSchedulers from RxAndroid, and SparseArray.)
  • Models layer is the domain model for the application and should be UI and platform independent, i.e. depend on neither the interaction/UI design nor the Android platform. (Currently it depends on SparseArray just for performance reason, and can be replaced if needed.)
  • Repository layer is the abstraction for data access to local storage (Shared Preferences, SQLite databases) and external Web APIs.

Major Components

Most of the classe names are obvious. For those not so obvious:

Design decisions for the application

  1. Model-View-ViewModel (MVVM) architecture: which takes advantage of the native Android data-binding support.
  2. Decouple with Dependency Injection: using Dagger-2.
  3. Asynchrony: I/O operations should run in background with RxJava+RxAndroid.
  4. Activity navigation: URI based, decouple the activities. Implemented in NavigationHelper.
  5. Object Lifecycle
    • Entities in model layer are saved in the EntityStore (as WeakReferences to prevent memory leaks) which ensures the uniqueness (one object instance for each movie) so that different views of the same entity can be kept in sync through change notifications.
    • View models have the same lifetime as the corresponding views. They have one to one mapping to the views displayed. Otherwise the different lifetime will be very confusing and cause more problems than benefits (believe me, I tried that in the beginning).

Module/Directory Structure (Development View)

As the development view of the application architecture, it is an essential part to have a clear separation into modules and directory structure (packages in Java).

  • Modules division
    • app: main application module.
    • lib-common: common functionality
    • lib-databinding: databinding support
    • lib-widgets: reusable UI widgets
  • Directory structure in App module
    • di: dependency injection
      • components: Component and Subcomponent for Dagger.
      • modules: Module for Dagger.
      • qualifiers: qualification specifiers for dependency injection.
    • models: Model layer classes.
    • repository: data access layer
      • data: value objects used for JSON deserialization.
      • local: local storage, including SharedPreferences and SQLite databases.
      • util: utility classes.
      • web: to access Web API of TMDb.
    • ui: UI layer classes.
      • activity: Activities in the application.
      • databinding: BindingAdapters for databinding.
      • fragment: Fragments in the application.
      • nav: utility class NavigationHelper.
      • view: application related UI controls.
    • viewmodels: View Model layer classes.
  • Directory structure in Common module
    • objstore: object store
    • observable: observable pattern implementation for objects and collections.
    • util: utility classes.
  • Directory structure in Databinding module
    • adapter: list adapter for RecyclerView, supporting binding to ObservableList.
    • message: display notifications through databinding, so that view model can show notifications (Toast for now) without depending on UI controls.
  • Directory structure in Widgets module
    • behaviors: behaviors for CoordinatorLayout.
    • utils: utility classes. ImageLoader for now.
    • widgets: project independent, reusable UI controls.

Get started

Clone the project with submodules

Since the project has a submodule, you need to clone with --recurse-submodules parameter, or run git submodule update --init --recursive later to clone also the submodules.

Prepare TMDb API key

Before you can run the application, you need to register a developer account following the TMDb introductions and get the API key. Then add the API key in the project's gradle.properties file:

# API Key for the TMDb API
API_KEY="xxxxx"

Reference: https://developers.themoviedb.org/3/getting-started/authentication

Reusable Components

Project independent reusable components are developed in separate modules.

Common

  • Observables: enable obsever registrations (with weak references) and event notifications.
  • ObjectStore and ModelObjectStore: thread-safe object store which ensures that only one object will be associated with one key.

Data Binding

UI Layer

  • DynamicGridView: RecyclerView based Grid view that can automatically adjust number of columns based on available width and specified cell width. Gaps between cells are carefully caculated so that they are spanned evenly, while the header can occupy the full width.
  • FixedAspectRatioImage: AppCompatImageView with fixed aspect ratio (set through an attribut).
  • ImageLoader: load image with Glide. Can load image after the ImageView is measured.
  • AutoHideWhenScrollDownBehavior: A CoordinatorLayout behavior for the target view (e.g. BottomNavigationView) to auto hide when scroll down.

External Libraries/Frameworks/Widgets

The usage of the following well know libraries/frameworks/widgets are demonstrated in this project:

Code Quality

In order to ensure the code quality, the following project is use (as a submodule) in this application:

Notes

The signing configs comes from the project's gradle.properties file. You should add the following if you would like to sign the APK with your own key:

# signingConfigs for release build
RELEASE_STORE_FILE=xxx.xxx
RELEASE_STORE_PASSWORD=xxx
RELEASE_KEY_ALIAS=xxx
RELEASE_KEY_PASSWORD=xxx

License

Copyright (C) 2018, Brian He

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Open Source Agenda is not affiliated with "Android App Architecture MVVM Databinding" Project. README Source: BrianSpace/Android-App-Architecture-MVVM-Databinding

Open Source Agenda Badge

Open Source Agenda Rating