安卓设计模式MVP

为啥要用MVP

网上有很多关于怎样写好MVP模式的文章,但是我还是想写一篇关于此类的文章,多一个不多少一个不少,仅供参考提供不同的见解嘛</可爱>。

很早就像写一篇MVC的进阶篇MVP了,MVC这里就不了说太多了,因为即使你不同设计模式,写代码的风格和MVC也差不了太远啦,MVC(model view control)M为数据,View为视图,C的控制即接受操作数据展示在View上,在Android中View即为XML布局,M的各种实体数据类,C为Activity,在实际开发中分的并没有那么清晰,因为往往实际需求都比较复杂(一个Activity往往有很多个网络请求)导致M和C都挤在Activity里面,使得Activity轻松就能上1000+行代码,这就是为啥要使用MVP的缘故了,这样一来给项目的后期维护修改带来了很大的困难,特别是还不写注释,看起啦简直不要太爽。

什么是MVP

MVP即Model  View Presenter,从字面上就能看到C变成了P即通俗理解为代表层,P持有M和V的引用。数据和视图的交互都通过P,即M和V不直接打交道,必须通过P来间接通信。我说的这么通俗,你们应该能够看懂哈,(看不懂顺着网线来打我呀^_^)

怎样写MVP

对于怎样写MVP模式,可谓仁者见仁智者见智,这里已登录操作作为背景,来看看MVP咋写,先来看初级版的MVP吧,我们先创建UserBean实体类

public class UserBean {

    private String Id;
    private String Name;

    public String getId() {
        return Id;
    }

    public void setId(String id) {
        Id = id;
    }

    public String getName() {
        return Name;
    }

    public void setName(String name) {
        Name = name;
    }
}

 

就是普通的实体类,再写一个UserBean的接口IUser来定义对UserBean实体类的数据操作接口,保存和载入数据

public interface IUser {

    void SaveUserInfo(UserBean user);
    UserBean loadUser();
}

然后在创建User类实现IUser接口,实现对UserBean的数据操作

public class User implements IUser {

    private UserBean userBean;

    @Override
    public void SaveUserInfo(UserBean user) {
        this.userBean=user;
    }

    @Override
    public UserBean loadUser() {
        if(userBean!=null){
            return userBean;
        }
        return null;
    }
}

好了Model到这就写完了,接下来就开始写View了,除了XML外要添加一个IUserView 的接口,XML就不贴了两个按钮和两个编辑框,下面是IUserView 的代码

public interface IUserView {

    UserBean getUser();

    void setUser(UserBean user);
}

用于定义View对数据的操作,这个接口是Activity 需要实现的,这样Activity就是IUserView 的实现类了

下面是重要的presenter的代码,它持有对View和Model的引用、定义了保存和载入的 两个方法

public class UserPresenter {

    private IUserView userView;
    private IUser user;

    public UserPresenter(IUserView userView) {
        this.userView = userView;
        user=new User();
    }

    public void saveUser(){
        user.SaveUserInfo(userView.getUser());
    }

    public void loadUser(){
        userView.setUser(user.loadUser());
    }
}

下面来看Activity的代码

public class MainActivity extends AppCompatActivity implements IUserView{

    private TextView id_tv,name_tv;
    private EditText id_ev,name_ev;
    private Button save,load;

    private UserPresenter userPresenter;



    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        userPresenter=new UserPresenter(this);
        id_tv=findViewById(R.id.id_tv);
        name_tv=findViewById(R.id.name_tv);
        id_ev=findViewById(R.id.id_ev);
        name_ev=findViewById(R.id.name_ev);
        save=findViewById(R.id.save_btn);
        load=findViewById(R.id.load_btn);
        save.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                userPresenter.saveUser();
            }
        });

        load.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               userPresenter.loadUser();
            }
        });
    }

    //从xml界面获取用户名和密码
    @Override
    public UserBean getUser() {
        return null;
    }

    //设置用户名和密码到xml界面
    @Override
    public void setUser(UserBean user) {

    }
}

可以看到两按钮的点击事件里面的方式是Presenter已经定义好了的,像这样把所有的数据逻辑操作都定义在UserPresenter中这样看起来就不会很乱特别是有需求很复杂的时候,思路就比较清晰了。到这里一个初级版的MVP结构就已经写完了,但是看着代码量比起MVC多了很多呀,难不成每一个需求写一个Presenter么?是不是费力不讨好呢,就现在的需求来看这样写确实费力不讨好。所以说有时候不能为了设计而设计。

这里还有一个问题会存在内存泄露,如果在Presenter中执行了耗时操作,在还没等耗时操作完Activity就退出来得话就会有内存泄露,这个问题暂且放在这儿。

我想这么简单的MVP你们应该看懂了,下面就来个进阶版的MVP

进阶版MVP

我们可以写一个基类来定义一些基本的操作,让其类实现/继承这基类就可以简化大量操作代码,好的,先来一个BaseView

public interface BaseView<T> {

    /**
     * 显示正在加载
     */
    void showLoading();

    /**
     * 关闭加载
     */
    void hideLoading();

    /**
     * 提示信息
     * @param msg
     */
    void showToast(String msg);

    /**
     * 显示错误提示
     */
    void showError();

