android MVP——mvp架构的应用和优化

来源:互联网 发布:mysql 死锁 编辑:程序博客网 时间:2024/05/03 17:29

MVP架构在android还是很好用的。我也在试着将mvp用在项目中。

下面我就来说说mvp模式的应用和优化。

mvp模式的概念

MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示。

比较

mvc:

1,在MVC里,View是可以直接访问Model的,View里会包含Model信息,不可避免的还要包括一些业务逻辑。
2,Model不依赖于View,但是View是依赖于Model。
3,有一些业务逻辑在View里实现了,导致要更改View也是比较困难的,至少那些业务逻辑是无法重用的。

mvp:

1,在MVP里,Presenter完全把Model和View进行了分离,主要的程序逻辑在Presenter里实现。
2,Presenter与具体的View是没有直接关联的,而是通过定义好的接口进行交互。从而使得在变更View时候可以保持Presenter的不变,即重用
3,应用程序的逻辑主要在Presenter来实现,其中的View是很薄的一层。这样一来就编写测试用的View,模拟用户的各种操作,从而实现对Presenter的测试

mvp的系统设计

我们先来个mvp系统的设计。(我们在这里模仿一个登陆的请求)
概要设计图
这里写图片描述

1,mobel层

mobel接口:规定操作数据的接口。

接口:IUserLoginMobel

/** * 用户操作接口 Model 业务层接口 */public interface IUserLoginMobel{    /**     * 用户登录     *     * @param name     * @param pwd     * @param loginListener     */    void login(String name, String pwd, OnLoginListener loginListener);}

登录状态回调接口:

/** * 登陆状态接口 */public interface OnLoginListener {    void loginSuccess(UserInfoBean user);    void loginFailed(String message);}

类:用户登陆操作类 UserLoginModel

/** * 用户登陆操作类,Model 业务层(接收数据,处理出局) */public class UserLoginModel implements IUserLoginMobel {    @Override    public void login(final String name, final String pwd, final OnLoginListener loginListener) {        new Thread(new Runnable() {            @Override            public void run() {                try {                    Thread.sleep(2000);                    if ("admin".equals(name) && "admin".equals(pwd)) {                        UserInfoBean userInfoBean = new UserInfoBean();                        userInfoBean.setUserId(System.currentTimeMillis());                        userInfoBean.setUserName(name);                        userInfoBean.setUserPwd(pwd);                        loginListener.loginSuccess(userInfoBean);                    } else {                        loginListener.loginFailed("密码错误");                    }                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }).start();    }}

2,Presenter层

定义 UserLoginPresenter类

/** * 用户登录的任命者。Presenter (用于接收模型发出的结果,给view层发送命令) */public class UserLoginPresenter  {    protected IUserLoginView mvcView;//view的接口    private IUserLoginMobel userLoginMobel;//mobel的接口    private Handler mHandler = new Handler();    public UserLoginPresenter(IUserLoginView userLoginView) {        this.mvcView = userLoginView;        this.userLoginMobel = new UserLoginModel();//实例化用户登录业务层    }    public void login() {        mvcView.showLoading();        userLoginMobel.login(mvcView.getUserName(), mvcView.getPassword(), new OnLoginListener() {            @Override            public void loginSuccess(final UserInfoBean user) {                //需要在UI线程执行                mHandler.post(new Runnable() {                    @Override                    public void run() {                        mvcView.toMainActivity(user);                        mvcView.hideLoading();                    }                });            }            @Override            public void loginFailed(final String message) {                //需要在UI线程执行                mHandler.post(new Runnable() {                    @Override                    public void run() {                        mvcView.showFailedError(message);                        mvcView.hideLoading();                    }                });            }        });    }    public void clear() {        mvcView.clearPassword();        mvcView.clearUserName();    }}

3,view 层
首相定义一个view接口 IUserLoginView
接口规定view层去实现的方法

/** * 完整的登陆接口。 View 接口 */public interface IUserLoginView {    //获得用户信息    String getUserName();    String getPassword();    //清除用户信息    void clearUserName();    void clearPassword();    //遮罩层    void showLoading();    void hideLoading();    //登陆成功    void toMainActivity(UserInfoBean user);    //登陆失败    void showFailedError(String message);}

我们定义一个MVCActivity来继承 IUserLoginView

/** * 这时候的activity相当于view (只负责显示数据) * Presenter与View交互是通过接口 */public class MVCActivity extends AppCompatActivity implements IUserLoginView {    private EditText user_name_edit, user_pwd_edit;    private Button user_login_btn, user_clear_btn;    private ProgressBar user_login_bar;    private UserLoginPresenter presenter;    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        presenter=new UserLoginPresenter(this);        initView();    }    private void initView() {        user_name_edit = (EditText) findViewById(R.id.user_name_edit);        user_pwd_edit = (EditText) findViewById(R.id.user_pwd_edit);        user_login_btn = (Button) findViewById(R.id.user_login_btn);        user_clear_btn = (Button) findViewById(R.id.user_clear_btn);        user_login_bar = (ProgressBar) findViewById(R.id.user_login_bar);        user_login_btn.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                presenter.login();            }        });        user_clear_btn.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                presenter.clear();            }        });    }    @Override    public String getUserName() {        return user_name_edit.getText().toString();    }    @Override    public String getPassword() {        return user_pwd_edit.getText().toString();    }    @Override    public void clearUserName() {        user_name_edit.setText("");    }    @Override    public void clearPassword() {        user_pwd_edit.setText("");    }    @Override    public void showLoading() {        user_login_bar.setVisibility(View.VISIBLE);    }    @Override    public void hideLoading() {        user_login_bar.setVisibility(View.INVISIBLE);    }    @Override    public void toMainActivity(UserInfoBean user) {        Toast.makeText(this, user.getUserName(), Toast.LENGTH_SHORT).show();    }    @Override    public void showFailedError(String message) {        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();    }}

