Android MVP设计模式介绍(附简单Demo下载)

推荐阅读:

Android MVC设计模式详解

Android 框架MVVM详解

Android 认识EventBus轻量级事件总线框架

Android 依赖注入库——Dagger2使用详解



    MVP模式属于UI框架模式的一种,随着UI技术的功能日益丰富,View层也履行着越来越多的职责。为了更好地细分视图(View)与模型(Model)的功能,让View专注于处理数据的可视化以及与用户的交互,同时让Model只关系数据的处理,基于MVC概念的MVP(Model-View-Presenter)模式应运而生。为了更好的理解MVP,那就必须先了解MVC。了解了MVC后动手实践一个MVP的案例,然后进行总结。


MVP与MVC对比


(1)相通之处

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

(2)MVC的缺陷

  • 在MVC模型里,更关注模型层(Model)的不变。因为一个模型层(Model)可以被多个视图层(View)重用,Model不依赖于View,但是View是依赖于Model的。
  • 在MVC模型里,视图层无法实现独立重用。视图层需要持有控制层Activity的引用才能进行UI引用。不仅如此,视图层(View)是可以直接访问模型层(Model)的数据,从而视图层不可避免会涉及一些业务逻辑。由于在View里实现的业务逻辑是无法被重用的,导致要更改和重用View变得比较困难。
  • 由于模型操作接口的不同,视图可能需要多次调用模型才能获得足够的显示数据,不必要的频繁访问未变化数据的,也损害一定性能。

(3)两者区别

    在MVP中视图层(View)不能直接访问模型层(Model),它们之间通过Presenter(Controller)来进行通信的。这一点很好的弥补了MVC的设计缺陷,使View与Model实现完全分离。View无法直接从Model中读取数据,所有的交互都发生在 Presenter层(Controller)。如下图所示:

Android MVP设计模式介绍(附简单Demo下载)_第1张图片

MVP代码案例


CSDN下载本案例:http://download.csdn.net/download/csdn_aiyang/10194951


(1)实现的功能,如下图所示。

Android MVP设计模式介绍(附简单Demo下载)_第2张图片

(2)Model 层 ,代码如下

LoginModel.class 接口

public interface LoginModel {

    interface OnLoginListener {
        void onLoginSuccess();

        void onLoginFail();
    }

    void loginSubmit(String username, String password, OnLoginListener listener);
}

UserInfo.class 

public class UserInfo implements LoginModel{
    private String age;
    private String name;
    private String gender;
    private String hobby;

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getHobby() {
        return hobby;
    }

    public void setHobby(String hobby) {
        this.hobby = hobby;
    }


    @Override
    public void loginSubmit(final String username, final String password, final OnLoginListener listener) {

        //此次Handler制造延迟3秒和回调——纯属模仿网络请求。实际开发可以替换
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                if (username.equals("1") && password.equals("1")) {
                    listener.onLoginSuccess();
                } else {
                    listener.onLoginFail();
                }
            }
        }, 2000);
    }
}

*弹框 Dialog.class

/**
 * 弹框工具类
 */
public class DialogUtil {

    /**
     *  显示基本Dialog
     */
    public static void showSimpleDialog(Context context, String title, String message , DialogInterface.OnClickListener clickListener) {
        AlertDialog.Builder builder =new AlertDialog.Builder(context);
//        builder.setIcon(R.mipmap.ic_launcher);
        builder.setTitle(title);
        builder.setMessage(message);
        //监听事件
        if (clickListener != null){
            builder.setPositiveButton("确认",clickListener);
        }else{
            builder.setPositiveButton("确认",null);
        }
        builder.setNegativeButton("取消",null);
        //设置对话框是可取消的
        builder.setCancelable(true);
        AlertDialog dialog=builder.create();
        dialog.show();
    }

    protected static ProgressDialog progressDialog;

    /**
     * 显示进度条
     * @param context
     * @param msg
     */
    public static void showProgress(Context context,String msg){
        if (progressDialog == null){
            progressDialog = new ProgressDialog(context);
        } else if (progressDialog.isShowing()){
            progressDialog.dismiss();
        }
        progressDialog.setMessage(msg);
        progressDialog.show();
    }

    /**
     * 关闭进度条
     */
    public static void dismissProgress(){
        if (progressDialog != null ){
            if (progressDialog.isShowing()){
                progressDialog.dismiss();
            }
        }
    }
}

(3)Presenter 层,代码如下

接口

public interface ILoginPresenter {

    void loginSubmit(String username, String password);

}

实现

/**
 * Created by aiyang on 2018/1/8.
 * 中介者——处理视图和模型
 */

public class LoginPresentImpl implements ILoginPresenter,LoginModel.OnLoginListener {

    private LoginView mView;
    private LoginModel mModel;

    /**
     * 构造函数进行实例化
     * @param mView
     * @param mModel
     */
    public LoginPresentImpl(LoginView mView, LoginModel mModel) {
        this.mView = mView;
        this.mModel = mModel;
    }

