您的位置:首页 > 移动开发 > Android开发

Android中的MVP模式及性能优化

2017-03-24 08:47 429 查看
声明:作者原创,转载注明出处。

作者:帅气陈吃苹果

一、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 on
b64d
OperationBegin() {
}

@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下载

个人博客:帅气陈吃苹果
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: