项目地址
java:https://github.com/zhangtiansimple/androidmvp_java
kotlin:https://github.com/zhangtiansimple/androidmvp_kotlin
本文将通过一个例子的不断优化,让同学们可以体会到MVP的优势。
现在有这样一个需求,在登录页上有一个登录按钮,点击就触发登录逻辑。
最普通的写法会怎么写呢?在当前Activity下
loginBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
System.out.print("user login");
}
});
这样看起来很简单,但是当项目规模较大需要多人协作开发的话,如此写法使得各个模块之间的耦合很深,协同开发难度很大,使用MVP可以把不同的模块解藕出去,Module模块负责数据等功能,View层的UI部分只关心UI实现,最后通过Presenter将其关联起来,架构清晰,协作开发效率高。
现在开始使用MVP来进行优化
第一步,添加UserModule、UserView、UserPresenter
UserModule类里添加login方法
public class UserModule {
public void login(String userName, String password) {
System.out.print("user login");
}
}
UserView接口
public interface UserView {
void onLoginResult(String result);
}
UserPresenter类 持有module的引用 持有View的引用
public class UserPresenter {
private UserModule userModule;
private UserView userView;
public UserPresenter(UserView userView) {
this.userModule = new UserModule();
this.userView = userView;
}
public void login(String userName, String password) {
userModule.login(userName, password);
if (userView != null) {
userView.onLoginResult("");
}
}
}
那么此时,只需要在当前Activity下
UserPresenter userPresenter = new UserPresenter(new UserView() {
@Override
public void onLoginResult(String result) {
}
});
userPresenter.login("", "");
这样一个简单的MVP就写好了,代码虽然变多了,但是结构却更加清晰了,不过这样一个MVP还存在很多问题,接下来大家一起来找茬。
问题一:当我们的网络请求正在进行,这个时候我们退出了Activitiy,然后UI层引用还在,还会回调,其实没有必要了,我们可以直接终止请求。
解决方案:绑定和解绑
在Presenter类里添加绑定和解绑方法
public class UserPresenter {
private UserModule userModule;
private UserView userView;
public UserPresenter() {
this.userModule = new UserModule();
}
public void attachView(UserView userView) {
this.userView = userView;
}
public void detachView() {
this.userView = null;
}
public void login(String userName, String password) {
userModule.login(userName, password);
if (userView != null) {
userView.onLoginResult("");
}
}
}
然后修改Activity里的代码,并且在onDestory解绑
userPresenter = new UserPresenter();
userPresenter.attachView(new UserView() {
@Override
public void onLoginResult(String result) {
}
});
userPresenter.login("", "");
@Override
protected void onDestroy() {
super.onDestroy();
if (userPresenter != null) {
userPresenter.detachView();
}
}
这样修改之后,问题一解决了,但是细心的同学肯定会发现这样又会引出一个问题
问题二:一个类还好,如果多个类,那么会反复定义绑定和解绑
解决方案:将绑定和解绑抽象
我们添加一个抽象类来解决这个问题
public abstract class BasePresenter<T extends BaseView> {
private T view;
public void attachView(T view) {
this.view = view;
}
public void detachView() {
this.view = null;
}
public T getView() {
return view;
}
}
添加这个抽象作为Presenter的父类后,虽然解决了反复定义写重复代码的问题,但是又会引出一个问题
问题三:随着Activity、Fragment数量增加,需要反复的绑定和解除绑定,这个操作属于代码冗余
解决方法:Activity的抽象 + 泛型设计
添加BaseActivity
public abstract class BaseActivity<V extends BaseView, P extends BasePresenter<V>> extends AppCompatActivity {
private P presenter;
private V view;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (presenter == null) {
presenter = createPresenter();
}
if (view == null) {
view = createView();
}
if (presenter != null && view != null) {
presenter.attachView(view);
}
}
public abstract P createPresenter();
public abstract V createView();
public P getPresenter() {
return presenter;
}
@Override
protected void onDestroy() {
super.onDestroy();
if (presenter != null && view != null) {
presenter.detachView();
}
}
}
Fragment和ViewGroup的原理类似,这里就不添加了,下面是几个类的完整代码
Module
public class UserModule {
public void login(String userName, String password) {
System.out.print("user login");
}
}
View
public interface BaseView {
}
public interface UserView extends BaseView{
void onLoginResult(String result);
}
Presenter
public abstract class BasePresenter<T extends BaseView> {
private T view;
public void attachView(T view) {
this.view = view;
}
public void detachView() {
this.view = null;
}
public T getView() {
return view;
}
}
public class UserPresenter extends BasePresenter<UserView> {
private UserModule userModule;
public UserPresenter() {
this.userModule = new UserModule();
}
public void login(String userName, String password) {
userModule.login(userName, password);
if (getView() != null) {
getView().onLoginResult("");
}
}
}
Activity(BaseActivity在问题三那里)
public class MainActivity extends BaseActivity<UserView, UserPresenter> implements UserView {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getPresenter().login("", "");
}
@Override
public UserPresenter createPresenter() {
return new UserPresenter();
}
@Override
public UserView createView() {
return this;
}
@Override
public void onLoginResult(String result) {
}
}