android MVP

转载自 http://www.jcodecraeer.com/a/anzhuokaifa/2017/1020/8625.html?1508484926

Android MVP架构搭建

JesseWuuu / 文 发表于2017-10-20 14:00 第148452次阅读 架构,android,mvp,android mvp,框架

目录

  • 引言
  • 为什么用MVP架构
  • MVP理论知识
  • 乞丐版MVP架构模式的代码实现
  • MVP中的代码复用场景
  • 平民版MVP架构 - base层顶级父类
  • Fragment怎么办
  • 时尚版MVP架构 - Model层的单独优化

引言

记得第一次接触MVP开发是上大学的时候,当时看了数十篇关于MVP的文章,这里不得不吐槽一下国内技术帖子的质量真是参次不齐啊。看完之后一直懵懵懂懂的,总觉有几处关键的地方没搞清但是文章中却一带而过了,比如:

  • 关于如何在Activity中高效的复用Presenter和View;
  • Mode层定义到什么程度才算是比较理想的解耦;
  • Model层与Presenter层如何比较优雅的相互通信。

抱着这些问题,我自己摸索着构建出了一套个性化风格MVP架构,使用过程中也优化了几次,如今一年多过去了再看这套架构也就算是个能用吧,所以决定新的架构优化。

本文讲述了MVP的核心概念和如何从最初的乞丐版MVP架构一步步升级到平民版MVP架构,时尚版MVP架构,以及即将开始更新的旗舰版MVP架构,为了保证思路清晰,文中包含大量代码与文字,跟着文中的例子便可写出一个完整的MVP架构。

为什么用MVP架构

其实我们日常开发中的Activity,Fragment和XML界面就相当于是一个 MVC 的架构模式,Activity中不仅要处理各种 UI 操作还要请求数据以及解析。

这种开发方式的缺点就是业务量大的时候一个Activity 文件分分钟飙到上千行代码,想要改一处业务逻辑光是去找就要费半天劲,而且有点地方逻辑处理是一样的无奈是不同的 Activity 就没办法很好的写成通用方法。

那 MVP 为啥好用呢?

MVP 模式将Activity 中的业务逻辑全部分离出来,让Activity 只做 UI 逻辑的处理,所有跟Android API无关的业务逻辑由 Presenter 层来完成。

将业务处理分离出来后最明显的好处就是管理方便,但是缺点就是增加了代码量。

MVP 理论知识

在MVP 架构中跟MVC类似的是同样也分为三层。

Activity 和Fragment 视为View层,负责处理 UI。

Presenter 为业务处理层,既能调用UI逻辑,又能请求数据,该层为纯Java类,不涉及任何Android API。

Model 层中包含着具体的数据请求,数据源。

三层之间调用顺序为view->presenter->model,为了调用安全着想不可反向调用!不可跨级调用!

那Model 层如何反馈给Presenter 层的呢?Presenter 又是如何操控View 层呢?看图!

MVP架构调用关系

上图中说明了低层的不会直接给上一层做反馈,而是通过 View 、 Callback 为上级做出了反馈,这样就解决了请求数据与更新界面的异步操作。上图中 View 和 Callback 都是以接口的形式存在的,其中 View 是经典 MVP 架构中定义的,Callback 是我自己加的。

View 中定义了 Activity 的具体操作,主要是些将请求到的数据在界面中更新之类的。

Callback 中定义了请求数据时反馈的各种状态:成功、失败、异常等。

乞丐版MVP架构模式的代码实现

下面我们用 MVP 模式构造一个简易模拟请求网络的小程序。效果图如下:

成功加载到数据

加载数据失败

数据获取异常

因为是模拟网络数据请求,所以有三个请求数据的按钮分别对应成功、失败、异常三种不同的反馈状态。

下面是Demo中的Java文件目录:

Java文件目录

Callback接口

Callback 接口是Model层给Presenter层反馈请求信息的传递载体,所以需要在Callback中定义数据请求的各种反馈状态:

 
  1. public interface MvpCallback {
  2.  
  3. /**
  4. * 数据请求成功
  5. * @param data 请求到的数据
  6. */
  7. void onSuccess(String data);
  8.  
  9. /**
  10. * 使用网络API接口请求方式时,虽然已经请求成功但是由
  11. * 于{@code msg}的原因无法正常返回数据。
  12. */
  13. void onFailure(String msg);
  14.  
  15. /**
  16. * 请求数据失败,指在请求网络API接口请求方式时,出现无法联网、
  17. * 缺少权限,内存泄露等原因导致无法连接到请求数据源。
  18. */
  19. void onError();
  20.  
  21. /**
  22. * 当请求数据结束时,无论请求结果是成功,失败或是抛出异常都会执行此方法给用户做处理,通常做网络
  23. * 请求时可以在此处隐藏“正在加载”的等待控件
  24. */
  25. void onComplete();
  26.  
  27. }

