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();
}
});
}
});
}
}
|