Android MVP 架构模式详解

来源:互联网 发布:南航网络教学平台 编辑:程序博客网 时间:2024/06/05 14:42

MVP Sample

MVP架构模式入门案例,在众多案例中,应该算是比较规范和容易理解的案例了。示例代码请到我的github下载: https://github.com/li-xiaojun/MVPSample

MVP架构模式详解

11. MVP架构模式

  • 概念解释
    • MVP是Model(数据) View(界面) Presenter(表现层)的缩写,它是MVC架构的变种,强调Model和View的最大化解耦和单一职责原则
    • Model:负责数据的来源和封装,比如网络请求类,数据库操作类以及java bean,如果有必要则提供接口暴露自己处理数据的状态和进度。
    • View:负责UI相关,如布局UI的初始化,各种listener的设置。在Android中,我们通常写的Activity和Fragment就是属于View层;在web开发中,html则是View层。
    • Controller:业务逻辑控制器,主要负责当获取到数据后对数据进行逻辑处理,然后将数据绑定到View上;比如:请求一个url,从网络获取到数据,进行解析javabean,然后各种set数据。对于控制器的概念大家很好理解,因为我们每天都在这样做,在Activity中请求数据然后更新UI。但是结合View的概念来看,很显然Activity和Fragment不但承担了View的任务,还负责完成的Controller的功能,随着业务功能的增多,Activity的代码越来越难以阅读和维护,这就是在Android中使用MVC的弊端,为了解决MVC模式下View层的臃肿,MVP模式应运而生。
    • Presenter:专门从C独立出来的业务逻辑层,主要负责处理原先View层的业务逻辑,解决了Activity的臃肿问题,让Activity只负责处理UI,职责更加明确;并且将View层的业务逻辑抽取到P层之后,View层与Model层也实现了解耦;便于后期代码的扩展和维护,并且业务逻辑层独立后代码还得到很大的重用性
    • 总结:MVC模式下,V和C纠缠不清,并且View和Model相互关联,而MVP模式下Model和VIew解耦,便于单元测试,项目维护,代码重用

  • 先看看一个登录界面的业务逻辑和UI更新全部写在Activity的代码:

    public class LoginActivity extends ActionBarActivity implements OnClickListener{    EditText etUsername,etPassword;    Button btn_login;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        etUsername = (EditText) findViewById(R.id.et_username);        etPassword = (EditText) findViewById(R.id.et_password);        btn_login = (Button) findViewById(R.id.btn_login);        //设置点击事件        btn_login.setOnClickListener(this);    }    @Override    public void onClick(View v) {        switch (v.getId()) {        case R.id.btn_login:            String username = etUsername.getText().toString();            String password = etPassword.getText().toString();            if(checkInput(username,password)){                //提交登录                //执行登录请求                execLogin(username, password);            }            break;        }    }    //执行登录请求    public void execLogin(String username,String password){        //执行登录请求的伪代码        HttpHelper helper = new HttpHelper();        helper.execRequest("http://www.baidu.com", new HttpCallback() {            @Override            public void onSuccess() {                //提示登录成功                showLoginSuccess(LoginActivity.this);                //保存登录相关数据,如登录的标记,用户的唯一标识                saveLoginData();            }            @Override            public void onFail() {                //关闭登录对话框                hideLoginDialog(LoginActivity.this);                //提示登录失败                showLoginFail(LoginActivity.this);            }        });    }    //保存登录数据    public void saveLoginData(){        //保存登录相关的数据,代码略过...    }    //提示登录成功    public void showLoginFail(Context context){        Toast.makeText(context, "登录失败", 0).show();    }    //提示登录失败    public void showLoginSuccess(Context context){        Toast.makeText(context, "登录成功", 0).show();    }    //检查输入的合法性    private boolean checkInput(String username, String password) {        boolean result = true;        //1.检查为空        if(TextUtils.isEmpty(username) || TextUtils.isEmpty(password)){            Toast.makeText(this, "用户名或者密码不能为空!", 0).show();            result = false;        }        //2.检查长度        if(username.length()!=11){            Toast.makeText(this, "用户名长度不正确!", 0).show();            result = false;        }        if(password.length()<5){            Toast.makeText(this, "密码长度不能小于5位!", 0).show();            result = false;        }        return result;    }}

  • 现在抽取出LoginPresenter类,如下:

      /** * 业务逻辑封装层 * @author lxj * */public class LoginPresenter {    private ILoginView loginView;    /**     * 生命周期相关方法     */    public void onDestory(){        //do something to release and avoid memory leak;    }    public void onStart(){        //do something when onStart    }    public void onStop(){        //do something when onStop    }    public void onResume(){        //do something when onResume    }    public void onPause(){        //do something when onPause    }    public LoginPresenter(ILoginView loginView){        this.loginView = loginView;    }    //登录的方法    public void login(String username, String password) {        if(!checkInput(username, password)){            return;        }        // 执行登录请求的伪代码        HttpHelper helper = new HttpHelper();        helper.execRequest("http://www.baidu.com", new HttpCallback() {            @Override            public void onSuccess() {                // 需要UI展示,暴露接口                loginView.showLoginSuccess();                // 保存登录相关数据,如登录的标记,用户的唯一标识                saveLoginData();            }            @Override            public void onFail() {                // 需要UI展示,暴露接口                loginView.showLoginFail();            }        });    }    private void saveLoginData() {        //do something    }    // 检查输入的合法性    private boolean checkInput(String username, String password) {            boolean result = true;            // 1.检查为空            if (TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) {                loginView.showInputNoNull();                result = false;            }            // 2.检查长度            if (username.length() != 11) {                loginView.showUsernameLengthError();                result = false;            }            return result;        }}
  • View和P解耦接口如下ILoginView:

    public interface ILoginView {    void showLoginSuccess();    void showLoginFail();    void showInputNoNull();    void showUsernameLengthError();}
  • 最后的View层编写如下:

    public class LoginActivity extends Activity implements OnClickListener,ILoginView{    EditText etUsername,etPassword;    Button btn_login;    private LoginPresenter loginPresenter;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        etUsername = (EditText) findViewById(R.id.et_username);        etPassword = (EditText) findViewById(R.id.et_password);        btn_login = (Button) findViewById(R.id.btn_login);        //设置点击事件        btn_login.setOnClickListener(this);        //与loginPresenter交互        loginPresenter = new LoginPresenter(this);    }    @Override    public void onClick(View v) {        switch (v.getId()) {        case R.id.btn_login:            String username = etUsername.getText().toString();            String password = etPassword.getText().toString();            loginPresenter.login(username,password);            break;        }    }    public void showLoginSuccess(){        Toast.makeText(this, "登录成功", 0).show();    }    public void showLoginFail(){        Toast.makeText(this, "登录失败", 0).show();    }    public void showInputNoNull(){        Toast.makeText(this, "用户名和密码不能为空!", 0).show();    }    public void showUsernameLengthError(){        Toast.makeText(this, "用户名长度不正确!", 0).show();    }    public void hideLoginDialog(){        if(progressDialog!=null){            progressDialog.dismiss();        }    }    @Override    protected void onResume() {        super.onResume();        loginPresenter.onResume();    }    @Override    protected void onStart() {        super.onStart();        loginPresenter.onStart();    }    @Override    protected void onStop() {        super.onStop();        loginPresenter.onStop();    }    @Override    protected void onPause() {        super.onPause();        loginPresenter.onPause();    }    @Override    protected void onDestroy() {        super.onDestroy();        loginPresenter.onDestory();    }}

  • 总结:MVP只是给我们提出了分层解耦的思想,并没有一个固定的实现。Google虽然出了官方的MVP实现示例,但是并没有太多人去跟随,很多公司在对Presenter层都有自己的理解,此处的案例相对来说比较规范,对MVP三层都有清晰的解耦和实现,在茫茫开源项目中,算是比较好的容易理解的上手项目了。

    0 0
    原创粉丝点击