    /**
     * 登陆方法
     * @param username
     * @param password
     */
    @Override
    public void loginSubmit(String username, String password) {
        mView.showProgress();
        mModel.loginSubmit(username,password,this);//在模型中通过接口分解结果,并将回调方法暴露出来,可以做到抽离开Model对View的联系。
    }

    @Override
    public void onLoginSuccess() {
        mView.loginSuccess();
    }

    @Override
    public void onLoginFail() {
        mView.loginFail();
        mView.hideProgress();
    }
}

(4)View 层  ,代码如下

/**
 * Created by aiyang on 2018/1/8.
 * UI接口
 */

public interface LoginView {
    /**
     * 显示进度条
     */
    void showProgress();

    /**
     * 隐藏进度条
     */
    void hideProgress();

    /**
     * 登录成功处理UI
     */
    void loginSuccess();

    /**
     * 登录失败处理UI
     */
    void loginFail();
}

BaseActivity.class

/**
 * Created by aiyang on 2018/7/3.
 */

public abstract class BaseActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_base);
        FrameLayout contentView = findViewById(R.id.view_content);
        LayoutInflater.from(this).inflate(setContentView(),contentView);

        init(savedInstanceState);
    }

    /**
     * 设置布局
     * @return
     */
    protected abstract int setContentView();

    /**
     * 子类初始化
     * @param savedInstanceState
     */
    protected abstract void init(Bundle savedInstanceState);

    /**
     * 设置标题
     * @param str
     */
    protected void setTitle(String str){
        if (str != null || !TextUtils.isEmpty(str)){
            TextView title_view = findViewById(R.id.title);
            title_view.setText(str);
        }
    }

    /**
     * 显示返回按钮
     * @param show
     */
    protected void setBack(boolean show){
        TextView back_view = findViewById(R.id.back);
        if (show){
            back_view.setVisibility(View.VISIBLE);
            back_view.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    finish();
                }
            });
        }else{
            back_view.setVisibility(View.INVISIBLE);
        }
    }

    /**
     * 普通页面跳转
     */
    protected void openActivity(Class Action){
        startActivity(new Intent(this,Action));
    }

}

LoginActivity.class

/**
 * Created by aiyang on 2018/7/3.
 * 登陆页面
 */

public class LoginActivity extends BaseActivity implements LoginView,View.OnClickListener{
    EditText name;
    EditText pwd;
    Button submit;

    ILoginPresenter presenter;
    @Override
    protected int setContentView() {
        return R.layout.activity_login;
    }

    @Override
    protected void init(Bundle savedInstanceState) {
        setTitle("登录");
        setBack(false);
        initView();

        presenter = new LoginPresentImpl(this,new UserInfo());


    }

    private void initView() {
        name = findViewById(R.id.name);
        pwd = findViewById(R.id.pwd);
        submit =findViewById(R.id.submit);
        submit.setOnClickListener(this);
    }
    @Override
    public void onClick(View view) {
        if (view.getId() == R.id.submit){
            String account = name.getText().toString().trim();
            String password =  pwd.getText().toString().trim();

            if (!TextUtils.isEmpty(account) && !TextUtils.isEmpty(password)){
                presenter.loginSubmit(account, password);
            }else {
                DialogUtil.showSimpleDialog(this,"错误提示","账户密码不能为空",null);
            }

        }
    }

    @Override
    public void showProgress() {
        DialogUtil.showProgress(this,"登陆中");
    }

    @Override
    public void hideProgress() {
        DialogUtil.dismissProgress();
    }

    @Override
    public void loginSuccess() {
        DialogUtil.showSimpleDialog(this, "登陆成功", "验证通过,是否进入到首页?", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                openActivity(MainActivity.class);
            }
        });
    }

    @Override
    public void loginFail() {
        DialogUtil.showSimpleDialog(this,"错误提示","账户密码不正确",null);
    }
}

MainActivity.class

public class MainActivity extends BaseActivity{


    @Override
    protected int setContentView() {
        return R.layout.activity_main;
    }

    @Override
    protected void init(Bundle savedInstanceState) {
        setTitle("首页");
        setBack(true);
    }
}


(1)优点

  • 项目结构清晰,解耦程度高,每个功能相互之间独立,可单独测试。
  • 代码维护性高,代码出现Bug,能够快速定位。
  • 功能扩展性强,增加业务功能时,并不需要去修改原始代码,只需增加相应的业务代码即可。

(2)缺点


  • 出现大量的presenter,增加了项目类的数量,代码量增大,项目依然看起来臃肿,有时业务简单就直接一个Activity搞定了。
  • 有时Activity和presenter有强引用关系,使用不当的话,容易出现内存泄漏的问题。
  • presenter与 View 之间的联系过与紧密,一旦视图变更,presenter也随之变更。



CSDN下载:http://download.csdn.net/download/csdn_aiyang/10194951


MVP高级使用。结合RXJava\Retrofit\okHttp等框架使用。

Githup地址:https://github.com/aiyangtianci/MVPDemo



你可能感兴趣的:(android,mvp,mvc,优缺点,实例源码,Android,UI\HTTP框架)