还有layout.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical">    <LinearLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:orientation="horizontal">        <TextView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="用户名" />        <EditText            android:id="@+id/user_name_edit"            android:layout_width="match_parent"            android:layout_height="wrap_content" />    </LinearLayout>    <LinearLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:orientation="horizontal">        <TextView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="密码" />        <EditText            android:id="@+id/user_pwd_edit"            android:layout_width="match_parent"            android:layout_height="wrap_content" />    </LinearLayout>    <LinearLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:gravity="center_horizontal"        android:orientation="horizontal">        <Button            android:id="@+id/user_login_btn"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="登陆" />        <Button            android:id="@+id/user_clear_btn"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="清除" />    </LinearLayout>    <ProgressBar        android:id="@+id/user_login_bar"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_gravity="center_horizontal"        android:visibility="invisible" /></LinearLayout>

我们先来看看效果图。
这里写图片描述

可以看出,和我们原来用mvc设计的登陆达到了一样的目的。但是可以看出我们的activity再也没有像原来一样多了很多逻辑处理。
而我们的逻辑处理都放在了Presenter层。而activity成了一个真正的view。

这就是mvp架构最基本的应用。可以看出使用mvp去设计app可以很好的将activity当作一个view层分离出来。

那么从上面的结构图可以看出我们还是有很多可以优化的地方的。

优化

mobel层 接口都继承IBaseMobel接口
我们可以在IBaseMobel这个元接口中定义一些整个mobel都会做的工作,例如初始化数据