Model 类

Model 类中定了具体的网络请求操作。为模拟真实的网络请求,利用postDelayed方法模拟耗时操作,通过判断请求参数反馈不同的请求状态:

 
  1. public class MvpModel {
  2.  
  3. /**
  4. * 获取网络接口数据
  5. * @param param 请求参数
  6. * @param callback 数据回调接口
  7. */
  8. public static void getNetData(final String param, final MvpCallback callback){
  9.  
  10. // 利用postDelayed方法模拟网络请求数据的耗时操作
  11. new Handler().postDelayed(new Runnable() {
  12. @Override
  13. public void run() {
  14. switch (param){
  15.  
  16. case "normal":
  17. callback.onSuccess("根据参数"+param+"的请求网络数据成功");
  18. break;
  19.  
  20. case "failure":
  21. callback.onFailure("请求失败:参数有误");
  22. break;
  23.  
  24. case "error":
  25. callback.onError();
  26. break;
  27. }
  28. callback.onComplete();
  29. }
  30.  
  31. },2000);
  32. }
  33.  
  34. }

View 接口

View接口是Activity与Presenter层的中间层,它的作用是根据具体业务的需要,为Presenter提供调用Activity中具体UI逻辑操作的方法。

 
  1. public interface MvpView {
  2.  
  3. /**
  4. * 显示正在加载进度框
  5. */
  6. void showLoading();
  7.  
  8. /**
  9. * 隐藏正在加载进度框
  10. */
  11. void hideLoading();
  12.  
  13. /**
  14. * 当数据请求成功后,调用此接口显示数据
  15. * @param data 数据源
  16. */
  17. void showData(String data);
  18.  
  19. /**
  20. * 当数据请求失败后,调用此接口提示
  21. * @param msg 失败原因
  22. */
  23. void showFailureMessage(String msg);
  24.  
  25. /**
  26. * 当数据请求异常,调用此接口提示
  27. */
  28. void showErrorMessage();
  29.  
  30. }

Presenter类

Presenter类是具体的逻辑业务处理类,该类为纯Java类,不包含任何Android API,负责请求数据,并对数据请求的反馈进行处理。

Presenter类的构造方法中有一个View接口的参数,是为了能够通过View接口通知Activity进行更新界面等操作。

 
  1. public class MvpPresenter {
  2.  
  3. // View接口
  4. private MvpView mView;
  5.  
  6. public MvpPresenter(MvpView view){
  7. this.mView = view;
  8. }
  9.  
  10. /**
  11. * 获取网络数据
  12. * @param params 参数
  13. */
  14. public void getData(String params){
  15.  
  16. //显示正在加载进度条
  17. mView.showLoading();
  18. // 调用Model请求数据
  19. MvpModel.getNetData(params, new MvpCallback() {
  20. @Override
  21. public void onSuccess(String data) {
  22. //调用view接口显示数据
  23. mView.showData(data);
  24. }
  25.  
  26. @Override
  27. public void onFailure(String msg) {
  28. //调用view接口提示失败信息
  29. mView.showFailureMessage(msg);
  30. }
  31.  
  32. @Override
  33. public void onError() {
  34. //调用view接口提示请求异常
  35. mView.showErrorMessage();
  36. }
  37.  
  38. @Override
  39. public void onComplete() {
  40. // 隐藏正在加载进度条
  41. mView.hideLoading();
  42. }
  43. });
  44. }
  45.  
  46. }

xml布局文件

