MVP in Android

来源:互联网 发布:cp1e编程软件cx one 编辑:程序博客网 时间:2024/05/17 01:02

1.为什么要使用MVP而不是MVC

1.1 从代码架构看MVP

MVC与MVP模式对比图

MVC模式中:
View:对应布局文件
Model:对应业务逻辑和实体模型
Control:对应Activity
分析:在Android中,假如使用MVC,代表V的layout资源并不能完全解决怎么去渲染的问题,还需要Activity的辅助,所以Activity也必然要代表V;但是同时,Activity一方面拥有生命周期回调,另外一方面还为View设置监听,Activity接收来自用户的输入,所以Activity也必然也代表C,Activity就像一个万能对象同时代表着V与C。因此,使用MVC并不能很好地将V与C分离开来。
MVP模式中:
View :对应Activity,负责View的绘制以及与用户交互
Model:对应业务处理与实体模型
Presenter:连接View与model的中介者,持有View,将数据返回到Model处理
分析: MVP中的View是可以接收用户输入,同时也能解决怎么去渲染的问题,所以Activity可以作为MVP里面的View,因此,MVP更适用于Android。
但是View的改变不只在此:View在一方面增加了接收事件的责任,又在另一方面减少了操作Model的责任。View不再直接操作Model,只能通过Presenter去Model操作数据。可以说,MVC中的View与Control交换了部分工作,就成了现在的MVP。

MVC模式 ——> MVP模式

这里写图片描述

1.2 从程序架构看MVP
任何模式的使用都是为了解开程序的耦合度,使用传统的MVC模式,由于Activity既充当了显示的View,也变成了业务逻辑处理的Control,View层与Model层之间可以直接交互,这样造成了各个模块的之间耦合度加大,不便于后期代码的修改的维护。MVP的解耦使得一个大工程可以拆分成一个小工程,因为每个开发模块之间的关联度被解开了,所以每个人都可以专注自己开发的模块。
另一方面提高了维护性,就是容易区分边界,一旦出了问题,能立刻定位是哪个模块,哪个接口出了问题。

2.MVP模式的缺点

由于对视图的渲染放在了Presenter中,所以视图和Presenter的交互会过于频繁。如果Presenter过多地渲染了视图,往往会使得它与特定的视图的联系过于紧密。一旦视图需要变更,那么Presenter也需要变更了。换句话说,由于Presenter与Activity结合度太高,会造成Presenter的复用性降低。

3.MVP试炼
结合博客上简单的demo,分析一下MVP模式的写法,demo实现用户登录功能,用户输入用户名和密码,模拟耗时线程实现用户的登录跳转。

实体类 User

public class User {    private String username;    private String password;    public String getUsername() {        return username;    }    public void setUsername(String username) {        this.username = username;    }    public String getPassword() {        return password;    }    public void setPassword(String password) {        this.password = password;    }}

实体类包含参数username(用户名)和password(密码)

业务处理借口 IUserBiz

public interface IUserBiz {    public void login(String username,String password,OnLoginListener onLoginListener);}

包含login方法,params为username,password和loginListener,最后一个监听器是进行登录逻辑处理之后根据解决实现相应业务处理的监听借口

业务处理实现类 UserBiz

public class UserBiz implements IUserBiz {    @Override    public void login(final String username, final String password, final OnLoginListener onLoginListener) {        new Thread(new Runnable() {            @Override            public void run() {                try                {                    //线程休眠两秒                    Thread.sleep(2000);                }catch(Exception e)                {                    e.printStackTrace();                }                if("lin".equals(username) && "123".equals(password))                {                    User user = new User();                    user.setUsername(username);                    user.setPassword(password);                    onLoginListener.loginSuccess(user);                }else                    onLoginListener.loginFailed();            }        }).start();    }}

接收到用户名和密码进行登录验证与结果判断动作执行

登录验证结果处理监听 OnLoginListener

public interface OnLoginListener {    public void loginSuccess(User user);    public void loginFailed();}

备注:到这一步,我们已经拥有了Model,包含操作的实体类和业务处理类,Presenter持有View与Model的对象,接下来要做的,就是定义一个View的行为接口,就像登录我们必须在Activity中接受用户输入,传到Presenter中再由它调用相应的model方法进行处理。

View接口

public interface IUserLoginView {    String getUserName();    String getPassword();    void clearUserName();    void clearPassword();    void showLoading();    void hideLoading();    void toMainActivity(User user);    void showFailedError();}

这个定义View的行为,我觉得这里是比较难定的,这里有几个原则
对于View的接口,去观察功能上的操作,然后考虑:
1.该操作需要什么?(getUserName, getPassword)
2.该操作的结果,对应的反馈?(toMainActivity, showFailedError)
3.该操作过程中对应的友好的交互?(showLoading, hideLoading)

Activity的实现

public class MainActivity extends AppCompatActivity implements IUserLoginView {    @Bind(R.id.mEtUsername)    EditText mEtUsername;    @Bind(R.id.mEtPassword)    EditText mEtPassword;    @Bind(R.id.mBtnLogin)    Button mBtnLogin;    @Bind(R.id.mBtnClear)    Button mBtnClear;    @Bind(R.id.mPbLoading)    ProgressBar mPbLoading;    //Init_Presenter    private UserLoginPresenter mUserLoginPresenter = new UserLoginPresenter(this);    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        //ButterKinfe组件绑定        ButterKnife.bind(this);    }    //按钮动作设定    @OnClick(R.id.mBtnLogin)    public void login(View view)    {        mUserLoginPresenter.login();    }    @OnClick(R.id.mBtnClear)    public void clear(View view)    {        mUserLoginPresenter.clear();    }    @Override    public String getUserName() {        return mEtUsername.getText().toString();    }    @Override    public String getPassword() {        return mEtPassword.getText().toString();    }    @Override    public void clearUserName() {        mEtUsername.setText("");    }    @Override    public void clearPassword() {        mEtPassword.setText("");    }    @Override    public void showLoading() {        mPbLoading.setVisibility(View.VISIBLE);    }    @Override    public void hideLoading() {        mPbLoading.setVisibility(View.GONE);    }    @Override    public void toMainActivity(User user) {        Toast.makeText(this, user.getUsername() +                " login success , to MainActivity", Toast.LENGTH_SHORT).show();    }    @Override    public void showFailedError() {        Toast.makeText(this,                "login failed", Toast.LENGTH_SHORT).show();    }}

Presenter在Activity中进行初始化,将实现了IUserView接口的Activity对象传递给Presenter,使用Activity中重写的View行为方法就能将接受的用户输入传入到Presenter中再进行处理,之后使用View行为的其他方法刷新UI线程。

Presenter的实现

public class UserLoginPresenter {    private IUserBiz userBiz;    private IUserLoginView userLoginView;    private Handler mHandler = new Handler();    public UserLoginPresenter(IUserLoginView userLoginView)    {        this.userLoginView = userLoginView;        this.userBiz = new UserBiz();    }    public void login()    {        userLoginView.showLoading();        userBiz.login(userLoginView.getUserName(), userLoginView.getPassword(), new OnLoginListener() {            @Override            public void loginSuccess(final User user) {                //需要在UI线程执行                mHandler.post(new Runnable()                {                    @Override                    public void run()                    {                        userLoginView.toMainActivity(user);                        userLoginView.hideLoading();                    }                });            }            @Override            public void loginFailed() {                //需要在UI线程执行                mHandler.post(new Runnable()                {                    @Override                    public void run()                    {                        userLoginView.showFailedError();                        userLoginView.hideLoading();                    }                });            }        });    }    public void clear()    {        userLoginView.clearUserName();        userLoginView.clearPassword();    }}

参考博客:
1)http://blog.csdn.net/lmj623565791/article/details/46596109
2)http://blog.csdn.net/duo2005duo/article/details/50594757
3)http://blog.csdn.net/duo2005duo/article/details/50778321
4)http://www.zhihu.com/question/35185744

0 0
原创粉丝点击