A Practical android framework (I)-architecture, android framework architecture
Source: http://saulmm.github.io/2015/02/02/A%20useful%20stack%20on%20android%20%231,%20architecture/
Source code github address: https://github.com/saulmm/Material-Movies
Author: Sa úl Molinero
Note: This is a recent project on Android architecture. It is also based on MVP, And the subcontracting ideas are quite compatible with me. In addition, Material Design is also used for this project, which is novel and practical. Therefore, we decided to translate the blog corresponding to this project for your reference.
This is the first chapter in this article. It describes how to build a basic environment for developing an extensible, maintainable, and testable android project. In this section, I will introduce some of the modes and tool libraries used. These models and libraries ensure that your project will not become out of control due to daily development.
Scenario
I will explain the following project as an example. This project is a simple movie classification app that supports marking a movie in multiple ways (read and coming soon ).
Movie data is obtained from a public API themoviedb. You can obtain a complete document from Apiary about the API description.
This project is based on the Model-View-Presenter mode, and also implements some Material Design features, such as transition effect, UI structure, animation, and color.
All the code is on Github and can be downloaded or read at will. At the same time, there is also a video to show the effect of the app.
Architecture
This architecture is designed based on MVP. MVP is a change in the MVC Architecture.
The MVP architecture tries to extract the business logic from the presentation layer (Presenter. In android, this is a very important process. Because the android architecture is also promoting the separation of business logic and presentation, the two are connected through the data layer. Several typical examples are Adapter and CursorLoader.
This architecture makes modifications to the view unnecessary to affect the business logic layer and data layer. The domain and conversion tools responsible for converting multiple data sources (databases or rest apit) can be easily reused.
Overview
The overall architecture can be divided into three parts:
- Presentation
- Model
- Domain
Presentation
The Presentation layer displays graphical interfaces and fills in data.
Model
The Model layer provides data. The Model layer does not know any data about Domain and Presentation. It can be used to connect to data sources (databases, REST APIs, or other sources) or interfaces.
At the same time, this layer also implements the entity classes required by the entire app, such as classes used to represent movies or categories.
Domain
The Domain layer is completely independent from the Presentation layer and implements the business logic of the app. (Translator's note: here the so-called business logic may be somewhat confused in terms of the Presenter function. For example, if usecase receives a json string containing a list of movies, convert the json string to json and package it into an ArrayList, this should be done by usecase. If the size of the ArrayList is 0, that is, the list is empty, the default graph needs to be displayed. This judgment and control should be done by the presenter .)
Implementation
Domain and Model layers are placed in two java modules respectively (note: this means that there is no android dependency). The Presentation layer is the default app module, which is also called an android Application. At the same time, I added a common module to share the support library and tool classes directly in each module.
Domain Module
The Domain module stores some usecases and their implementations. These are the implementation of application business logic. This module is completely independent from the android framework. Its Dependency only comes from the model module and general module.
A usecase can be used to obtain the total score of each category of a movie, so as to obtain the most popular category. To implement this function, usecase may need to obtain data and perform computation. Data is provided by the model module.
dependencies { compile project (':common') compile project (':model')}
Model Module
The Model module manages data, such as data acquisition, storage, and deletion. In the first version, I used the REST API to manage movie data.
At the same time, this module also implements some entity classes. For exampleTvMovie
Is used to represent a movie.
It depends only on the common module and the support library used to initiate network requests. For this feature, I used the feature FIT developed by Square. I will introduce Retrofit in the next blog.
dependencies { compile project(':common') compile 'com.squareup.retrofit:retrofit:1.9.0'}
Presentation module
This module is the android application, including its resource, asset, and logic. It also interacts with the domain layer by running usecase. For example, you can obtain a list of movies within a period of time and request the specific data of a movie.
This module contains presenter and view. EveryActivity
,Fragment
AndDialog
Both implement the View Interface in an MVP. This interface specifies the operations that a View needs to support, including displaying, hiding, and displaying data. For example:PopularMoviesView
Defines an interface to display the current list of movies.MoviesActivity
Completed.
public interface PopularMoviesView extends MVPView { void showMovies (List<TvMovie> movieList); void showLoading (); void hideLoading (); void showError (String error); void hideError ();}
The MVP mode was designed to make the View simpler and better. The View behavior should be determined by the Presenter, rather than the View itself.
public class MoviesActivity extends ActionBarActivity implements PopularMoviesView, ... { ... private PopularShowsPresenter popularShowsPresenter; private RecyclerView popularMoviesRecycler; private ProgressBar loadingProgressBar; private MoviesAdapter moviesAdapter; private TextView errorTextView; @Override protected void onCreate(Bundle savedInstanceState) { ... popularShowsPresenter = new PopularShowsPresenterImpl(this); popularShowsPresenter.onCreate(); } @Override protected void onStop() { super.onStop(); popularShowsPresenter.onStop(); } @Override public Context getContext() { return this; } @Override public void showMovies(List<TvMovie> movieList) { moviesAdapter = new MoviesAdapter(movieList); popularMoviesRecycler.setAdapter(moviesAdapter); } @Override public void showLoading() { loadingProgressBar.setVisibility(View.VISIBLE); } @Override public void hideLoading() { loadingProgressBar.setVisibility(View.GONE); } @Override public void showError(String error) { errorTextView.setVisibility(View.VISIBLE); errorTextView.setText(error); } @Override public void hideError() { errorTextView.setVisibility(View.GONE); } ...}
Usecase will be executed in the presenter. The presenter will accept the returned data and control the view behavior based on the data.
public class PopularShowsPresenterImpl implements PopularShowsPresenter { private final PopularMoviesView popularMoviesView; public PopularShowsPresenterImpl(PopularMoviesView popularMoviesView) { this.popularMoviesView = popularMoviesView; } @Override public void onCreate() { ... popularMoviesView.showLoading(); Usecase getPopularShows = new GetMoviesUsecaseController(GetMoviesUsecase.TV_MOVIES); getPopularShows.execute(); } @Override public void onStop() { ... } @Override public void onPopularMoviesReceived(PopularMoviesApiResponse popularMovies) { popularMoviesView.hideLoading(); popularMoviesView.showMovies(popularMovies.getResults()); }}
Communication
In this project, I chose the MessageBus system. This system is very helpful for sending broadcast events or establishing communication in each module. This is exactly what we need. Simply put, an event is sent to the Bus, and the class that needs to handle this event must subscribe to the Bus. Using this system can greatly reduce the coupling between modules.
To implement the bus of this system, I used the library Otto developed by Square. I declare two buses. One (REST_BUS) is used to implement direct communication between usecase and REST APIs, and the other (UI_BUS) sends events to the presentation layer. Among them, REST_BUS will use any available thread to process the event, while UI_BUS will only send the event to the default thread, that is, the main UI thread.
public class BusProvider { private static final Bus REST_BUS = new Bus(ThreadEnforcer.ANY); private static final Bus UI_BUS = new Bus(); private BusProvider() {}; public static Bus getRestBusInstance() { return REST_BUS; } public static Bus getUIBusInstance () { return UI_BUS; }}
This class is managed by a common module, because all modules have dependencies on this module, and you can also use this module to operate the bus.
dependencies { compile 'com.squareup:otto:1.3.5'}
Finally, let's think about this situation: when a user opens an application, the most popular movies will first be displayed.
WhenonCreate
When the method is called, presenter subscribes to UI_BUS to listen for events. Then, the presenter will executeGetMoviesUseCase
To initiate a request. Presenter willonStop
Cancels the subscription when it is called.
@Overridepublic void onCreate() { BusProvider.getUIBusInstance().register(this); Usecase getPopularShows = new GetMoviesUsecaseController(GetMoviesUsecase.TV_MOVIES); getPopularShows.execute();}...@Overridepublic void onStop() { BusProvider.getUIBusInstance().unregister(this);}
To receive events, the presenter must implement a method. The parameters of this method must be consistent with those of the events sent to the bus. And this method must be annotated@Subsribe
Tagging
@Subscribe@Overridepublic void onPopularMoviesReceived(PopularMoviesApiResponse popularMovies) { popularMoviesView.hideLoading(); popularMoviesView.showMovies(popularMovies.getResults());}
Reference
- Architecting Android... The clean way? -Fernando Cejas Chinese Link
- Proactive Android UI-Pedro Vicente G ómez Sanchez
- Reactive programming and message buses for mobile-Csaba Palfi
- The clean architecture-Uncle Bob
- MVP Android-Antonio Leiva
Translator's summary
The author of this project also summarized a set of templates based on several famous Android architectures. In terms of architecture and subcontracting, it is in line with my idea Android architecture practice (I)-core ideas, which are much simpler than a complete set of processes and more suitable for small teams (1-3 individuals) development.
The difference is that this project uses the event bus to implement inter-module communication. Compared with RxJava, the event bus is easy to understand. However, the fault of the event bus is that the dependency is weak, that is, you cannot easily find out who initiated the event. This effect can be understood as low coupling, or "lost" during maintenance. Therefore, I prefer RxJava's design pattern.
There are two subsequent chapters on this project, mainly about Material Design and its compatibility. It is not particularly profound, but if you need the same UI effect, you can refer to it and I will translate it for your convenience.