没什么好说的,直接上代码:

 
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:tools="http://schemas.android.com/tools"
  4. android:layout_width="match_parent"
  5. android:layout_height="match_parent"
  6. android:padding="16dp"
  7. android:orientation="vertical"
  8. tools:context="com.jessewu.mvpdemo.MainActivity">
  9.  
  10. <TextView
  11. android:id="@+id/text"
  12. android:layout_width="match_parent"
  13. android:layout_height="0dp"
  14. android:layout_weight="1"
  15. android:text="点击按钮获取网络数据"/>
  16.  
  17. <Button
  18. android:layout_width="match_parent"
  19. android:layout_height="wrap_content"
  20. android:text="获取数据【成功】"
  21. android:onClick="getData"
  22. />
  23.  
  24. <Button
  25. android:layout_width="match_parent"
  26. android:layout_height="wrap_content"
  27. android:text="获取数据【失败】"
  28. android:onClick="getDataForFailure"
  29. />
  30.  
  31. <Button
  32. android:layout_width="match_parent"
  33. android:layout_height="wrap_content"
  34. android:text="获取数据【异常】"
  35. android:onClick="getDataForError"
  36. />
  37.  
  38. </LinearLayout>

Activity

在Activity代码中需要强调的是如果想要调用Presenter就要先实现Presenter需要的对应的View接口。

 
  1. public class MainActivity extends AppCompatActivity implements MvpView {
  2.  
  3. //进度条
  4. ProgressDialog progressDialog;
  5. TextView text;
  6.  
  7. MvpPresenter presenter;
  8.  
  9. @Override
  10. protected void onCreate(Bundle savedInstanceState) {
  11. super.onCreate(savedInstanceState);
  12. setContentView(R.layout.activity_main);
  13.  
  14. text = (TextView)findViewById(R.id.text);
  15.  
  16. // 初始化进度条
  17. progressDialog = new ProgressDialog(this);
  18. progressDialog.setCancelable(false);
  19. progressDialog.setMessage("正在加载数据");
  20.  
  21. //初始化Presenter
  22. presenter = new MvpPresenter(this);
  23.  
  24. }
  25.  
  26. // button 点击事件调用方法
  27. public void getData(View view){
  28. presenter.getData("normal");
  29. }
  30.  
  31. // button 点击事件调用方法
  32. public void getDataForFailure(View view){
  33. presenter.getData("failure");
  34. }
  35.  
  36. // button 点击事件调用方法
  37. public void getDataForError(View view){
  38. presenter.getData("error");
  39. }
  40.  
  41. @Override
  42. public void showLoading() {
  43. if (!progressDialog.isShowing()) {
  44. progressDialog.show();
  45. }
  46. }
  47.  
  48. @Override
  49. public void hideLoading() {
  50. if (progressDialog.isShowing()) {
  51. progressDialog.dismiss();
  52. }
  53. }
  54.  
  55. @Override
  56. public void showData(String data) {
  57. text.setText(data);
  58. }
  59.  
  60. @Override
  61. public void showFailureMessage(String msg) {
  62. Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
  63. text.setText(msg);
  64. }
  65.  
  66. @Override
  67. public void showErrorMessage() {
  68. Toast.makeText(this, "网络请求数据出现异常", Toast.LENGTH_SHORT).show();
  69. text.setText("网络请求数据出现异常");
  70. }
  71. }

至此,已经完整的实现了一个简易的MVP架构。

注意!以上代码中还存在很大的问题,不能用到实际开发中,下面会讨论目前存在的问题以及如何优化。

MVP中的代码复用场景

因为上节中乞丐版MVP Demo的代码只实现了一个Activity的请求操作,容易出现一个概念的混淆:

每个Activity都需要有与它对应的一套MVP(Model,View,Presenter)吗?

答案肯定是否定的!

首先不需要数据请求的Activity当然就同样不需要MVP辅助。与其他Activity中存在相同逻辑的Activity,就不需要重复生成对应的MVP。但是这个存在相同逻辑的定义,不同的场景有不同的说法:

需求.jpg

场景1:业务逻辑完全相同

场景1中Activity A和Activity C都只有一个“买东西”的逻辑,属于典型的逻辑相同,所以Activity C就可以直接用Activity A写好的MVP无需再做任何处理。

场景2、3:包含部分相同业务逻辑

场景2和场景3的逻辑类似,都属于一个业务逻辑中包含另外一个可以单独存在的业务逻辑,这种情况采用继承的方法即可:

继承关系.png

场景4

场景4中Activity C想要同时调用独立服务于Activity A 和 Activity B的业务逻辑,只需要将两个业务逻辑对应的Presenter分别实例化并调用业务方法即可:

 
  1. private PresenterA presenterA;
  2. private PresenterB presenterB;
  3.  
  4. ...
  5. ...
  6.  
  7. private void getData(){
  8. presenterA.getData();
  9. presenterB.getData();
  10. }

