Android中的MVP模式及性能优化

声明:作者原创,转载注明出处。

作者:帅气陈吃苹果

一、MVC

Model:模型,处理业务逻辑。

View:视图,呈现用户界面。

Controller:控制器,处理用户交互。

这里写图片描述
(图片来源:MVC图片

二、MVP

Model:模型,处理业务逻辑。

View:视图,呈现用户界面。

Presenter:中间者,负责调控View和Model之间的交互。

这里写图片描述

(图片来源:《不要再给MVP中Prensenter写接口了》

MVP是MVC模式经过改良演变而来,二者都是用来分离UI、数据、业务和UI逻辑和的软件开发模式,controller/presenter负责交互的处理,model负责提供数据和逻辑处理,view负责显示和接收数据。

区别是:MVP模式中,View和Model不直接进行交互,而是采用Presenter这个中间者,通过绑定View和Model的接口,进行间接的交互。而在MVC中,View和Model是可以直接进行通信的。

三、MVP For Android

架构的意义之一在于,让应用程序提高可扩展性。

大部分的Android应用采用的都是如下的开发模式:

这里写图片描述

(图片来源:《Amdrid MVP详解(上)》

Activity既承担着View显示用户界面的任务,又包含了Controller处理业务逻辑的任务,因此Android中的MVC并不严格。

当项目规模大到一定程度,Activity就会像一个臃肿的胖子,行动不便。

行动不便体现在,当项目需求变更时,由于View和Model之间耦合度过高,导致代码改动变得复杂而庞大,不利于项目的功能扩展,也不便于进行单元测试。

在Android中,UI是线程不安全的,也就是只能在MainThread中才能进行UI更新,所以对View和Model的分离是合理的。

四、示例

这个示例采用我上一篇博客《Bmob后端云初体验》的Demo,使用Bmob后端云实现一个登陆注册的例子。如果你感兴趣可以点击阅读,当然,不读也没多大影响。

首先,先看一下项目结构:

这里写图片描述

1.MyUser.class

public class MyUser extends BmobObject {
    //用户名
    private String userName;

    //密码
    private String userPwd;

    public MyUser() {

    }

    public MyUser(String name, String pwd) {
        this.userName = name;
        this.userPwd = pwd;
    }

    public String getUserName() {
        return this.userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getUserPwd() {
        return this.userPwd;
    }

    public void setUserPwd(String userPwd) {
        this.userPwd = userPwd;
    }
}

2.IUserView.class

对用户输入进行数据抽象,得到View的接口。

public interface IUserView {

/**
 * 获取用户输入的用户名
 */
String getUserName();

/**
 * 获取用户输入的密码
 */
String getUserPwd();

/**
 * 加载进度对话框
 */
void showLoading();

/**
 * 隐藏进度对话框
 */
void hideLoading(); 
}

3.IUserModel.class

对需要用到的数据进行抽象,得到Model的接口,通过回调的方式进行过程的判断(在这里体现为,当用户注册或登陆时,可分为操作开始、操作成功、因用户原因导致的操作失败、因系统原因导致的操作失败四个过程)。

public interface IUserModel {

    /**
     * 用户登录
     * @param name
     * @param pwd
     * @param listener
     */
    void checkUser(String name,String pwd,OnUserOperationListener listener);

    /**
     * 用户注册
     * @param name
     * @param pwd
     * @param listener
     */
    void registerUser(String name,String pwd,OnUserOperationListener listener);

    interface OnUserOperationListener {

        /**
         * 操作开始
         */
        void onOperationBegin();

        /**
         * 操作成功
         */
        void onSuccess();

        /**
         * 因用户原因,导致操作失败
         */
        void onUserFailed();

        /**
         * 因系统原因,导致操作失败
         */
        void onSysFailed();
    }
}

4.IUserModelImpl.class

接着对Model接口进行实现,其中涉及到Bmob后端云的数据服务,可以看成是在这里进行业务逻辑的具体操作(包括网络请求、后台进程、数据加载等)。

public class IUserModelImpl implements IUserModel {

    /**
     * 由model进行具体的业务逻辑操作,检查用户名和密码
     * @param name
     * @param pwd
     */
    @Override
    public void checkUser(String name, String pwd, final OnUserOperationListener listener) {
        //开始检查过程
        listener.onOperationBegin();
        BmobQuery<MyUser> userQuery = new BmobQuery<MyUser>();
        userQuery.addWhereEqualTo("userName",name);
        userQuery.addWhereEqualTo("userPwd",pwd);
        userQuery.findObjects(new FindListener<MyUser>() {
            @Override
            public void done(List<MyUser> list, BmobException e) {
                if(e == null) {
                    if(list.size() == 1) {
                        listener.onSuccess();
                    } else {
                        listener.onUserFailed();
                    }
                } else {
                    listener.onSysFailed();
                }
            }
        });
    }

    @Override
    public void registerUser( String name, String pwd, final OnUserOperationListener listener) {
        listener.onOperationBegin();
        MyUser mUser = new MyUser();
        mUser.setUserName(name);
        mUser.setUserPwd(pwd);
        mUser.save(new SaveListener<String>() {
            @Override
            public void done(String s, BmobException e) {
                if(e == null) {;
                    listener.onSuccess();
                } else {
                    listener.onSysFailed();
                }
            }
        });
    }
}

5.UserPresenter.class

然后创建一个中间者Presenter,持有View和Model的引用,通过对View和Model的绑定,将原本在View中的那些繁杂的操作指定给Model去实现,而不是View直接与Model进行交互。

public class UserPresenter extends BasePresenter<IUserView>{

    //model
    private IUserModel mUserModel;

    //view
    private IUserView mUserView;

    /**
     * 实例化view
     * @param mUserview
     */
    public UserPresenter(IUserView mUserview) {
        super();
        this.mUserModel = new IUserModelImpl();
        this.mUserView = mUserview;
    }

    /**
     * bind view and model for user login
     *
     * @param context   上下文环境
     * @param name  用户名
     * @param pwd   密码
     */
    public void check(final Context context, String name, String pwd) {
        //显示进度对话框
        mUserView.showLoading();
        if(mUserModel != null) {
            mUserModel.checkUser(name, pwd, new IUserModel.OnUserOperationListener() {
                @Override
                public void onOperationBegin() {
                }

                @Override
                public void onSuccess() {
                    mUserView.hideLoading();
                    Toast.makeText(getApplicationContext(),
                            "登录成功!",
                            Toast.LENGTH_SHORT).show();
                    Intent intent = new Intent(getApplicationContext(),MainActivity.class);
                    context.startActivity(intent);
                }

                @Override
                public void onUserFailed() {
                    mUserView.hideLoading();
                    Toast.makeText(getApplicationContext(),
                            "用户名或密码错误,请重新输入!",
                            Toast.LENGTH_SHORT).show();
                }

                @Override
                public void onSysFailed() {
                    mUserView.hideLoading();
                    Toast.makeText(getApplicationContext(),
                            "登录失败,请检查网络设置!",
                            Toast.LENGTH_SHORT).show();
                }
            });
        }
    }

    /**
     * bind view and model for user register
     *
     * @param context
     * @param name
     * @param pwd
     */
    public void register(final Context context,String name,String pwd) {
        //显示进度对话框
        mUserView.showLoading();
        if(mUserModel != null) {
            mUserModel.registerUser(name, pwd, new IUserModel.OnUserOperationListener() {
                @Override
                public void onOperationBegin() {
                }

                @Override
                public void onSuccess() {
                    mUserView.hideLoading();
                    Toast.makeText(getApplicationContext(),
                            "注册成功!",
                            Toast.LENGTH_SHORT).show();
                    ((Activity) context).finish();
                }

                @Override
                public void onUserFailed() {
                    mUserView.hideLoading();
                    Toast.makeText(getApplicationContext(),
                            "用户名或密码不合法,请重新输入!",
                            Toast.LENGTH_SHORT).show();
                }

                @Override
                public void onSysFailed() {
                    mUserView.hideLoading();
                    Toast.makeText(getApplicationContext(),
                            "你可能长得太丑,网络都看不下去了 ^_^ ",
                            Toast.LENGTH_SHORT).show();
                }
            });
        }
    }
}

6.LoginActivity.class

这里写图片描述

Activity就是View层中很典型的一个体现,所以要让他实现抽象出来的View接口。在View层中,只与中间者Presenter进行交互。

public class LoginActivity extends BaseActivity<IUserView,UserPresenter> implements View.OnClickListener,IUserView{

    private EditText editName;

    private EditText editPwd;

    private Button btnLogin;

    private Button btnToRegister;

    //进度对话框
    ProgressDialog progressDialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //初始化BmobSDK,
        Bmob.initialize(this, "4fd01c1c4eaca3d97c85e36494554549");
        setContentView(R.layout.activity_login);
        initView();
    }

    /**
     * 控件初始化
     */
    private void initView() {
        editName = (EditText) findViewById(R.id.edit_login_name);
        editPwd = (EditText) findViewById(R.id.edit_login_pwd);
        btnLogin = (Button) findViewById(R.id.btn_login);
        btnToRegister = (Button) findViewById(R.id.btn_to_register);
        btnLogin.setOnClickListener(this);
        btnToRegister.setOnClickListener(this);

        progressDialog = new ProgressDialog(this);
        progressDialog.setTitle("登陆");
        progressDialog.setMessage("正在登陆...");
        progressDialog.setCancelable(false);
    }

    /**
     * 重写按钮的点击事件
     * @param view
     */
    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn_login:
                userLogin();
                break;
            case R.id.btn_to_register:
                toRegister();
                break;
            default:
                break;
        }
    }

    /**
     * 去注册
     */
    private void toRegister() {
        Intent intentReg = new Intent(LoginActivity.this,RegisterActivity.class);
        startActivity(intentReg);
    }

    /**
     * 登陆
     */
    private void userLogin() {
        //初始化中间者
        mPresenter = new UserPresenter(this);
        //通过中间者进行用户名和密码的检查
        mPresenter.check(this,getUserName(),getUserPwd());
    }

    @Override
    public String getUserName() {
        return editName.getText().toString();
    }

    @Override
    public String getUserPwd() {
        return editPwd.getText().toString();
    }

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

    @Override
    public void hideLoading() {
        progressDialog.hide();
    }

    @Override
    protected UserPresenter createPresenter() {
        return new UserPresenter(this);
    }

}

因为Presenter是用过View和Model的接口对View、Model进行访问的,它持有他们的引用。

存在这样一种情况,当Activity通过Presenter在Model进行业务逻辑的具体实现操作时,很可能这些操作是耗时的,假设有一个耗时长达5s的操作,而在这5s里,如果Activity被销毁(用户离开此界面),而Presenter还持有对View的引用,就会造成内存泄漏了。

如果不对这个问题进行处理,当一个应用有很多个Activity,假设一个Activity需要进行十个耗时操作,那么将严重降低应用的性能。

所以,需要让Activity继承一个父类BaseActivity,在这个BaseActivity中,当onCreate()方法执行,则关联Presenter,当onDestroy()执行,则解除对Presenter的关联,让Presenter继承一个BasePresenter,当系统内存不足时,优先释放Model,而不是View,这样用户体验才好。就好像一个爱臭美的人要被打时说,有事好商量,别打脸行么。当敌人来势不汹,门面重要。

7.BaseActivity.class

public abstract class BaseActivity<V,T extends BasePresenter<V>> extends AppCompatActivity {

    protected T mPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_base);
        //创建Presenter
        mPresenter = createPresenter();
        //关联View
        mPresenter.attachView((V) this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //解除关联
        mPresenter.detachView();
    }

    protected abstract T createPresenter();
}

