Android中MVP的使用
以前在写项目的时候,没有过多考虑架构模式的问题,所以一开始使用的都是类似MVC的架构,严格来讲,之前的项目都不算MVC模式,只是简单的将网络请求与界面分离,然后通过Handler通知更新界面。随着项目越来越大,Activity和Fragment中代码越来越多,导致项目的维护变得越来越复杂,一开始重构成了MVC模式,随着深入了解,发现MVP模式能够大大减少Model和View层之间的耦合度,遂又将项目重构成MVP模式。最近又发现MVP已经开始不能满足需求,正在研究MVVM,后期会将MVVM的研究成果奉上。
当然不是所有的模块都使用MVP,MVP适合在一些逻辑复杂的模块中使用,如果业务逻辑简单,一个Activity就能搞定,也没有必要生搬硬套的做成MVP模式的。只要整体的模式思路使用MVP模式,其中夹杂一些MVC模式,甚至简单的模块不使用模块,不会对整个项目有太大的影响,反而是最优选择。
MVP模式也有其缺点,首先就是代码量多了,文件数量大大提升,但换来的好处是项目结构清晰,在前期只有原型图的时候,我们也可以提前开发,Model层负责数据的请求和数据的处理,写好Presenter层和View层后,基本不用改,只需关注Model层的变化就可以了;还是就是Activity内部类基本不再使用了,部分UI逻辑判断也可以转到Presenter去处理,View层(UI)也非常清晰干净。
什么是MVP
MVP是 模型(Model)、视图(View)、主持人(Presenter)的缩写,代表不同模块。
模型(Model):负责处理数据的加载或者存储;比如从网络或者本地数据库获取数据。
视图(View):负责界面数据的展示,与用户进行交互。
主持人(Presenter):相当于协调者,是Model和View层之间的桥梁,占据主导地位。

