Android MVP 架构介绍与使用入门 [复制链接]

2019-8-12 09:54
小小CTO 阅读:987 评论:1 赞:1
Tag:  MVP

MVP 与 MVC 简介

MVP 软件架构在现在的应用中, 特别是 Android 端的编程中尤为突出的使用,因为 MVP 架构可以很深层次的去解耦视图、业务逻辑、数据源三者的关系,让它们之间的相互依赖性降低。MVP 是针对 Android 端开发而言的,它其实是 MVC 演变过来的,因为 MVC 模式在 Android 开发中并不是那么愉快。主要表现方面,比如,我们在写一个功能模块时,Activity 中的代码很容易就突?#39057;?#19978;千甚至上万行,除了必要的 findviewid,listener 等,其他的代码几乎都是业务逻辑相关的,这就显得 Activity 非常的臃肿,也不是说不可以,但是在模块升级时造成业务逻辑的改变,我们就需要去成千上万行的 Activity 中寻找业务逻辑代码,这就有可能出现多处代码需要修?#27169;?#19981;细心的话,非常容易出 Bug,而?#39029;?#20102; Bug 代码也不好定位。

第二个问题,数据源(来自数据库或这网络)部分可能会写好几份,比如,有两个 Activity 同时有着个人信息数据要显示,我必须在这两个 Activity 中各请求一次网络或者访问数据库,得到最新的数据,这就出现了代码重复的问题。

带着这两个问题,我们来看看 MVP 架构能够怎样帮助我们解决 MVC 出现的这种问题!首先,?#20381;?#31616;单的介绍一下 MVC 它指的是:

  • model(模型),负责访问网络数据、访问数据库数据,提供数据源
  • view(视图),负责更?#38470;?#38754;、响应用户界面操作
  • controller(控制器),负责业务逻辑控制,处理数据

我们在 Android 开发中,Activity 负责 UI 显示及更新操作,相当于 View 层,我们?#35805;?#37117;会把代码直?#26377;?#22312; Activity 中,不管是数据源、逻辑控制、数据处理等操作一个劲的往 Activity 里写,那么它们三者的关系都互相依赖,你中有我,我中有你,它们就形成了三角的关系。

Android MVP 架构介绍与使用入门

这样的关系,导致 Activity 代码量过于庞大,修改和维护起来比较困难。这样的三角恋关系,显然不是长久之际。于是,MVP 它就强行把 View 与 Model 隔离了,让它们不再有联系,传说中的 Presenter 就是脚踏两只船,来回奔走于 View 与 Model 之间的契约者、联系者。

MVP(model、view 、presenter),将 Activity 中的代码抽离了出来,把 Activity(Fragment、View) 只当作 View 层,通过一个 Presenter(契约层)将 View 和 Model 层联系起来,让 View 和 Model 层充分的解耦。

我们访问网络得到数据并显示出来会是这样一个流程:

  1. Activity 启动时,告诉 Presenter 我要数据了
  2. Presenter ?#31361;?#21483; Model 去访问数据接口得到数据
  3. Model 得到原数据就交给 Presenter 了,Presenter 一看数据不规范,赶紧处理一下,处理完成
  4. Presenter 就把处理后的数据汇报给 Activity(View),View 拿到数据就做显示的操作,工作结束

MVC

下面我们通过一个简单的案例来进行了解 MVP 架构的代码写法,?#27604;唬?#36825;里的写法不唯一,因为每个人写代码的方式都是不一样的,最核心的是 MVP 架构的思想,把思想转为?#23548;?#20195;码,这才是值得我们探索和学习的地方。

来看看代码,我们在网络请求时,获取到数据,并 toast 和显示到 textview 上,这样的代码我相信谁都会写,但?#35805;?#20154;都是直接一口气写在 MainActivity 类上面,比如这样:

public class MainActivity extends AppCompatActivity {

private TextView tv;
private static final String TAG = "MainActivity";

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

initViews();

request();
showDialog();
}

private void initViews() {
tv = findViewById(R.id.tv);
}

private void showDialog() {
ProgressDialog dialog = new ProgressDialog(this);
dialog.show();
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
dialog.dismiss();
}
}, 1500);
}

private void request() {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://www.baidu.com/")
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}

@Override
public void onResponse(Call call, Response response) throws IOException {
String resp = response.body().string();
toast(resp);
}
});
}

private void toast(String resp) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "" + resp, Toast.LENGTH_SHORT).show();
tv.setText(resp);
}
});
}

}

它的效果其实就是请求百度首页,返回网页数据以 string 方式显示出来而已。

Android MVP 架构介绍与使用入门

上面的代码,其实就是 MVC 架构的写法,把 Model(网页数据)、Presenter(数据处理)、View(activity)三层写在了一起,虽然没什?#27425;?#39064;,但我们看看 MainActivity 中的代码比?#26174;?#20081;,在业务代码比较多的情况下,MainActivity 更加的沉重,不利于维护与业务升级。

MVP 实战

那?#27425;?#20204;下面就要将这个类中的代码改写为 MVP 的写法,回顾上面提及的 MVP 架构的思想,它是将 View 层与 Model 层彻底隔离,意味着 View 和 Model 都不再持对方的引用,它们通过一个第三者 Presenter 来代理事物的传递,所以 Presenter 层会持有 Model 与 View 层的引用,这是第一步。

第二步,是将它们之间的联系抽象出来,以接口的方式相互调用,所以 Model 、View、Presenter 各自拥有自己的接口和抽象方法,所以这?#31361;?#26080;形的多出了三个接口类,这也就是 MVP 的缺点之一。所以,为了较少的创建接口类,我们就给这三层接口定义了一个契约接口,把它们更加紧密的结合在一起,方法查看,例如代码这样写:

(1)契约类:

package com.test.mvp.mvpdemo.mvp.v1;

import okhttp3.Callback;

/**
* 契约接口,可以很直观的看到 M、V、P 层接口中提供的方法
*/
public interface MainContract {
interface IMainModel {
void requestBaidu(Callback callback);
}

interface IMainView {
void showDialog();

void succes(String content);
}

interface IMainPresenter {
void handlerData();
}
}

然后,再将之前的一个单独的 MainActivity 分包,分别创建 Model 层实现类、Presenter 层实现类、MainActivity 就相当于View 层,这样一来架构就更加清晰明了:

Android MVP 架构介绍与使用入门

接着,分别给这三层实现我们刚刚写的 MainContract 中相对应的接口,我们先来看看 Model 层,它就主要负责网络请求,也就是我们的 OKHttp 请求到百度首页的一个操作,很简单的代码。

(2)Model 层:

package com.test.mvp.mvpdemo.mvp.v1.model;

import com.test.mvp.mvpdemo.mvp.v1.MainContract;

import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;

/**
* model 层,请求网络或数据库,提供数据源(原始数据)
*/
public class DataModel implements MainContract.IMainModel {

@Override
public void requestBaidu(Callback callback) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://www.baidu.com/")
.build();
client.newCall(request).enqueue(callback);
}
}

这里需要传入一个 OKHttp 的 Callback ,由于不能在 Model 层中处理业务数据,所以我们让它自己回调给 Presenter 层来处理,保证数据的原始状态。

(3)Presenter 层:

然后是 Presenter 层,它是处理业务逻辑和业务数据的,所以必须持有 Model 的引用,同?#24065;?#23558;处理完的数据交给 View 层用于显示,也必须持有 View 的引用,那么,一开始我们就要把这两层给实例化,具体看下面的代码:

package com.test.mvp.mvpdemo.mvp.v1.presenter;

import com.test.mvp.mvpdemo.mvp.v1.MainContract;
import com.test.mvp.mvpdemo.mvp.v1.model.DataModel;

import java.io.IOException;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Response;

/**
* presenter 层,承担业务逻辑处理,数据源处理等
*/
public class MainPresenter implements MainContract.IMainPresenter {

private MainContract.IMainModel mModel;
private MainContract.IMainView mView;

public MainPresenter(MainContract.IMainView view) {
this.mView = view;
mModel = new DataModel();
}

@Override
public void handlerData() {
if (mView != null) {
mView.showDialog();
}
/**
* 发起请求,获?#27809;?#35843;数据
*/
mModel.requestBaidu(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}

@Override
public void onResponse(Call call, Response response) throws IOException {
String content = response.body().string();
if (mView != null) {
mView.succes(content);
}
}
});
}
}

这里的处理数据我就不做?#24471;?#20102;,注意一点,这里是请求网络数据会有延时,我们 Presenter 层持有了 View 层的引用,也就是 Activity 的引用,在网络堵塞的情况下,用户在打开 Activity ?#33268;?#19978;给关闭了,这时 View 相当于被摧毁了,如果不进?#20449;?#26029; View 引用是否为空,当数据在延迟几秒后返回时,?#31361;?#36896;成空指针异常,所以每次都要进?#20449;?#26029;才合理。

最后,在 Presenter 层处理完了数据,就要将数据传达给 View 层显示,所以要调用 View 接口的抽象方法,把数据参数传递过去,那么 View 在收到数据后,不需要多余的逻辑操作,直接显示就好了。那么 View(MainActivity)的代码应该做如下修?#27169;?/p>

(4)View 层:

package com.test.mvp.mvpdemo.mvp.v1.view;

import android.app.ProgressDialog;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
import android.widget.Toast;

import com.test.mvp.mvpdemo.R;
import com.test.mvp.mvpdemo.mvp.v1.MainContract;
import com.test.mvp.mvpdemo.mvp.v1.presenter.MainPresenter;

/**
* MVP 的写法,Version 1: 基础写法
*/
public class MainActivity extends AppCompatActivity implements MainContract.IMainView {

private TextView tv;

private MainPresenter mPresenter;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

initViews();

mPresenter = new MainPresenter(this);
mPresenter.handlerData();
}

private void initViews() {
tv = findViewById(R.id.tv);
}

@Override
public void showDialog() {
ProgressDialog dialog = new ProgressDialog(this);
dialog.show();
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
dialog.dismiss();
}
}, 1500);
}

@Override
public void succes(String content) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "" + content, Toast.LENGTH_SHORT).show();
tv.setText(content);
}
});
}
}

使用 MVP 架构,我们可以做到 Model 层和 Presenter 层的复用,如果不同的 Veiw 层需要相同的数据,那么就无需修改 Model 层和 Presenter 层,直接实现接口就可以了。

到此为止,一个从网络获取数据并显示的案例就被我们改写为 MVP 架构了,这是最基础的 MVP 的入门版本,其中精要的就是interface 接口的使用,而接口的用法也是 Java 的基础,所以本篇文章内容应该不难理解。而 MVP 架构也无需引入哪些库、框架啊等等,它更是一种编程架构、编程思想,并且现在也非常流行,所以我们一定要搞定它。


?#20381;?#35828;两句
您需要登录后才可以评论 登录 | 立即注册
facelist
所有评论(1)
gunner880101 2019-8-12 11:19
  
回复
领先的中文移动开发者社区
18620764416
7*24全天服务
意见反馈:[email protected]

扫一扫关注我们

Powered by Discuz! X3.2© 2001-2019 Comsenz Inc.( 粤ICP备15117877号 )

招财童子彩金