└── 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 | --------------------------------------------------------------------------------