8.BasePresenter.class

public abstract class BasePresenter<T> {

    //当内存不足时,释放内存
    protected WeakReference<T> mViewRef;

    /**
     * bind view with presenter
     * @param view
     */
    public void attachView(T view) {
        mViewRef = new WeakReference<T>(view);
    }

    public void detachView() {
        if(mViewRef != null) {
            mViewRef.clear();
            mViewRef = null;
        }
    }

    protected T getView() {
        return mViewRef.get();
    }
}

9.RegisterActivity.class

这里写图片描述

可以看到,在这里,我们的登录界面和注册界面所需要的数据时一样的,那么,可以看出MVP的优势之一:

当项目需求变动,如数据的展现方式不一样而数据本身不存在变动时,我们只需要新建一个Activity或Fragment,在这个Activity或Fragment里同样对Presenter进行关联就可以了,而Presenter层和Model层的代码都不需要变动,这就是可扩展性的体现。

public class RegisterActivity extends BaseActivity<IUserView,UserPresenter> implements IUserView{

    private EditText editName;

    private EditText editPwd;

    private Button btnRegister;

    private UserPresenter mUserPresenter;

    //进度对话框
    ProgressDialog progressDialog;

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

    /**
     * 控件初始化
     */
    private void initView() {
        editName = (EditText) findViewById(R.id.edit_register_name);
        editPwd = (EditText) findViewById(R.id.edit_register_pwd);
        btnRegister = (Button) findViewById(R.id.btn_register);
        btnRegister.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                userRegister();
            }
        });

        progressDialog = new ProgressDialog(this);
        progressDialog.setTitle("注册");
        progressDialog.setMessage("正在注册...");
        progressDialog.setCancelable(false);
    }

    /**
     * 用户注册,即添加一行数据
     */
    private void userRegister() {
        mUserPresenter = new UserPresenter(this);
        mUserPresenter.register(this,getUserName(),getUserPwd());
    }

    @Override
    public String getUserName() {
        return editName.getText().toString();
    }

    @Override
    public String getUserPwd() {
        return editPwd.getText().toString();
    }

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

    @Override
    public void hideLoading() {
        progressDialog.hide();
    }

    @Override
    protected UserPresenter createPresenter() {
        return new UserPresenter(this);
    }
}

10.MainActivity.class

这里写图片描述

登录之后的主界面就是显示一段文本,没什么特别的。

public class MainActivity extends AppCompatActivity {

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

总结:

MVP For Android对View层和Model实现了解耦,有利于提高应用的扩展性、健壮性,便于进行单元测试,但增加了很多代码量。

除了MVP,还有MVVM有待学习。

不同的项目有不同的业务需求,要根据具体需求和项目规模进行开发模式的选择。

源码下载:Github下载

个人博客:帅气陈吃苹果

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值