    /**
     * 显示网络请求数据
     * @param data
     */
    void showData(T data);

    /**
     * 获取上下文
     * @return
     */
    Context getContent();
}

这个类定义个一些对用户友好的提示,如果你还要其他的,可以自行添加,接下来来看网络请求回调响应的基类CallBack

public interface CallBack<T> {

    /**
     * 数据请求成功回调
     * @param data
     */
    void onSuccess(T data);

    /**
     * 网络请求失败提示信息
     * @param msg
     */
    void onFail(String msg);

    /**
     * 网络请求发送错误提示信息
     * @param msg
     */
    void onError(String  msg);

    /**
     * 请求结束,无论请求结果是否成功
     */
    void onComplete();

}

好了,现在还差BasePresenter基类,它持有BaseVIew的子类,定义个绑定Activity和解绑Activity的操作,解决上面内存泄露的问题,当要退出当前Activity的时候在onDestory回调detachView()方法解绑就可以了

public class BasePresenter<V extends BaseView> {

    //需要绑定的view
    private V view;

    //绑定view时初始化 相关参数
    public void attachView(V view){
        this.view=view;
    }

    //解绑View
    public void detachView(){
        this.view=null;
    }

    //获取view的绑定状态
    public boolean isViewAttach(){
        return view!=null;
    }

    //获取绑定的View
    public V getView(){
        return view;
    }


}

数据实体类的和上面初级版的一样。来看看Model,来模拟网络数据请求,定义了三种情况,请求成功,请求失败,请求发送错误和请求完成。

public class Model {

    public static void getNetData(final String params, final CallBack<String> callBack){
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                switch (params){
                    case "success":
                        callBack.onSuccess("请求成功");
                        break;
                    case "failure":
                        callBack.onFail("请求失败");
                        break;
                    case "error":
                        callBack.onError("请求发送错误");
                        break;
                }
                callBack.onComplete();
            }

        },3000);
    }
}

下面来创建Presenter MvpPresenter

public class MvpPresenter extends BasePresenter<MVPView> {

    public void getData(String params){
        if(!isViewAttach()){
            //如果没有持有view直接返回
            return;
        }

        //显示加载进度
        getView().showLoading();

        Model.getNetData(params, new CallBack<String>() {
            @Override
            public void onSuccess(String data) {
                if(isViewAttach()){
                    getView().showData(data);

                }
            }

            @Override
            public void onFail(String msg) {
                if(isViewAttach()){
                    getView().showToast(msg);
                }
            }

            @Override
            public void onError(String msg) {
                if(isViewAttach()){
                    getView().showError();
                }
            }

            @Override
            public void onComplete() {
                if(isViewAttach()){
                    getView().hideLoading();
                }
            }
        });
    }
}

对于Activity也创建一个BaseActivity,写上一些公共的方法,它实现了BaseView

public abstract class BaseActivity extends AppCompatActivity implements BaseView{

    private ProgressDialog progressDialog;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        progressDialog=new ProgressDialog(this);
        progressDialog.setCancelable(false);
    }

    @Override
    public void showLoading() {
        if(!progressDialog.isShowing()){
            progressDialog.show();
        }
    }

    @Override
    public void hideLoading() {
        if(progressDialog.isShowing()){
            progressDialog.dismiss();
        }
    }

    @Override
    public void showToast(String msg) {
        Toast.makeText(this,msg,Toast.LENGTH_SHORT).show();
    }

    @Override
    public void showError() {
        Toast.makeText(this,"发生错误",Toast.LENGTH_SHORT).show();
    }

    @Override
    public Context getContent() {
        return BaseActivity.this;
    }
}

好了基本上框架基本搭完了,等等,还少显示视图数据的接口MVPView

public interface MVPView extends BaseView{

    /**
     * 当请求数据成功后回调此接口
     * @param data
     */
    @Override
    void showData(Object data);
}

这样就完了

来看看对于一个登录的需求怎样运用MVP框架

public class MainActivity extends BaseActivity implements MVPView{

    private TextView id_tv,name_tv;
    private EditText id_ev,name_ev;
    private Button save,load;

    private MvpPresenter mvpPresenter;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mvpPresenter=new MvpPresenter();
        mvpPresenter.attachView(this);
        initView();
    }

    private void initView() {
        id_tv=findViewById(R.id.id_tv);
        name_tv=findViewById(R.id.name_tv);
        id_ev=findViewById(R.id.id_ev);
        name_ev=findViewById(R.id.name_ev);
        save=findViewById(R.id.save_btn);
        load=findViewById(R.id.load_btn);
        save.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mvpPresenter.getData("保存");
            }
        });

        load.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mvpPresenter.getData("载入");
            }
        });
    }

    //回到现实数据
    @Override
    public void showData(Object data) {
        
    }

    @Override
    protected void onDestroy() {
        mvpPresenter.detachView();
        super.onDestroy();
    }
}

同样还是登录的Activity是不是看上去代码更优雅了呢?(劳资就是要说优雅,不服来打我呀)。到这里你会发现有个问题,对于不同的需求还是要重新写不同的具体Model和不同的具体Presenter,嗯这是个问题,不过可以解决的,留在下次写吧,要相信寄几个儿嘛

 

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值