Android系统架构之MVP使用
在开始这篇博客之前,我们先来探讨几个问题:
- 为什么要用MVP?
传统开发中,View(包括Activity、Fragment及其子类)作为APP功能的基本单元完成了过多任务,包括UI绘制与刷新、页面逻辑、动画渲染,这就导致一些复杂的页面动辄数千行,这就导致维护和升级的成本越来越高,代码牵一发而动全身。为了解决这个问题,我们提出了MVP的思想。 - MVP如何解决这个View代码过于庞大的问题?
MVP全称是Model,View,Presenter,核心思想就是将整个应用分为三层。 - View层:视图层,包含界面相关功能,主要提供与用户的交互,包括实现设计师要求的界面以及动画的加载等交互效果。View层一般会持有Presenter的引用,通过这个引用将一些业务逻辑委托给Presenter完成,或者也可以通过依赖注入(如Dagger)的方式获得Presenter的实例进而实现逻辑操作的委托;
- Presenter层:逻辑控制层,这层是从View层中通过抽离控制逻辑部分形成的,充当View层和Model层交互的中间人,比如用户在屏幕上点击了下载按钮,下载一张图片并显示出来,这时,View层会发出网络请求,而Presenter层会接受View层的请求,并分发给对应的Model处理,最终结果通过Presenter层反馈给View层并显示出来;
- Model层:封装各种数据来源,例如远程网络数据,本地数据库数据,对Presenter层提供简单的接口。
- MVP与MVC的区别
MVP作为经典MVC的延伸,与MVC最大的区别在于:MVP中的View层和Model层没有直接通信,是通过Presenter这个中间人进行的,其交互都是基于接口的,并且通常情况下,Presenter和View是一对一的,特殊情况下可能存在一个View对应多个Presenter的情况;而MVC中View和Model是直接通信的,Control本身是基于行为的,可以被多个View共享,比如网络下载行为。
OK,现在我们正式开始实现我们自己的MVP框架。
一、 创建callback接口,Callback 接口是Model层给Presenter层反馈请求信息的传递载体,所以需要在Callback中定义数据请求的各种反馈状态:
public interface MvpCallback {
/**
* 数据请求成功
* @param data 请求到的数据
*/
void onSuccess(String data);
/**
* 使用网络API接口请求方式时,虽然已经请求成功但是由于某些原因无法正常返回数据。
* @param msg:无法返回数据的原因
*/
void onFailure(String msg);
/**
* 请求数据失败,指在请求网络API接口请求方式时,出现无法联网、缺少权限,内存泄露等原因导致无法连接到请求数据源。
*/
void onError();
/**
* 当请求数据结束时,无论请求结果是成功,失败或是抛出异常都会执行此方法给用户做处理,通常做网络请求时可以在此处隐藏“正在加载”的等待控件
*/
void onComplete();
}
二、 创建Model类,Model 类中定了具体的网络请求操作。为模拟真实的网络请求,利用postDelayed方法模拟耗时操作,通过判断请求参数反馈不同的请求状态:
public class MvpModel {
/**
* 获取网络接口数据
* @param param 请求参数
* @param callback 数据回调接口
*/
public static void getNetData(final String param, final MvpCallback callback){
// 利用postDelayed方法模拟网络请求数据的耗时操作
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
switch (param){
case "normal":
callback.onSuccess("根据参数"+param+"的请求网络数据成功");
break;
case "failure":
callback.onFailure("请求失败:参数有误");
break;
case "error":
callback.onError();
break;
}
callback.onComplete();
}
},2000);
}
}
三、 创建View接口,View接口是Activity与Presenter层的中间层,它的作用是根据具体业务的需要,为Presenter提供调用Activity中具体UI逻辑操作的方法。
public interface MvpView {
/**
* 显示正在加载进度框
*/
void showLoading();
/**
* 隐藏正在加载进度框
*/
void hideLoading();
/**
* 当数据请求成功后,调用此接口显示数据
* @param data 数据源
*/
void showData(String data);
/**
* 当数据请求失败后,调用此接口提示
* @param msg 失败原因
*/
void showFailureMessage(String msg);
/**
* 当数据请求异常,调用此接口提示
*/
void showErrorMessage();
}
四、 创建Presenter类,Presenter类是具体的逻辑业务处理类,该类为纯Java类,不包含任何Android API,负责请求数据,并对数据请求的反馈进行处理。Presenter类的构造方法中有一个View接口的参数,是为了能够通过View接口通知Activity进行更新界面等操作。
public class MvpPresenter {
// View接口
private MvpView mView;
public MvpPresenter(MvpView view){
this.mView = view;
}
/**
* 获取网络数据
* @param params 参数
*/
public void getData(String params){
//显示正在加载进度条
mView.showLoading();
// 调用Model请求数据
MvpModel.getNetData(params, new MvpCallback() {
@Override
public void onSuccess(String data) {
//调用view接口显示数据
mView.showData(data);
}
@Override
public void onFailure(String msg) {
//调用view接口提示失败信息
mView.showFailureMessage(msg);
}
@Override
public void onError() {
//调用view接口提示请求异常
mView.showErrorMessage();
}
@Override
public void onComplete() {
// 隐藏正在加载进度条
mView.hideLoading();
}
});
}
}
五、 页面布局,只需要包括三个按钮即可,就是对应于访问成功,失败,异常三种情况的按钮
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
android:orientation="vertical"
tools:context="com.jessewu.mvpdemo.MainActivity">
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:text="点击按钮获取网络数据"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="获取数据【成功】"
android:onClick="getData"
/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="获取数据【失败】"
android:onClick="getDataForFailure"
/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="获取数据【异常】"
android:onClick="getDataForError"
/>
六、创建MainActivity,通过按钮点击进行调用
public class MainActivity extends AppCompatActivity implements MvpView {
//进度条
ProgressDialog progressDialog;
TextView text;
MvpPresenter presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text = (TextView)findViewById(R.id.text);
// 初始化进度条
progressDialog = new ProgressDialog(this);
progressDialog.setCancelable(false);
progressDialog.setMessage("正在加载数据");
//初始化Presenter
presenter = new MvpPresenter(this);
}
// button 点击事件调用方法
public void getData(View view){
presenter.getData("normal");
}
// button 点击事件调用方法
public void getDataForFailure(View view){
presenter.getData("failure");
}
// button 点击事件调用方法
public void getDataForError(View view){
presenter.getData("error");
}
@Override
public void showLoading() {
if (!progressDialog.isShowing()) {
progressDialog.show();
}
}
@Override
public void hideLoading() {
if (progressDialog.isShowing()) {
progressDialog.dismiss();
}
}
@Override
public void showData(String data) {
text.setText(data);
}
@Override
public void showFailureMessage(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
text.setText(msg);
}
@Override
public void showErrorMessage() {
Toast.makeText(this, "网络请求数据出现异常", Toast.LENGTH_SHORT).show();
text.setText("网络请求数据出现异常");
}
}
分析一下代码,这里的View对应的就是我代码中的MainActivity,Model对应我代码中的MvpModel类,Presenter对应我代码中的MvpPresenter类。代码原理就是,在MainActivity中持有一个MvpPresenter类的实例presenter,用户与MainActivity进行交互(准确的说应该是与MainActivity对应的页面交互),用户的网络请求将会委托给presenter中的getData()方法,而在getData()中再将网络数据请求操作委托给MvpModel中的静态方法getNetData()进行实现,因为是公有静态,所以这里不需要持有Model的引用,同时通过View接口实现对页面的更新,这个View接口是在MainActivity中实现的。
从整体上看,MVP其实首先就是通过面向对象技术将一个页面及其完整的交互逻辑封装为三部分,分别是Model, View和Presenter, 三者之间通过接口进行交互,Model与Present二者完全解耦,二者通过Presenter实现交互,而在Presenter中在通过一种多态实现页面刷新接口中方法,这个方法的实现在View中。
作者:kuai_j
来源:CSDN
原文:https://blog.csdn.net/qq_41613281/article/details/90449133
版权声明:本文为博主原创文章,转载请附上博文链接!