This article briefly describes the problems encountered in the use of MVP architecture in the actual project and the corresponding solutions, and finally sorted out the upgraded VERSION of MVP architecture.
Android Architecture series
This series of articles will update you on some of the best architecture and tips for Android project development
How to write an Intent series 3 Android architecture series 3 Android architecture series 4 Android architecture series 5 Encapsulate your own OkHTTP series Android Architecture Series – Practical application of MVP architecture
1 Original MVP structure
Using the MVP architecture was introduced in the first article in this series. Check back for details
The MVP structure looks like this:
2. Problems in the application of actual projects
MVP is a layering idea of code that doesn’t actually use any libraries, but just tells you how to place your code neatly. Make each level of code do its job, increasing readability and testability.
In real development, however, MVP is a highly cohesive pattern in a module where the Presenter layer takes over the logical implementation of the Activity. The following problems have arisen:
2.1 Presenter lifecycle issues
The Presnter layer and the View layer are one-to-one, so the Presnter layer and the View layer have the same life cycle.
But now all the logic is written in the Presenter layer, and if it needs to be called elsewhere it can only be called through static methods. You can’t create a Presenter instance again
2.2 Cross-module invocation
In the actual development, there is often part of the logic in B module to call A module.
For example, when Posting, we should judge whether the user is logged in or not, and obtain the information of the current logged in user. That is, in the post module to get the user module’s data and logic.
If the logic is written in Presenter, other modules can only read the current user cache directly and then parse it in their own modules. There is also increased coupling between modules.
3. Optimized MVP layering
Name the Model layer Interactor here. We write the atomic logic inside each module (a function rather than a series of logical functions) in interactor. The Presenter layer is only responsible for receiving view events, calling interactor functions, and giving back to the View.
Here, a Presenter can hold an Interactor of multiple modules so that they can access functional logic and data. And there is no need to analyze the data of other modules in their own modules.
The main difference between this optimized layer and the regular MVP is that it frees up the Presenter layer, which no longer holds specific logic, but calls logic directly.
Analyze each layer:
3.1 the View layer
- It only holds its own one-to-one instance of Presenter, which is called by implementing the interface
- Controls that initialize the page, refresh the display page, and listen for element events
- There should be no status, logic, etc. (unless there is very little logic related to the page, such as a field indicating whether the password is visible)
3.2 the Presenter layer
- Hold and own one – to – one corresponding View instance, can hold multiple modules of the Interactor layer. Call by implementing the interface.
- The Glue layer, which is the View and Interactor layer, receives View operations, calls methods in the module, and returns data to the View.
3.3 Interactor layer
- The atomic logic in this module is encapsulated, not as a family of logic, so that it can be easily called elsewhere.
- References to other modules should not appear in the Interactor layer
- Return of the Interactor layer. Callback is defined in contract.interactor if it is synchronous and if asynchronous.
4 A code example
Here is a brief description of the Sample login code
LoginContract layer:
public interface LoginContract {
interface View extends BaseView {
/** * jump Home */
void goHome(a);
}
interface Presenter extends BasePresenter {
/**
* login
* @param phone
* @param password
*/
void onLogin(String phone, String password);
}
interface Interactor {
/**
* do login
* @param phone
* @param password
* @param callback
*/
void doLogin(String phone, String password, LoginCallback callback);
interface LoginCallback {
void onSuccess(UserInfo user_info);
void onFailure(String msg);
}
/** * Whether to log in *@return* /
boolean isLogin(a);
/** * Get the current login user *@return* /
UserInfo getLoginUser(a); }}Copy the code
LoginActivity:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
ButterKnife.bind(this);
mPresenter = new LoginPresenter(this);
}
@OnClick(R.id.btnLogin)
public void onLogin(a) {
mPresenter.onLogin(editPhone.getText().toString(), editPassword.getText().toString());
}
@OnClick(R.id.txtRegister)
public void goRegister(a) {
ToastUtils.showShort(GlobalApp.getInstance().getContext(), "goRegister");
}
@OnClick(R.id.txtForgetPwd)
public void goForgetPwd(a) {
ToastUtils.showShort(GlobalApp.getInstance().getContext(), "goForgetPwd");
}
@Override
public void showToast(String msg) {
ToastUtils.showShort(GlobalApp.getInstance().getContext(), msg);
}
@Override
public void goHome(a) {
// Go to the Home page
ToastUtils.showShort(GlobalApp.getInstance().getContext(), "Login successful, go to Home page");
Intent intent = new Intent(this, HomeActivity.class);
startActivity(intent);
finish();
}Copy the code
LoginPresnter:
public class LoginPresenter implements LoginContract.Presenter {
private LoginContract.View mView;
private LoginContract.Interactor mInteractor;
public LoginPresenter(LoginContract.View view) {
mView = view;
mInteractor = new LoginInteractor();
}
@Override
public void start(a) {}@Override
public void onLogin(String phone, String password) {
if(StringUtils.isEmpty(phone)) {
mView.showToast("Empty phone");
return;
}
if(StringUtils.isEmpty(password)) {
mView.showToast("Empty password");
return;
}
mInteractor.doLogin(phone, password, new LoginContract.Interactor.LoginCallback() {
@Override
public void onSuccess(UserInfo user_info) {
mView.goHome();
}
@Override
public void onFailure(String msg) { mView.showToast(msg); }}); }}Copy the code
LoginInteractor:
public class LoginInteractor implements LoginContract.Interactor {
private MyOkHttp mApi;
private ACache mCache;
/ / the cache key
private final String CACHE_KEY_USERINFO = "CACHE_KEY_USERINFO";
public LoginInteractor(a) {
mApi = MyOkHttp.get();
mCache = ACache.get(GlobalApp.getInstance().getContext());
}
@Override
public void doLogin(String phone, String password, final LoginCallback callback) {
// Simulate an asynchronous network request to log in
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run(a) {
UserInfo userInfo = new UserInfo();
userInfo.uid = "1212121";
userInfo.userName = "tsy12321";
userInfo.token = "wqw13w12312wsqw12";
// CachemCache.put(CACHE_KEY_USERINFO, userInfo); callback.onSuccess(userInfo); }},2000);
}
@Override
public boolean isLogin(a) {
UserInfo userInfo = (UserInfo) mCache.getAsObject(CACHE_KEY_USERINFO);
if(! StringUtils.isEmpty(userInfo.uid) && ! StringUtils.isEmpty(userInfo.token)) {return true;
}
return false;
}
@Override
public UserInfo getLoginUser(a) {
return(UserInfo) mCache.getAsObject(CACHE_KEY_USERINFO); }}Copy the code
As you can see from the examples above, the specific logic is placed in the Interactor layer. The following shows how other modules call the logic or data of the Login module.
If you click post on the Home page, you need to determine the current login status. It also holds an instance of LoginInteractor in HomePresenter.
public class HomePresenter implements HomeContract.Presenter {
private HomeContract.View mView;
private HomeContract.Interactor mInteractor;
private LoginContract.Interactor mLoginInteractor;
public HomePresenter(HomeContract.View view) {
mView = view;
mInteractor = new HomeInteractor();
mLoginInteractor = new LoginInteractor();
}
@Override
public void start(a) {}@Override
public void onPost(a) {
// Check whether the user is logged in
if(! mLoginInteractor.isLogin()) {// Jump to login
// TODO: 16/8/30
return;
}
// Jump to the post page
// TODO: 16/8/30}}Copy the code
Specific code in Github project: BaseAndroidProject
At the end of may
Whether it’s an MVP or an architecture, the ultimate goal is to write readable and testable code. So don’t over-design your architecture. The architecture will naturally evolve during actual development. Keep in mind! Balanced and reasonable!!
More articles follow my public account