android MVP——mvp架构的应用和优化
来源:互联网 发布:mysql 死锁 编辑:程序博客网 时间:2024/05/03 17:29
MVP架构在android还是很好用的。我也在试着将mvp用在项目中。
下面我就来说说mvp模式的应用和优化。
mvp模式的概念
MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示。
比较
mvc:
1,在MVC里,View是可以直接访问Model的,View里会包含Model信息,不可避免的还要包括一些业务逻辑。
2,Model不依赖于View,但是View是依赖于Model。
3,有一些业务逻辑在View里实现了,导致要更改View也是比较困难的,至少那些业务逻辑是无法重用的。
mvp:
1,在MVP里,Presenter完全把Model和View进行了分离,主要的程序逻辑在Presenter里实现。
2,Presenter与具体的View是没有直接关联的,而是通过定义好的接口进行交互。从而使得在变更View时候可以保持Presenter的不变,即重用
3,应用程序的逻辑主要在Presenter来实现,其中的View是很薄的一层。这样一来就编写测试用的View,模拟用户的各种操作,从而实现对Presenter的测试
mvp的系统设计
我们先来个mvp系统的设计。(我们在这里模仿一个登陆的请求)
概要设计图
1,mobel层
mobel接口:规定操作数据的接口。
接口:IUserLoginMobel
/** * 用户操作接口 Model 业务层接口 */public interface IUserLoginMobel{ /** * 用户登录 * * @param name * @param pwd * @param loginListener */ void login(String name, String pwd, OnLoginListener loginListener);}
登录状态回调接口:
/** * 登陆状态接口 */public interface OnLoginListener { void loginSuccess(UserInfoBean user); void loginFailed(String message);}
类:用户登陆操作类 UserLoginModel
/** * 用户登陆操作类,Model 业务层(接收数据,处理出局) */public class UserLoginModel implements IUserLoginMobel { @Override public void login(final String name, final String pwd, final OnLoginListener loginListener) { new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(2000); if ("admin".equals(name) && "admin".equals(pwd)) { UserInfoBean userInfoBean = new UserInfoBean(); userInfoBean.setUserId(System.currentTimeMillis()); userInfoBean.setUserName(name); userInfoBean.setUserPwd(pwd); loginListener.loginSuccess(userInfoBean); } else { loginListener.loginFailed("密码错误"); } } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); }}
2,Presenter层
定义 UserLoginPresenter类
/** * 用户登录的任命者。Presenter (用于接收模型发出的结果,给view层发送命令) */public class UserLoginPresenter { protected IUserLoginView mvcView;//view的接口 private IUserLoginMobel userLoginMobel;//mobel的接口 private Handler mHandler = new Handler(); public UserLoginPresenter(IUserLoginView userLoginView) { this.mvcView = userLoginView; this.userLoginMobel = new UserLoginModel();//实例化用户登录业务层 } public void login() { mvcView.showLoading(); userLoginMobel.login(mvcView.getUserName(), mvcView.getPassword(), new OnLoginListener() { @Override public void loginSuccess(final UserInfoBean user) { //需要在UI线程执行 mHandler.post(new Runnable() { @Override public void run() { mvcView.toMainActivity(user); mvcView.hideLoading(); } }); } @Override public void loginFailed(final String message) { //需要在UI线程执行 mHandler.post(new Runnable() { @Override public void run() { mvcView.showFailedError(message); mvcView.hideLoading(); } }); } }); } public void clear() { mvcView.clearPassword(); mvcView.clearUserName(); }}
3,view 层
首相定义一个view接口 IUserLoginView
接口规定view层去实现的方法
/** * 完整的登陆接口。 View 接口 */public interface IUserLoginView { //获得用户信息 String getUserName(); String getPassword(); //清除用户信息 void clearUserName(); void clearPassword(); //遮罩层 void showLoading(); void hideLoading(); //登陆成功 void toMainActivity(UserInfoBean user); //登陆失败 void showFailedError(String message);}
我们定义一个MVCActivity来继承 IUserLoginView
/** * 这时候的activity相当于view (只负责显示数据) * Presenter与View交互是通过接口 */public class MVCActivity extends AppCompatActivity implements IUserLoginView { private EditText user_name_edit, user_pwd_edit; private Button user_login_btn, user_clear_btn; private ProgressBar user_login_bar; private UserLoginPresenter presenter; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); presenter=new UserLoginPresenter(this); initView(); } private void initView() { user_name_edit = (EditText) findViewById(R.id.user_name_edit); user_pwd_edit = (EditText) findViewById(R.id.user_pwd_edit); user_login_btn = (Button) findViewById(R.id.user_login_btn); user_clear_btn = (Button) findViewById(R.id.user_clear_btn); user_login_bar = (ProgressBar) findViewById(R.id.user_login_bar); user_login_btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { presenter.login(); } }); user_clear_btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { presenter.clear(); } }); } @Override public String getUserName() { return user_name_edit.getText().toString(); } @Override public String getPassword() { return user_pwd_edit.getText().toString(); } @Override public void clearUserName() { user_name_edit.setText(""); } @Override public void clearPassword() { user_pwd_edit.setText(""); } @Override public void showLoading() { user_login_bar.setVisibility(View.VISIBLE); } @Override public void hideLoading() { user_login_bar.setVisibility(View.INVISIBLE); } @Override public void toMainActivity(UserInfoBean user) { Toast.makeText(this, user.getUserName(), Toast.LENGTH_SHORT).show(); } @Override public void showFailedError(String message) { Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); }}
还有layout.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="用户名" /> <EditText android:id="@+id/user_name_edit" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="密码" /> <EditText android:id="@+id/user_pwd_edit" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" android:orientation="horizontal"> <Button android:id="@+id/user_login_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="登陆" /> <Button android:id="@+id/user_clear_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="清除" /> </LinearLayout> <ProgressBar android:id="@+id/user_login_bar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:visibility="invisible" /></LinearLayout>
我们先来看看效果图。
可以看出,和我们原来用mvc设计的登陆达到了一样的目的。但是可以看出我们的activity再也没有像原来一样多了很多逻辑处理。
而我们的逻辑处理都放在了Presenter层。而activity成了一个真正的view。
这就是mvp架构最基本的应用。可以看出使用mvp去设计app可以很好的将activity当作一个view层分离出来。
那么从上面的结构图可以看出我们还是有很多可以优化的地方的。
优化
mobel层 接口都继承IBaseMobel接口
我们可以在IBaseMobel这个元接口中定义一些整个mobel都会做的工作,例如初始化数据
/** * presenter层基类接口 */public interface IBaseMobel { void initData();//定义一个所有presenter初始化数据的方法}
同理:view层 接口都继承 IBaseView 接口
我们可以在IBaseView 这个元接口中定义一些整个mobel都会做的工作,例如初始化数据。
/** * view层基类接口 */public interface IBaseView { void initView();//view初始化view的一个基本接口}
presenter层继承一个基类来收集重复方法或者属性
定义一个 BasePresenter类
/** * 在基类presenter中将添加和销毁方法提供 */public class BasePresenter<T> { protected T mvcView; /** * 每个继承基类的presenter都要去实现构造方法,并传入view层 */ protected BasePresenter(T mvcView) { this.mvcView = mvcView; } /** * 因为presenter层持有view层,所以,提供一个方法,在view层不使用的时候将对象释放 */ public void onDestroy() { mvcView = null; }}
我们来看看如何让原来的UserLoginPresenter使用
修改后的UserLoginPresenter类
/** * 用户登录的任命者。Presenter (用于接收模型发出的结果,给view层发送命令) */public class UserLoginPresenter extends BasePresenter<IUserLoginView> { private IUserLoginMobel userLoginMobel;//mobel的接口 private Handler mHandler = new Handler(); public UserLoginPresenter(IUserLoginView userLoginView) { super(userLoginView); //this.userLoginView = userLoginView;//未优化前的方法 this.userLoginMobel = new UserLoginModel();//实例化用户登录业务层 } public void login() { mvcView.showLoading(); userLoginMobel.login(mvcView.getUserName(), mvcView.getPassword(), new OnLoginListener() { @Override public void loginSuccess(final UserInfoBean user) { //需要在UI线程执行 mHandler.post(new Runnable() { @Override public void run() { mvcView.toMainActivity(user); mvcView.hideLoading(); } }); } @Override public void loginFailed(final String message) { //需要在UI线程执行 mHandler.post(new Runnable() { @Override public void run() { mvcView.showFailedError(message); mvcView.hideLoading(); } }); } }); } public void clear() { mvcView.clearPassword(); mvcView.clearUserName(); }}
这样我们就把Presenter的初始化工作和关于activity在onDestory的时候手动置空Presenter中view对象(这时候的view对象其实就是activity)的方法给提取到基类中。
因为这两个方法很多地方会用到,所以我们不必每次都去写它们。
我们再将view层的activity封装。view层例如:fragment也是可以封装的。
我们建立一个抽象类BaseActivity。
/** * mvc模式的view层基类<继承 presenter 具体> */public abstract class BaseActivity<T extends BasePresenter> extends AppCompatActivity { //必须实例化presenter对象 public abstract T initPresenter(); public T presenter; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(setMvcView()); presenter = initPresenter(); initView(); } protected abstract int setMvcView(); protected abstract void initView(); @Override protected void onDestroy() { presenter.onDestroy(); super.onDestroy(); }}
接下来修改MVCActivity类
/** * 这时候的activity相当于view (只负责显示数据) * Presenter与View交互是通过接口 */public class MVCActivity extends BaseActivity<UserLoginPresenter> implements IUserLoginView { private EditText user_name_edit, user_pwd_edit; private Button user_login_btn, user_clear_btn; private ProgressBar user_login_bar; @Override public UserLoginPresenter initPresenter() { return new UserLoginPresenter(this); } @Override protected int setMvcView() { return R.layout.activity_main; } @Override protected void initView() { user_name_edit = (EditText) findViewById(R.id.user_name_edit); user_pwd_edit = (EditText) findViewById(R.id.user_pwd_edit); user_login_btn = (Button) findViewById(R.id.user_login_btn); user_clear_btn = (Button) findViewById(R.id.user_clear_btn); user_login_bar = (ProgressBar) findViewById(R.id.user_login_bar); user_login_btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { presenter.login(); } }); user_clear_btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { presenter.clear(); } }); } @Override public String getUserName() { return user_name_edit.getText().toString(); } @Override public String getPassword() { return user_pwd_edit.getText().toString(); } @Override public void clearUserName() { user_name_edit.setText(""); } @Override public void clearPassword() { user_pwd_edit.setText(""); } @Override public void showLoading() { user_login_bar.setVisibility(View.VISIBLE); } @Override public void hideLoading() { user_login_bar.setVisibility(View.INVISIBLE); } @Override public void toMainActivity(UserInfoBean user) { Toast.makeText(this, user.getUserName(), Toast.LENGTH_SHORT).show(); } @Override public void showFailedError(String message) { Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); }}
我们把activity的onCreate和onDestory交给基类来处理,我们也可以在基类中处理onResume等方法。所有的生命周期由基类来管理。
另外,我们将Presenter对象也交给基类来管理,让基类来处理Presenter对象中的公有方法。
mvp架构的应用和优化已经写完了,我们可以根据自己项目的实际情况作进一步的优化。
mvp其实就是一种设计思想。它不光用于对model、presenter和view的处理。这只是一种思想,可以用到很多地方。
更多的mvp模式可以去参照:
Google在Github开源的一个项目:Android Architecture Blueprints
简书:Android官方MVP架构项目解析
献上这篇博客的demo:
demo下载
- android MVP——mvp架构的应用和优化
- Android——MVP架构
- 架构:Android的MVP
- android的mvp架构
- android的MVP架构
- Android MVC和MVP架构的详解
- MVP之Android官方MVP架构学习—View层和Presenter层
- 指尖资讯——基于MVP架构、遵循Material Design的Android应用
- Android架构系列-MVP架构的实际应用
- 浅言架构——Android MVP ...
- Android的架构设计——MVC、MVP
- Android APP架构设计——MVP的使用示例
- Android MVP架构的使用
- Android mvp 架构的自述
- android下的MVP架构
- Android mvp 架构的自述
- Android MVP架构的自述
- Android mvp 架构的自述
- Xutils中httputils请求(Post请求)
- nrf51822学习之BLE400初次接触
- 【Leetcode】TwoSum的解析
- 小型电子商务网站数据管理系统
- PHP集成包
- android MVP——mvp架构的应用和优化
- 【JZOJ3397】雨天的尾巴
- 回调函数初识
- Unity 4.x 各版本IOS IL2CPP对比
- 剑指offer之编程(十三)
- Android学习笔记(2)
- kaggle 入门 Bike sharing Demand prediction
- Python中sorted()方法的用法
- ffmpeg常见的命令行参数