不要忘了实现两个Presenter对应的View:

 
  1. public class ActivityC extends Activity implements ViewA,ViewB{
  2. ...
  3. }

场景5

场景5属于场景3与场景4的结合体,同样需要先把A和B的业务逻辑拆分开,然后同时调用,这里就不举例子了。

总结

通过上面一揽子场景的分析,得出的第一个结论就是MVP的结构太过于繁重,所以为了避免多写重复代码和日后需要进行无意义的修改,在开发前一定要设计好逻辑调用图,这样才能事半功倍。

对于上面经典的通过业务逻辑继承实现包含重复逻辑的方法,其实也可以在一个Presenter中写好完整的逻辑方法,对于不同的Activity需要哪个业务逻辑方法就调用哪个,这样岂不就简单多了。但是从架构设计角度看这种做法是不严谨的,可能存在漏洞,所以为保持软件架构的健壮还是不要偷懒的好。

平民版MVP架构 - base层顶级父类

之前说过乞丐版MVP架构模式中还存在很多问题不能应用到实际的开发中,大概存在的问题有:

  • 构架存在漏洞
  • 代码冗余量大
  • 通用性差

针对这些问题我们需要进一步优化,单车变摩托,升级为可以在实际开发中使用的平民版MVP架构。

调用View可能引发的空指针异常

举一个例子,在上述乞丐版MVP架构中的应用请求网络数据时需要等待后台反馈数据后更新界面,但是在请求过程中当前Activity突然因为某种原因被销毁,Presenter收到后台反馈并调用View接口处理UI逻辑时由于Activity已经被销毁,就会引发空指针异常。

想要避免这种情况的发生就需要每次调用View前都知道宿主Activity的生命状态。

之前是在Presenter的构造方法中得到View接口的引用,现在我们需要修改Presenter引用View接口的方式让View接口与宿主Activity共存亡:

 
  1. public class MvpPresenter {
  2.  
  3. // View接口
  4. private MvpView mView;
  5.  
  6. public MvpPresenter(){
  7. //构造方法中不再需要View参数
  8. }
  9.  
  10.  /**
  11. * 绑定view,一般在初始化中调用该方法
  12. */
  13. public void attachView(MvpView mvpView) {
  14. this.mView= mvpView;
  15. }
  16.  
  17. /**
  18. * 断开view,一般在onDestroy中调用
  19. */
  20. public void detachView() {
  21. this.mView= null;
  22. }
  23.  
  24. /**
  25. * 是否与View建立连接
  26. * 每次调用业务请求的时候都要出先调用方法检查是否与View建立连接
  27. */
  28. public boolean isViewAttached(){
  29. return mView!= null;
  30. }
  31.  
  32. /**
  33. * 获取网络数据
  34. * @param params 参数
  35. */
  36. public void getData(String params){
  37.  
  38. //显示正在加载进度条
  39. mView.showLoading();
  40. // 调用Model请求数据
  41. MvpModel.getNetData(params, new MvpCallback() {
  42. @Override
  43. public void onSuccess(String data) {
  44. //调用view接口显示数据
  45. if(isViewAttached()){
  46. mView.showData(data);
  47. }
  48.  
  49. }
  50.  
  51. @Override
  52. public void onFailure(String msg) {
  53. //调用view接口提示失败信息
  54. if(isViewAttached()){
  55. mView.showFailureMessage(msg);
  56. }
  57. }
  58.  
  59. @Override
  60. public void onError() {
  61. //调用view接口提示请求异常
  62. if(isViewAttached()){
  63. mView.showErrorMessage();
  64. }
  65. }
  66.  
  67. @Override
  68. public void onComplete() {
  69. // 隐藏正在加载进度条
  70. if(isViewAttached()){
  71. mView.hideLoading();
  72. }
  73. }
  74. });
  75. }
  76.  
  77. }

上面Presenter代码中比之前增加了三个方法:

  • attachView() 绑定View引用。
  • detachView 断开View引用。
  • isViewAttached() 判断View引用是否存在。

其中attachView()detachView()是为Activity准备的,isViewAttached()作用是Presenter内部每次调用View接口中的方法是判断View 的引用是否存在。

