Android中的MVP模式及性能优化
2017-03-24 08:47
429 查看
声明:作者原创,转载注明出处。
作者:帅气陈吃苹果
View:视图,呈现用户界面。
Controller:控制器,处理用户交互。
(图片来源:MVC图片)
View:视图,呈现用户界面。
Presenter:中间者,负责调控View和Model之间的交互。
(图片来源:《不要再给MVP中Prensenter写接口了》)
MVP是MVC模式经过改良演变而来,二者都是用来分离UI、数据、业务和UI逻辑和的软件开发模式,controller/presenter负责交互的处理,model负责提供数据和逻辑处理,view负责显示和接收数据。
区别是:MVP模式中,View和Model不直接进行交互,而是采用Presenter这个中间者,通过绑定View和Model的接口,进行间接的交互。而在MVC中,View和Model是可以直接进行通信的。
大部分的Android应用采用的都是如下的开发模式:
(图片来源:《Amdrid MVP详解(上)》)
Activity既承担着View显示用户界面的任务,又包含了Controller处理业务逻辑的任务,因此Android中的MVC并不严格。
当项目规模大到一定程度,Activity就会像一个臃肿的胖子,行动不便。
行动不便体现在,当项目需求变更时,由于View和Model之间耦合度过高,导致代码改动变得复杂而庞大,不利于项目的功能扩展,也不便于进行单元测试。
在Android中,UI是线程不安全的,也就是只能在MainThread中才能进行UI更新,所以对View和Model的分离是合理的。
首先,先看一下项目结构:
1.MyUser.class
2.IUserView.class
对用户输入进行数据抽象,得到View的接口。
3.IUserModel.class
对需要用到的数据进行抽象,得到Model的接口,通过回调的方式进行过程的判断(在这里体现为,当用户注册或登陆时,可分为操作开始、操作成功、因用户原因导致的操作失败、因系统原因导致的操作失败四个过程)。
4.IUserModelImpl.class
接着对Model接口进行实现,其中涉及到Bmob后端云的数据服务,可以看成是在这里进行业务逻辑的具体操作(包括网络请求、后台进程、数据加载等)。
5.UserPresenter.class
然后创建一个中间者Presenter,持有View和Model的引用,通过对View和Model的绑定,将原本在View中的那些繁杂的操作指定给Model去实现,而不是View直接与Model进行交互。
6.LoginActivity.class
Activity就是View层中很典型的一个体现,所以要让他实现抽象出来的View接口。在View层中,只与中间者Presenter进行交互。
因为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
8.BasePresenter.class
9.RegisterActivity.class
可以看到,在这里,我们的登录界面和注册界面所需要的数据时一样的,那么,可以看出MVP的优势之一:
当项目需求变动,如数据的展现方式不一样而数据本身不存在变动时,我们只需要新建一个Activity或Fragment,在这个Activity或Fragment里同样对Presenter进行关联就可以了,而Presenter层和Model层的代码都不需要变动,这就是可扩展性的体现。
10.MainActivity.class
登录之后的主界面就是显示一段文本,没什么特别的。
总结:
MVP For Android对View层和Model实现了解耦,有利于提高应用的扩展性、健壮性,便于进行单元测试,但增加了很多代码量。
除了MVP,还有MVVM有待学习。
不同的项目有不同的业务需求,要根据具体需求和项目规模进行开发模式的选择。
源码下载:Github下载
个人博客:帅气陈吃苹果
作者:帅气陈吃苹果
一、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下载
个人博客:帅气陈吃苹果
相关文章推荐
- Android性能优化-StrictMode(严苛模式)
- [置顶] [android进阶篇]MVP模式优化,防止内存泄漏和空指针问题
- 浅谈Android中MVP模式用于实际项目中的问题与优化
- Android性能优化(一)内存泄露优化(静态变量、单例模式、属性动画)
- android开发_性能优化_单例模式
- Android Mvp架构设计与性能优化
- 求 架构设计 的视屏和 设计模式的视频 性能优化 的视频 系统源码分析 的视频 android
- Android 用户界面编程技巧和设计模式(性能优化)
- 浅谈Android开发中内存泄露与优化与框架模式之MVC与MVP
- [Android]ListView性能优化之视图缓存
- Android开发之性能优化概述
- Android 性能优化的一些方法
- Android代码性能优化技巧 (一)
- Android数据库SQLite性能优化技巧
- [Android]ListView性能优化之视图缓存 解决listView显示重复问题
- 【Android游戏开发十五】关于Android 游戏开发中 OnTouchEvent() 触屏事件的性能优化笔记!
- 15—关于Android 游戏开发中 OnTouchEvent() 触屏事件的性能优化笔记
- Android ListView 性能优化-----(异步加载图片资源)
- 用 WSAD5.0和SQLServer2K采用Meet-in-Middle模式开发CMP实体Bean及其完整客户端(下篇:客户端的开发和性能优化)
- [Android]ListView性能优化之视图缓存(续)