/** * presenter层基类接口 */public interface IBaseMobel {    void initData();//定义一个所有presenter初始化数据的方法}

同理:view层 接口都继承 IBaseView 接口

我们可以在IBaseView 这个元接口中定义一些整个mobel都会做的工作,例如初始化数据。

/** * view层基类接口 */public interface IBaseView {    void initView();//view初始化view的一个基本接口}

presenter层继承一个基类来收集重复方法或者属性
定义一个 BasePresenter类

/** * 在基类presenter中将添加和销毁方法提供 */public class BasePresenter<T> {    protected T mvcView;    /**     * 每个继承基类的presenter都要去实现构造方法,并传入view层     */    protected BasePresenter(T mvcView) {        this.mvcView = mvcView;    }    /**     * 因为presenter层持有view层,所以,提供一个方法,在view层不使用的时候将对象释放     */    public void onDestroy() {        mvcView = null;    }}

我们来看看如何让原来的UserLoginPresenter使用
修改后的UserLoginPresenter类

/** * 用户登录的任命者。Presenter (用于接收模型发出的结果,给view层发送命令) */public class UserLoginPresenter extends BasePresenter<IUserLoginView> {    private IUserLoginMobel userLoginMobel;//mobel的接口    private Handler mHandler = new Handler();    public UserLoginPresenter(IUserLoginView userLoginView) {        super(userLoginView);        //this.userLoginView = userLoginView;//未优化前的方法        this.userLoginMobel = new UserLoginModel();//实例化用户登录业务层    }    public void login() {        mvcView.showLoading();        userLoginMobel.login(mvcView.getUserName(), mvcView.getPassword(), new OnLoginListener() {            @Override            public void loginSuccess(final UserInfoBean user) {                //需要在UI线程执行                mHandler.post(new Runnable() {                    @Override                    public void run() {                        mvcView.toMainActivity(user);                        mvcView.hideLoading();                    }                });            }            @Override            public void loginFailed(final String message) {                //需要在UI线程执行                mHandler.post(new Runnable() {                    @Override                    public void run() {                        mvcView.showFailedError(message);                        mvcView.hideLoading();                    }                });            }        });    }    public void clear() {        mvcView.clearPassword();        mvcView.clearUserName();    }}

这样我们就把Presenter的初始化工作和关于activity在onDestory的时候手动置空Presenter中view对象(这时候的view对象其实就是activity)的方法给提取到基类中。

因为这两个方法很多地方会用到,所以我们不必每次都去写它们。

我们再将view层的activity封装。view层例如:fragment也是可以封装的。

我们建立一个抽象类BaseActivity。

/** * mvc模式的view层基类<继承 presenter 具体> */public abstract class BaseActivity<T extends BasePresenter> extends AppCompatActivity {    //必须实例化presenter对象    public abstract T initPresenter();    public T presenter;    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(setMvcView());        presenter = initPresenter();        initView();    }    protected abstract int setMvcView();    protected abstract void initView();    @Override    protected void onDestroy() {        presenter.onDestroy();        super.onDestroy();    }}

接下来修改MVCActivity类

/** * 这时候的activity相当于view (只负责显示数据) * Presenter与View交互是通过接口 */public class MVCActivity extends BaseActivity<UserLoginPresenter> implements IUserLoginView {    private EditText user_name_edit, user_pwd_edit;    private Button user_login_btn, user_clear_btn;    private ProgressBar user_login_bar;    @Override    public UserLoginPresenter initPresenter() {        return new UserLoginPresenter(this);    }    @Override    protected int setMvcView() {        return R.layout.activity_main;    }    @Override    protected void initView() {        user_name_edit = (EditText) findViewById(R.id.user_name_edit);        user_pwd_edit = (EditText) findViewById(R.id.user_pwd_edit);        user_login_btn = (Button) findViewById(R.id.user_login_btn);        user_clear_btn = (Button) findViewById(R.id.user_clear_btn);        user_login_bar = (ProgressBar) findViewById(R.id.user_login_bar);        user_login_btn.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                presenter.login();            }        });        user_clear_btn.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                presenter.clear();            }        });    }    @Override    public String getUserName() {        return user_name_edit.getText().toString();    }    @Override    public String getPassword() {        return user_pwd_edit.getText().toString();    }    @Override    public void clearUserName() {        user_name_edit.setText("");    }    @Override    public void clearPassword() {        user_pwd_edit.setText("");    }    @Override    public void showLoading() {        user_login_bar.setVisibility(View.VISIBLE);    }    @Override    public void hideLoading() {        user_login_bar.setVisibility(View.INVISIBLE);    }    @Override    public void toMainActivity(UserInfoBean user) {        Toast.makeText(this, user.getUserName(), Toast.LENGTH_SHORT).show();    }    @Override    public void showFailedError(String message) {        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();    }}

我们把activity的onCreate和onDestory交给基类来处理,我们也可以在基类中处理onResume等方法。所有的生命周期由基类来管理。

另外,我们将Presenter对象也交给基类来管理,让基类来处理Presenter对象中的公有方法。

mvp架构的应用和优化已经写完了,我们可以根据自己项目的实际情况作进一步的优化。

mvp其实就是一种设计思想。它不光用于对model、presenter和view的处理。这只是一种思想,可以用到很多地方。

更多的mvp模式可以去参照:

Google在Github开源的一个项目:Android Architecture Blueprints

简书:Android官方MVP架构项目解析

献上这篇博客的demo:

demo下载

3 0
原创粉丝点击