Hands-on MVP in Android application development

Sakib Sami
LiveKlass
Published in
5 min readOct 1, 2017

What is MVP ?

-> MVP stands for Model-View-Presenter.

MVP is a software development methodology of keeping UI, Business logic, IO and other things separated by group. Like in a real world application we may have lots of activities, data models, network service, database connections etc and we have to keep them separate by group as well as we should separate related tasks.

Such as we shouldn’t do a network call from view class.

Have a look on the above package hierarchy.
ninja.sakib.androidmvpexample is the main package where we are going to put all the codes. Then we have models package to keep model classes, networkio to keep network related classes, ui to keep UI related classes, presenter to keep presenter classes. There could lot more packages. Also this groups have subgroups like in models we have two sub group db and network. db is to keep database entity models and network to keep network related models like you call a api to do login and the api will return few data, to hold this data you may need a class put them here.

In software engineering its a design principle called Separation of Concern (SoC).

Now we will see example, how you can implement MVP in android application. For that we will use Moxy library. Its a opensource library licensed under MIT.

Dependencies,

compile 'com.arello-mobile:moxy:1.5.3'
compile 'com.arello-mobile:moxy-android:1.5.3'
annotationProcessor 'com.arello-mobile:moxy-compiler:1.5.3'

Lets say we are implementing login functionality in our application.

MvpView -> is the skeleton of your activity class. Its a interface and figure out the tasks you need to do in activity class. Such as,

public interface LoginView extends MvpView {
void onLoginResponse(LoginResponse loginResponse);
}

Note : LoginResponse is a network model to hold the login response data.

MvpPresenter -> presenter is where we should keep our business logic.

@InjectViewState
public class LoginPresenter extends MvpPresenter<LoginView> {
private UserApi userApi = new UserApi();
public void onLoginRequest(LoginRequest loginRequest) {
Log.d("Where", "LoginRequestedOnPresenter");
try {
LoginResponse loginResponse = userApi.onUserLogin(loginRequest);
Log.d("Where", "LoginResponseOnPresenter");
getViewState().onLoginResponse(loginResponse);
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
}
}

Few things to check here. We have a method named onUserLogin(LoginRequest loginRequest) in UserApi.

// This is UserApi class to handle User API related task
public class UserApi {
public LoginResponse onUserLogin(LoginRequest loginRequest) throws ExecutionException, InterruptedException {
// LoginTask will be called here
LoginAsyncTask loginAsyncTask = new LoginAsyncTask();
return loginAsyncTask.execute(loginRequest).get();
}
}

And here we have a asynctask named LoginAsyncTask.

public class LoginAsyncTask extends AsyncTask<LoginRequest, Integer, LoginResponse> {@Override
protected LoginResponse doInBackground(LoginRequest... loginRequests) {
for (int i = 1; i <= 15; i++) {
Log.d("Where", "Timer " + i);
try {
Thread.sleep(1000);
} catch (Exception ex) {
}
}
return new LoginResponse();
}
}

We will actually do network call here. Demo purpose did Thread.sleep(1000) call, to keep the thread blocking for 15s and return LoginResponse. In real world you may have to do api call, parse the result then put them in login response class and return it.

MvpActivity -> Keep UI implementation here.

public class LoginActivity extends MvpActivity implements LoginView {
@InjectPresenter
public LoginPresenter loginPresenter;
private Button loginBtn;@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
Log.d("Where", "ViewCreated");loginBtn = findViewById(R.id.loginBtn);
loginBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d("Where", "LoginRequested");
loginPresenter.onLoginRequest(new LoginRequest());
}
});
}
@Override
public void onLoginResponse(LoginResponse loginResponse) {
Log.d("Where", "LoginResponse");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d("Where", "ViewDestroyed");
}
}

Flow Diagram

One of the most important feature of moxy is handling activity view state.
Lets assume a scenario where user did login request and rotated the device so UI will be destroyed and created with new orientation. In general In this case UI will lost all related objects and asynctask as it has been send to background thread. In the case moxy will hold the current view state and wait till it finishes current task (login request) then will change view state. You can change this behaviour as per your requirement.

Check the below log.
Here user requested for login in Portrait mode and then rotate the device to landscape. Moxy holds the Portrait view state finishes the task and changes the view state to landscape with login response. But in general android scenario login activity will lose the background thread and user have to do click login button again to make login request.

10-01 21:12:06.992 3775-3775/ninja.sakib.androidmvpexample D/Where: LoginRequested
10-01 21:12:06.992 3775-3775/ninja.sakib.androidmvpexample D/Where: LoginRequestedOnPresenter
10-01 21:12:07.002 3775-5887/ninja.sakib.androidmvpexample D/Where: Timer 1
10-01 21:12:08.003 3775-5887/ninja.sakib.androidmvpexample D/Where: Timer 2
10-01 21:12:09.004 3775-5887/ninja.sakib.androidmvpexample D/Where: Timer 3
10-01 21:12:10.005 3775-5887/ninja.sakib.androidmvpexample D/Where: Timer 4
10-01 21:12:11.006 3775-5887/ninja.sakib.androidmvpexample D/Where: Timer 5
10-01 21:12:12.007 3775-5887/ninja.sakib.androidmvpexample D/Where: Timer 6
10-01 21:12:13.018 3775-5887/ninja.sakib.androidmvpexample D/Where: Timer 7
10-01 21:12:14.029 3775-5887/ninja.sakib.androidmvpexample D/Where: Timer 8
10-01 21:12:15.030 3775-5887/ninja.sakib.androidmvpexample D/Where: Timer 9
10-01 21:12:16.041 3775-5887/ninja.sakib.androidmvpexample D/Where: Timer 10
10-01 21:12:17.042 3775-5887/ninja.sakib.androidmvpexample D/Where: Timer 11
10-01 21:12:18.053 3775-5887/ninja.sakib.androidmvpexample D/Where: Timer 12
10-01 21:12:19.044 3775-5887/ninja.sakib.androidmvpexample D/Where: Timer 13
10-01 21:12:20.055 3775-5887/ninja.sakib.androidmvpexample D/Where: Timer 14
10-01 21:12:21.056 3775-5887/ninja.sakib.androidmvpexample D/Where: Timer 15
10-01 21:12:22.057 3775-3775/ninja.sakib.androidmvpexample D/Where: LoginResponseOnPresenter
10-01 21:12:22.057 3775-3775/ninja.sakib.androidmvpexample D/Where: LoginResponse
10-01 21:12:22.067 3775-3775/ninja.sakib.androidmvpexample D/Where: ViewDestroyed
10-01 21:12:22.237 3775-3775/ninja.sakib.androidmvpexample D/Where: ViewCreated
10-01 21:12:22.237 3775-3775/ninja.sakib.androidmvpexample D/Where: LoginResponse

The final package structure,

GET THE SOURCE CODE

Using MVP will keep the UI, Business Logic separate and it will help to create more boilerplate less, testable code. And changing code in one part won’t effect another.

Originally published at www.sakib.ninja on October 1, 2017.

--

--

Sakib Sami
LiveKlass

Senior Software Engineer @ Twilio | Entrepreneur | Tech Ninja