把绑定View的方法写到Activity的生命周期中:

 
  1. public class MainActivity extends Activity implements MvpView{
  2.  
  3. MvpPresenter presenter;
  4.  
  5. @Override
  6. protected void onCreate(Bundle savedInstanceState) {
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.activity_main);
  9.  
  10. //初始化Presenter
  11. presenter = new MvpPresenter();
  12. // 绑定View引用
  13. presenter.attachView(this);
  14. }
  15.  
  16. @Override
  17. protected void onDestroy() {
  18. super.onDestroy();
  19. // 断开View引用
  20. presenter.detachView();
  21. }
  22.  
  23. }

结合Activity构建base层

写到这里,相信大多数人都会惊讶于MVP模式代码量的巨大,冗余代码实在太多,所以接下需要为MVP中所有单元都设计一个顶级父类来减少重复的冗余代码。同样的道理,我们也为Activity设计一个父类方便与MVP架构更完美的结合。最后将所有父类单独分到一个base包中供外界继承调用。

Callback

在乞丐版中Callback接口中的onSuccess()方法需要根据请求数据类型的不同设置为不同类型的参数,所以每当有新的数据类型都需要新建一个Callback,解决方法是引入泛型的概念,用调用者去定义具体想要接收的数据类型:

 
  1. public interface Callback<T> {
  2.  
  3. /**
  4. * 数据请求成功
  5. * @param data 请求到的数据
  6. */
  7. void onSuccess(T data);
  8.  
  9. /**
  10. * 使用网络API接口请求方式时,虽然已经请求成功但是由
  11. * 于{@code msg}的原因无法正常返回数据。
  12. */
  13. void onFailure(String msg);
  14.  
  15. /**
  16. * 请求数据失败,指在请求网络API接口请求方式时,出现无法联网、
  17. * 缺少权限,内存泄露等原因导致无法连接到请求数据源。
  18. */
  19. void onError();
  20.  
  21. /**
  22. * 当请求数据结束时,无论请求结果是成功,失败或是抛出异常都会执行此方法给用户做处理,通常做网络
  23. * 请求时可以在此处隐藏“正在加载”的等待控件
  24. */
  25. void onComplete();
  26.  
  27. }

BaseView

View接口中定义Activity的UI逻辑。因为有很多方法几乎在每个Activity中都会用到,例如显示和隐藏正在加载进度条,显示Toast提示等,索性将这些方法变成通用的:

 
  1. public interface BaseView {
  2.  
  3. /**
  4. * 显示正在加载view
  5. */
  6. void showLoading();
  7.  
  8. /**
  9. * 关闭正在加载view
  10. */
  11. void hideLoading();
  12.  
  13. /**
  14. * 显示提示
  15. * @param msg
  16. */
  17. void showToast(String msg);
  18.  
  19. /**
  20. * 显示请求错误提示
  21. */
  22. void showErr();
  23.  
  24. /**
  25. * 获取上下文
  26. * @return 上下文
  27. */
  28. Context getContext();
  29. }

BasePresenter