如图所示,Model层和View并不直接交互,而是通过Presenter层进行交互。Presenter持有Model层和View层的Interface的引用,而View层持有Presenter层Interface的引用。当View层需要展示数据的时候,会调用Presenter层的某个接口,然后Presenter层会调用Model层请求数据,当Model层数据获取成功后会调用Presenter层的回调方法通知Presenter层数据加载完毕,然后Presenter层再调用View层的接口将获取到的数据展示给用户。
整个过程,View层告知Presenter层需要什么数据,Presenter层调用Model获取数据,Model数据获取完成告知Presenter层,Presenter层再调用View层将数据展示给用户。
这样分层减少了Model层和View层的耦合度:
1、可以使得Model层和View层单独开发与测试,互补依赖;
2、可以将Model层封装复用,减少代码量
3、方便构建单元测试,测试逻辑中存在的潜在BUG
4、清晰的结构,便于维护
准备工作
创建LoginBean类
|
public class
LoginBean { private
String mName; private
String mPassWord;
public
String getmName(){ return
mName; }
public void
setmName(String name){ mName
= name; }
public
String getmPassWord(){ return
mPassWord; }
public void
setmPassWord(String passWord){ mPassWord
= passWord; } }
|
模型层(Model)
1. 先写一个接口
|
public interface
ILoginModel { void
login(String username, String password, OnLoginListener loginListener);
interface
OnLoginListener{ void
LoginSuccess(LoginBean loginBean); void
LoginFail(); } }
|
2. 继承接口进行数据请求
|
public class
LoginModel implements
ILoginModel {
@Override public void
login(String username, String password, OnLoginListener loginListener) { //模拟子线程耗时操作 simulationRequest(username, password, loginListener); }
private void
simulationRequest(final
String username,
final String password,final
OnLoginListener loginListener) { new
Thread(){ @Override public void
run() { try
{ Thread.sleep(2000); } catch
(InterruptedException e) { e.printStackTrace(); } if(username.equals("Lking")
&& password.equals("123456")){//登录成功 LoginBean loginBean =
new LoginBean(); loginBean.setmName(username); loginBean.setmPassWord(password); loginListener.LoginSuccess(loginBean); }else{//登录失败 loginListener.LoginFail(); } } }.start(); } }
|
视图层(View)
1、先写一个接口
|
public interface
ILoginView { /**
获取账号*/ String getName(); /**
获取密码*/ String getPassword(); /**
显示加载圈*/ void
showLoading(); /**
关闭加载圈*/ void
hideLoading(); /**
跳转到主界面*/ void
toMainActivity(LoginBean loginBean); /**
显示失败提示*/ void
showFailToast(); }
|
2、编写布局文件activity_mvp.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:background="@color/color_ffffff" android:orientation="vertical">
<LinearLayout android:layout_width="match_parent" android:layout_height="45dp" android:layout_margin="10dp">
<TextView android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center" android:text="账 号:" android:textColor="@color/color_000000" android:textSize="18sp"
/>
<EditText android:id="@+id/lking_mvp_name_edt" android:layout_width="match_parent" android:layout_height="match_parent" android:hint="请输入账号" android:textColor="@color/color_000000" android:textSize="16sp"
/> </LinearLayout>
<LinearLayout android:layout_width="match_parent" android:layout_height="45dp" android:layout_margin="10dp">
<TextView android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center" android:text="密 码:" android:textColor="@color/color_000000" android:textSize="18sp"
/>
<EditText android:id="@+id/lking_mvp_pas_edt" android:layout_width="match_parent" android:layout_height="match_parent" android:hint="请输入密码" android:textColor="@color/color_000000" android:textSize="16sp"
/> </LinearLayout>
<TextView android:id="@+id/lking_mvp_login_txt" android:layout_width="match_parent" android:layout_height="45dp" android:gravity="center" android:text="登录" android:textColor="@color/color_000000" android:textSize="18sp"
/>
<ProgressBar android:id="@+id/lking_mvp_login_pro" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:visibility="gone" android:layout_marginTop="20dp"
/> </LinearLayout>
|
3、继承接口进行数据显示
|
public class
MvpActivity extends
Activity implements
ILoginView { private
EditText mNameEdt; private
EditText mPasswordEdt; private
TextView mLoginBtn; private
ProgressBar mLoading; private
LoginPresenter mLoginPresenter; @Override protected void
onCreate(@Nullable
Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_mvp);
initViews(); }
/** *
初始化控件 */
private void
initViews() { mNameEdt
= (EditText) findViewById(R.id.lking_mvp_name_edt); mPasswordEdt
= (EditText) findViewById(R.id.lking_mvp_pas_edt); mLoginBtn
= (TextView) findViewById(R.id.lking_mvp_login_txt); mLoading
= (ProgressBar) findViewById(R.id.lking_mvp_login_pro); mLoginPresenter
= new
LoginPresenter(this);
mLoginBtn.setOnClickListener(new
View.OnClickListener() { @Override public void
onClick(View v) { mLoginPresenter.login(); } }); }
@Override public
String getName() { return
mNameEdt.getText().toString().trim(); }
@Override public
String getPassword() { return
mPasswordEdt.getText().toString().trim(); }
@Override public void
showLoading() { mLoading.setVisibility(View.VISIBLE); }
@Override public void
hideLoading() { mLoading.setVisibility(View.GONE); }
@Override public void
toMainActivity(LoginBean loginBean) { Toast.makeText(this,
"登录成功,跳转到主界面", Toast.LENGTH_SHORT).show(); }
@Override public void
showFailToast() { Toast.makeText(this,
"登录失败,请重新输入账号和密码", Toast.LENGTH_SHORT).show(); mNameEdt.setText(""); mPasswordEdt.setText(""); }
}
|
主持人(Presenter)
|
创建管理逻辑类
public class
LoginPresenter { private
ILoginView mILoginView; private
LoginModel mLoginModel; private
Handler mHandler
= new
Handler();
public
LoginPresenter(ILoginView iLoginView) { mILoginView
= iLoginView; mLoginModel
= new
LoginModel(); }
public void
login(){ mILoginView.showLoading(); mLoginModel.login(mILoginView.getName(),
mILoginView.getPassword(),
new ILoginModel.OnLoginListener() { @Override public void
LoginSuccess(final
LoginBean loginBean) { mHandler.post(new
Runnable() { @Override public void
run() { mILoginView.hideLoading(); mILoginView.toMainActivity(loginBean); } });
}
@Override public void
LoginFail() { mHandler.post(new
Runnable() { @Override public void
run() { mILoginView.hideLoading(); mILoginView.showFailToast(); } });
} }); } }
|