└── README.md
/README.md:
--------------------------------------------------------------------------------
1 | # An Introduction to MVP on Android
2 | It's already proven1 that using the default architecture provided by the Android SDK isn't a good choice for building complex Android applications which are meant to be **testable**, **maintainable** and always kept **up to date**. This article is going to introduce **MVP** (Model View Presenter) pattern as a better approach to build your applications and help you easily develop better Android applications based on MVP pattern using [**EasyMVP** library](https://github.com/6thsolution/EasyMVP).
3 | So first let's observe MVP on Android and then we'll dig into its implementaion with the help of EasyMVP library.
4 |
5 | * [What Is MVP](#what-is-mvp)
6 | * [MVP On Android](#mvp-on-android)
7 | * [Why?](#why)
8 | * [How MVP solves these problems?](#how-mvp-solves-these-problems)
9 | * [Starting Guide](#starting-guide-using-easymvp-to-implement-mvp-on-android)
10 | * [MvpView](#mvpview-code)
11 | * [MvpPresenter](#mvppresenter-code)
12 | * [MvpActivity](#mvpactivity-code)
13 | * [Best Practice](#best-practice-easymvp-and-clean-architecture)
14 | * [How does EasyMVP work?](#how-does-easymvp-work?)
15 | * [Refrences](#refrences)
16 |
17 |
18 | ## What Is MVP?
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | [MVP](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93presenter) stands for Model View Presenter and is an **architectural pattern** that separates an application into three layers:
27 |
28 | - **Model:** holds the business logic of our application and is responsible for all data interactions in the application.
29 | - **View:** a passive interface that is responsible for all the UI/UX related things, shows data to the user and reacts to the input events
30 | - **Presenter:** acts as the middle man, provides view with the data from model and defines reactions to user input which view forwards to it.
31 |
32 | As mentioned [here](https://martinfowler.com/eaaDev/PassiveScreen.html), there is a variation of MVP with **Passive View**. In passive view (***which is considered in this article***), the view layer is just an slave of the presenter, meaning that it just **forwards the inputs** to the presenter and **obeys its commands**. Using this approach, **independent** testing of the layers gets a lot easier.
33 |
34 | ## MVP On Android
35 |
36 | As default android architecture wasn't developed with good attention to **separation of concerns**, MVP is used to separate the UI/UX (Activity, Fragment, ...) from the I/O and application's business logic. But how does this separation help us?
37 |
38 | ###Why?
39 | In default Android application architecture, activities (or their replacements, like fragments and ...) are **god objects** and are extremely **coupled** to both UI interface and business logic or data manipulations. In this god object, literally *everything is connected to everything* and the code is **over complicated**. So changing a small portion of the code requires updating the entire code and takes a lot of effort. Moreover, since the different parts of the code are connected, no part will be **reusable** nor **testable**.
40 |
41 | Moreover, handling the background tasks in this code is really a pain in the neck. Apart from having to handle numerous edge cases for **configuration changes**, the probability of having **memory leaks** grows exponentially as your code gets more and more complex.
42 | Adding new parts and features to this code is also extremely sorrowful because you'll probably need to check most of the code and update it!
43 |
44 | Finally you'll find multiple bugs which are not at all easy to even find, let alone **debugging** and solving them.
45 |
46 | ### How MVP solves these problems?
47 |
48 |
49 |
50 |
51 |
52 |
53 | The answer is: **By separating the application into three main layers!**
54 |
55 | The **view** which is normally the activity or fragment is only responsible for the UI/UX part, like showing data to the user, handling animations, forwarding user input to presenter and navigating between screens.
56 | The view is also responsible for handling lifecycle events for presenter and **saving/restoring the presenter** when needed (for example in the onResume/onPause methods of an activity).
57 |
58 | The **model** layer is only responsible for data interactions and all the **business logic** and data manipulations and doesn't know anything about the application's UI and doesn't even have a reference to the presenter or the view.
59 |
60 | The **presenter** layer is a new part which isn't available in the plain old android architecture. This layer almost acts as the brain of the application and **decides** everything about the application's behavior. Presenter in android is a **pure java** class that we're able to do **unit tests** on it. It fills the view layer with the data that it retrieves from the model layer.
61 |
62 | With this separation, there's no god object and each layer follows this connection rule:
63 | **Picture of MVP design: Model <-> View <-> Presenter**
64 |
65 | This way our code isn't complicated, different parts can be easily **maintained**, **tested**, **debugged** and **reused**. As view saves and restores presenter and handles the presenter's lifecycle, the **background tasks** are now performed in presenter without concerning about the lifecycle events and configuration changes. They are saved/restored automatically while we're sure there'll be **no memory leak**.
66 |
67 | **Abstraction** of these layers helps us make sure that no layer knows anything about the implementation of any other layer and each part of the application is completely **decoupled**.
68 |
69 | ## **Starting Guide**: Using EasyMVP to implement MVP on Android
70 | In this example we're going to fetch data from a remote server and show it in our activity as a `ListView`. We'll use **EasyMVP** to implement MVP pattern in our approach. So as mentioned above, the `activity` is going to play the **view** role, the **presenter** is going to be a java class and the **model** retrieves information from the remote server and provides a `RxJava Observable` for the presenter to use.
71 |
72 |
73 |
74 |
75 |
76 |
77 | These classes show how the above idea can be easily implemented using *only a few annotations* from **EasyMVP** :
78 |
79 |
80 | #### **MvpView** code:
81 |
82 | ```java
83 | public interface MvpView {
84 | void showData(List items);
85 | void showLoading();
86 | void showError();
87 | }
88 | ```
89 |
90 | #### **MvpPresenter** code:
91 |
92 | ```java
93 | public class MvpPresenter extends RxPresenter {
94 |
95 | private List items;
96 |
97 | public void reloadData() {
98 | this.items = null;
99 | loadData();
100 | }
101 |
102 | private void loadData() {
103 |
104 | getView().showLoading();
105 |
106 | /**
107 | * Loading our data from a Web API using an {@link rx.Observable} from RxJava
108 | */
109 | Subscription subscription = API.getDataObservable()
110 | .subscribeOn(Schedulers.io())
111 | .observeOn(AndroidSchedulers.mainThread())
112 | .subscribe(
113 | items -> {
114 | // Showing data when items are retrieved
115 | this.items = items;
116 | // Checking if the view is still accessible
117 | if (isViewAttached()) {
118 | getView().showData(items);
119 | }
120 | },
121 | throwable -> {
122 | // Printing the log and showing the error
123 | throwable.printStackTrace();
124 | // Checking if the view is still accessible
125 | if (isViewAttached()) {
126 | getView().showError();
127 | }
128 | }
129 | );
130 |
131 | /**
132 | * Adding the {@link Subscription} to the presenter so
133 | * it will automatically get unsubscribed at {@link RxPresenter#onDestroyed()}
134 | */
135 | this.addSubscription(subscription);
136 |
137 | }
138 |
139 | @Override
140 | public void onViewAttached(MvpView view) {
141 | super.onViewAttached(view);
142 | // Checking if the data has been loaded before
143 | if (items == null) {
144 | loadData();
145 | }
146 | }
147 | }
148 |
149 | ```
150 |
151 | #### **MvpActivity** code:
152 |
153 | ```java
154 | @ActivityView(presenter = MvpPresenter.class, layout = R.layout.activity_mvp)
155 | public class MvpActivity extends AppCompatActivity implements MvpView {
156 |
157 | private ListView listView;
158 | private LinearLayout errorView;
159 | private ProgressBar progressBar;
160 |
161 | private ArrayAdapter adapter = new ArrayAdapter<>(this, R.layout.item);
162 |
163 | @Presenter
164 | MvpPresenter presenter;
165 |
166 | @Override
167 | protected void onCreate(Bundle savedInstanceState) {
168 | super.onCreate(savedInstanceState);
169 |
170 | initializeViews();
171 | listView.setAdapter(adapter);
172 |
173 | findViewById(R.id.reloadbutton).setOnClickListener(new View.OnClickListener() {
174 | @Override
175 | public void onClick(View view) {
176 | presenter.reloadData();
177 | }
178 | });
179 |
180 | }
181 |
182 | private void initializeViews() {
183 | listView = (ListView) findViewById(R.id.listview);
184 | errorView = (LinearLayout) findViewById(R.id.errorview);
185 | progressBar = (ProgressBar) findViewById(R.id.progressbar);
186 | }
187 |
188 | @Override
189 | public void showData(List items) {
190 |
191 | adapter.clear();
192 | adapter.addAll(items);
193 |
194 | errorView.setVisibility(View.GONE);
195 | progressBar.setVisibility(View.GONE);
196 | listView.setVisibility(View.VISIBLE);
197 |
198 | }
199 |
200 | @Override
201 | public void showLoading() {
202 | listView.setVisibility(View.GONE);
203 | errorView.setVisibility(View.GONE);
204 | progressBar.setVisibility(View.VISIBLE);
205 | }
206 |
207 | @Override
208 | public void showError() {
209 | listView.setVisibility(View.GONE);
210 | progressBar.setVisibility(View.GONE);
211 | errorView.setVisibility(View.VISIBLE);
212 | }
213 |
214 | }
215 | ```
216 |
217 | As you might have noticed, we use `@ActivityView` to attach the presenter to the activity and set its layout id and `@Presenter` in the activity to define the presenter field. The `activity` implements the `view interface` so that we're sure its completely decoupled from `presenter`.
218 |
219 | In presenter's `OnViewAttached` method we check if we should load the data because it will get called everytime the view becomes on screen such as in `onCreate` method of the activity (more information can be found [here](https://github.com/6thsolution/EasyMVP/)).
220 |
221 | Finally, the activity (view) forwards any button click to the presenter so the user input is also handled in the presenter and there's **no business logic in view**.
222 |
223 | EasyMVP does all the hard work here, like saving/restoring presenter's stMVate in and preventing any **memory leaks** by releasing the view in presenter in lifecycle events. You can proceed to the next example to see all EasyMVP features.
224 |
225 | ## Best practice: EasyMVP and Clean Architecture
226 | **Comming soon:** we're publishing an example to observe all features of EasyMVP in developing a feature rich Android application based on Clean Architecture and MVP pattern.
227 | ## How does EasyMVP work?
228 | **Comming soon**: we're going to publish an article on how EasyMVP works to help us build better Android applications.
229 |
230 | ### Refrences
231 | 1: There are a lot of articles out there that show why MVP should be used in Android applications, some of them are listed here:
232 |
233 | - https://antonioleiva.com/mvp-android/
234 | - http://engineering.remind.com/android-code-that-scales/
235 | - https://www.ackee.cz/en/blog/an-introduction-to-mvp-on-android/
236 | - https://code.tutsplus.com/tutorials/an-introduction-to-model-view-presenter-on-android--cms-26162
237 |
--------------------------------------------------------------------------------