Presenter中可共用的代码就是对View引用的方法了,值得注意的是,上面已经定义好了BaseView,所以我们希望Presenter中持有的View都是BaseView的子类,这里同样需要泛型来约束:

 
  1. public class BasePresenter<V extends IBaseView> {
  2.  
  3. /**
  4. * 绑定的view
  5. */
  6. private V mvpView;
  7.  
  8. /**
  9. * 绑定view,一般在初始化中调用该方法
  10. */
  11. @Override
  12. public void attachView(V mvpView) {
  13. this.mvpView = mvpView;
  14. }
  15.  
  16. /**
  17. * 断开view,一般在onDestroy中调用
  18. */
  19. @Override
  20. public void detachView() {
  21. this.mvpView = null;
  22. }
  23.  
  24. /**
  25. * 是否与View建立连接
  26. * 每次调用业务请求的时候都要出先调用方法检查是否与View建立连接
  27. */
  28. public boolean isViewAttached(){
  29. return mvpView != null;
  30. }
  31.  
  32. /**
  33. * 获取连接的view
  34. */
  35. public V getView(){
  36. return mvpView;
  37. }

BaseActivity

BaseActivity主要是负责实现 BaseView 中通用的UI逻辑方法,如此这些通用的方法就不用每个Activity都要去实现一遍了。

 
  1. public abstract class BaseActivity extends Activity implements IBaseView {
  2.  
  3. private ProgressDialog mProgressDialog;
  4.  
  5. @Override
  6. protected void onCreate(Bundle savedInstanceState) {
  7. super.onCreate(savedInstanceState);
  8. mProgressDialog = new ProgressDialog(this);
  9. mProgressDialog.setCancelable(false);
  10. }
  11.  
  12. @Override
  13. public void showLoading() {
  14. if (!mProgressDialog.isShowing()) {
  15. mProgressDialog.show();
  16. }
  17. }
  18.  
  19. @Override
  20. public void hideLoading() {
  21. if (mProgressDialog.isShowing()) {
  22. mProgressDialog.dismiss();
  23. }
  24. }
  25.  
  26. @Override
  27. public void showToast(String msg) {
  28. Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
  29. }
  30.  
  31. @Override
  32. public void showErr() {
  33. showToast(getResources().getString(R.string.api_error_msg));
  34. }
  35.  
  36. @Override
  37. public Context getContext() {
  38. return BaseActivity.this;
  39. }

平民版MVP架构代码实现

封装好了base层我们的平民版MVP架构就完成了,下面再来实现一遍之前用乞丐版MVP实现的应用。

Model

 
  1. public class MvpModel {
  2.  
  3. /**
  4. * 获取网络接口数据
  5. * @param param 请求参数
  6. * @param callback 数据回调接口
  7. */
  8. public static void getNetData(final String param, final MvpCallback<String> callback){
  9.  
  10. // 利用postDelayed方法模拟网络请求数据的耗时操作
  11. new Handler().postDelayed(new Runnable() {
  12. @Override
  13. public void run() {
  14. switch (param){
  15.  
  16. case "normal":
  17. callback.onSuccess("根据参数"+param+"的请求网络数据成功");
  18. break;
  19.  
  20. case "failure":
  21. callback.onFailure("请求失败:参数有误");
  22. break;
  23.  
  24. case "error":
  25. callback.onError();
  26. break;
  27. }
  28. callback.onComplete();
  29. }
  30.  
  31. },2000);
  32. }
  33.  
  34. }

View 接口

 
  1. public interface MvpView extends BaseView{
  2. /**
  3. * 当数据请求成功后,调用此接口显示数据
  4. * @param data 数据源
  5. */
  6. void showData(String data);
  7.  
  8. }

Presenter类

 
  1. public class MvpPresenter extends BasePresenter<MvpView > {
  2.  
  3. /**
  4. * 获取网络数据
  5. * @param params 参数
  6. */
  7. public void getData(String params){
  8.  
  9. if (!isViewAttached()){
  10. //如果没有View引用就不加载数据
  11. return;
  12. }
  13.  
  14. //显示正在加载进度条
  15. getView().showLoading();
  16.  
  17. // 调用Model请求数据
  18. MvpModel.getNetData(params, new MvpCallback()<String> {
  19. @Override
  20. public void onSuccess(String data) {
  21. //调用view接口显示数据
  22. if(isViewAttached()){
  23. getView().showData(data);
  24. }
  25. }
  26.  
  27. @Override
  28. public void onFailure(String msg) {
  29. //调用view接口提示失败信息
  30. if(isViewAttached()){
  31. getView().showToast(msg);
  32. }
  33. }
  34.  
  35. @Override
  36. public void onError() {
  37. //调用view接口提示请求异常
  38. if(isViewAttached()){
  39. getView().showErr();
  40. }
  41. }
  42.  
  43. @Override
  44. public void onComplete() {
  45. // 隐藏正在加载进度条
  46. if(isViewAttached()){
  47. getView().hideLoading();
  48. }
  49. }
  50. });
  51. }
  52.  
  53. }

Activity

 
  1. public class MainActivity extends BaseActivity implements MvpView {
  2.  
  3. TextView text;
  4.  
  5. MvpPresenter presenter;
  6.  
  7. @Override
  8. protected void onCreate(Bundle savedInstanceState) {
  9. super.onCreate(savedInstanceState);
  10. setContentView(R.layout.activity_main);
  11.  
  12. text = (TextView)findViewById(R.id.text);
  13.  
  14. //初始化Presenter
  15. presenter = new MvpPresenter();
  16. presenter.attachView(this);
  17. }
  18.  
  19. @Override
  20. protected void onDestroy() {
  21. super.onDestroy();
  22. //断开View引用
  23. presenter.detachView();
  24. }
  25.  
  26. @Override
  27. public void showData(String data) {
  28. text.setText(data);
  29. }
  30.  
  31. // button 点击事件调用方法
  32. public void getData(View view){
  33. presenter.getData("normal");
  34. }
  35.  
  36. // button 点击事件调用方法
  37. public void getDataForFailure(View view){
  38. presenter.getData("failure");
  39. }
  40.  
  41. // button 点击事件调用方法
  42. public void getDataForError(View view){
  43. presenter.getData("error");
  44. }
  45. }

Fragment怎么办?

日常开发中,并不是所有的UI处理都在Activity中进行,Fragment也是其中很重要的一员,那么如何将Fragment结合到MVP中呢?

实现BaseFragement做法跟BaseActivity很类似,需要注意一下Fragement与宿主Activity的链接情况就可以。

 
  1. public abstract class BaseFragment extends Fragment implements BaseView {
  2.  
  3. public abstract int getContentViewId();
  4.  
  5. protected abstract void initAllMembersView(Bundle savedInstanceState);
  6.  
  7. protected Context mContext;
  8. protected View mRootView;
  9.  
  10. @Nullable
  11. @Override
  12. public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
  13. mRootView = inflater.inflate(getContentViewId(), container, false);
  14. this.mContext = getActivity();
  15. initAllMembersView(savedInstanceState);
  16. return mRootView;
  17. }
  18.  
  19. @Override
  20. public void showLoading() {
  21. checkActivityAttached();
  22. ((BaseFragmentActivity) mContext).showLoading();
  23. }
  24.  
  25. @Override
  26. public void showLoading(String msg) {
  27. checkActivityAttached();
  28. ((BaseFragmentActivity) mContext).showLoading(msg);
  29. }
  30.  
  31. @Override
  32. public void hideLoading() {
  33. checkActivityAttached();
  34. ((BaseFragmentActivity) mContext).hideLoading();
  35. }
  36.  
  37. @Override
  38. public void showToast(String msg) {
  39. checkActivityAttached();
  40. ((BaseFragmentActivity) mContext).showToast(msg);
  41. }
  42.  
  43. @Override
  44. public void showErr() {
  45. checkActivityAttached();
  46. ((BaseFragmentActivity) mContext).showErr();
  47. }
  48.  
  49. protected boolean isAttachedContext(){
  50. return getActivity() != null;
  51. }
  52.  
  53. /**
  54. * 检查activity连接情况
  55. */
  56. public void checkActivityAttached() {
  57. if (getActivity() == null) {
  58. throw new ActivityNotAttachedException();
  59. }
  60. }
  61.  
  62. public static class ActivityNotAttachedException extends RuntimeException {
  63. public ActivityNotAttachedException() {
  64. super("Fragment has disconnected from Activity ! - -.");
  65. }
  66. }
  67.  
  68. }

时尚版MVP架构 - Model层的单独优化

在从乞丐版MVP架构优化成平民版MVP架构的过程中,几乎每个单元都做了很大优化并封装到了base层,但是唯独Model层没什么变化。所以,时尚版MVP架构的优化主要就是对Model层的优化。

Model层相比其他单元来说比较特殊,因为它们更像一个整体,只是单纯的帮上层拿数据而已。再就是MVP的理念是让业务逻辑互相独立,这就导致每个的网络请求也被独立成了单个Model,不光没必要这么做而且找起来贼麻烦,所以时尚版MVP架构中Model层被整体封装成了庞大且独立单一模块。

时尚版MVP架构图

优化之后的Model层是一个庞大而且独立的模块,对外提供统一的请求数据方法与请求规则,这样做的好处有很多:

  • 数据请求单独编写,无需配合上层界面测试。
  • 统一管理,修改方便。
  • 实现不同数据源(NetAAPI,cache,database)的无缝切换。

写到这里本片文章实在是太长了,所以时尚版MVP架构的实现就留到下片文章继续把。

未完待续

下篇会完善时尚版MVP架构,以及最新的旗舰版MVP架构设计,敬请期待~

最后

硬广一波最新开发的RecyclerView通用集合适配器SuperAdapter,它的作用是帮助开发者快速构建RecyclerView的Adapter,并封装了许多常用功能,非常好用。之前有人跟我说已经有好多人做过这个了,我想说的是我很感谢之前做过这些的大佬们,是他们给了我灵感和思路,但是不得不承认目前网上的类似项目都存在问题,实际开发中会有些问题,我在前人的思路上进行了大幅度优化,意图打造一个既好用又实用的工具。

目前这个项目还没有全部完成,还有许多实用的功能可以开发,有兴趣的朋友可以一起搞啊。最后,文章链接传送门:RecyclerView多功能集合适配器:SuperAdapter

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值