MVPMosbyDemo

Introduction: MVP 框架(Mosby)的 Demo 和简单源码分析
More: Author   ReportBugs   
Tags:
MVP-Mosby-

Mosby 官方地址:Mosby

使用

以 MvpLceViewStateFragment 为例,具有 LCE 和 ViewState 两种功能,简单的使用见NewsListFragment.javaNewsListPersenter.java; 简单的新闻列表,模拟耗时网络请求,首先页面后显示 loading 状态,然后显示新闻数据,如果错误可以在 presenter 中调用getView().showError();这是 mosby 实现的 LCE 效果。 然后旋转屏幕,看看会发生什么,效果图: viewstate_gif

可以看到旋转之后数据直接显示了,是没有再去获取数据的,说明 viewstate 是恢复了的,看 log 也能看出来。

分析

先上 uml,这是基础类的类图: base_class

MvpFragment 是所有 mvp*Fragment 的父类,它实现了 BaseMvpDelegateCallback 接口,从名字可以看出,它是一个代理类的回调,后面可以看到这个代理类就是 FragmentMvpDelegate,可以看到 FragmentMvpDelegate 里面都是 Fragment 生命周期的声明,在 MvpFragment 中每个生命周期都是交给这个代理类处理。再说 BaseMvpDelegateCallback,它是供 FragmentMvpDelegate 使用的,它实际上是 view 层必须实现的接口,官方对它的说明是This interface must be implemented by all Fragment or android.view.View that you want to support mosbys mvp,使用 mosby 必须在 Fragment 或 View 实现它,它里面的方法都是 view 层基本的方法,比如createPresentergetMvpView等。到这里可以知道,当 Fragment 生命周期发生变化时,是交给 FragmentMvpDelegate 处理的,再看它的内部,它的构造方法需要传递 delegateCallback 对象也就是 Fragment,内部又多了一个 MvpInternalDelegate 类,从名字可以看出它是内部处理的重要类,在 Fragment 回调 onViewCreated 生命周期时,有如下代码getInternalDelegate().createPresenter(); getInternalDelegate().attachView();MvpInternalDelegate 会先创建 Presenter,然后调用它的 attachView(),MvpInternalDelegate 的 createPresenter 方法:

  void createPresenter() {
    P presenter = delegateCallback.getPresenter();
    if (presenter == null) {
      presenter = delegateCallback.createPresenter();
    }
    if (presenter == null) {
      throw new NullPointerException("Presenter is null! Do you return null in createPresenter()?");
    }
    delegateCallback.setPresenter(presenter);
  }

它就是判断然后创建 Presenter,这里会调用我们在 Fragment 实现的 createPresenter(),presenter 由我们自定义;注意,这里会判断是否为 null,也就是在 Fragment 的 onViewCreated 的时候会检查 createPresenter()是否为空,很容易忘写了报这个错。 attachView()方法:

 void attachView() {
    getPresenter().attachView(delegateCallback.getMvpView());
  }

调用了 presenter 的 attachView 方法,并将 MvpView 传递过去,看下 MvpPresenter 的基本实现类 MvpBasePresenter:

public class MvpBasePresenter<V extends MvpView> implements MvpPresenter<V> {
  private WeakReference<V> viewRef;
  @Override public void attachView(V view) {
    viewRef = new WeakReference<V>(view);
  }
  @Nullable public V getView() {
    return viewRef == null ? null : viewRef.get();
  }
  public boolean isViewAttached() {
    return viewRef != null && viewRef.get() != null;
  }
  @Override public void detachView(boolean retainInstance) {
    if (viewRef != null) {
      viewRef.clear();
      viewRef = null;
    }
  }
}

它内部使用了弱引用保存了 MvpView,防止内存泄漏,attachView 会初始化这个弱引用;它提供了一个有用的方法 getView(),可以在 presenter 里获得 mvpview 对象,由于它是弱引用,注意要做非空判断。 分析到这里,基本的 MvpFragment 流程已经分析完了,整理一下,首先开发者需要继承 MvpView 设计需要操作 UI 的接口,比如 showLoginSucc(),showLoginError(),再继承 MvpBasePresenter,编写需要的功能逻辑,比如 login(username,pwd),这里一般是异步操作,需要在成功失败的回调里调用 getView().showLoginSucc()和 showLoginError(),最后再编写 Fragment,需要继承 MvpFragment,实现前面设计的 MvpView 接口,实现它的抽象方法 createPresenter()返回 presenter,实现 showLoginSucc()/showLoginError()方法操作 ui,设置 button 监听器,调用 getPresenter().login(username,pwd);。运行时的流程,当点击 button 时,会调用 presenter 的 login 方法,成功\失败调用 fragment 的 showLoginSucc()\showLoginError(),修改 ui。 上面的基本 mvp 搞明白了,后面的只是一些扩展。

LCE

MvpLceFragment 内置了 R.id.loadingView/contentView/errorView,xml 里需要显示设置这些 id,必须得有,否则会报错,在 onViewCreated()中会判断,;相应的 MvpLceView 内置了 showLoading/showContent/showError/setData 等方法,其中 showloading/showError 等方法,在 MvpLceFragment 里实现了默认的处理,动画显示和隐藏对应的控件。在使用时,只需要实现相应的方法即可,注意在 presenter 中设置数据和设置 loading 使用getView().showLoading(false);getView().setData(newses);getView().showContent();等方法。

ViewState

这是 viewstate 的类图: viewstate_class

继承自 MvpViewStateFragment,需要设置 setRetainInstance(true); 增加了 ViewState 类,用来保存和恢复 view 的状态数据。它只有一个方法public void apply(V view, boolean retained);用来恢复 mvpview 的状态,它的直接子类有 LceViewState 和 RestorableViewState,前者是具有 lce 恢复功能,常用的是 RetainingLceViewState,后者是具有 parcelable 保存恢复功能。他两的子类很多,其中 AbsParcelableLceViewState 实现了实现了这两个接口,一般常用的有:ArrayListLceViewState 可以存放 list,ParcelableDataLceViewState 可以存放 Parcelable 对象,SerializeableLceViewState 存放 Serializable 对象。 由于增加了 ViewState,相应的 BaseMvpDelegateCallback 扩展成了 BaseMvpViewStateDelegateCallback,增加了对 ViewState 的处理,FragmentMvpViewStateDelegateImpl 扩展了 FragmentMvpDelegateImpl,主要增加了对状态保存,fragment 意外销毁的数据保存,比如旋转屏幕,对onCreate\onActivityCreated\onSaveInstanceState这 3 个方法进行处理:

@Override public void onCreate(Bundle saved) {
    super.onCreate(saved);
    ((MvpViewStateInternalDelegate) getInternalDelegate()).createOrRestoreViewState(saved);
  }
  @Override public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    ((MvpViewStateInternalDelegate) getInternalDelegate()).applyViewState();
  }
  @Override public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    ((MvpViewStateInternalDelegate) getInternalDelegate()).saveViewState(outState);
  }

调用 MvpViewStateInternalDelegate 的相应方法,创建/恢复/保存 ViewState,在createOrRestoreViewState中首先判断用户是否返回了 ViewState,根据是否需要 Parcelable 意外保存做了判断

if (savedInstanceState != null
        && viewStateSupport.getViewState() instanceof RestorableViewState) {
      ViewState restoredViewState =
          ((RestorableViewState) viewStateSupport.getViewState()).restoreInstanceState(
              savedInstanceState);//从 fragment oncreate 参数 savedInstanceState 读取丢失的数据
      boolean restoredFromBundle = restoredViewState != null;
      if (restoredFromBundle) {
        viewStateSupport.setViewState(restoredViewState);
        applyViewState = true;
        return true;
      }
    }

接着在onActivityCreated中调用applyViewState恢复数据,delegate.getViewState().apply(delegate.getMvpView(), retainingInstance);交给相应的 ViewState 处理恢复。 在意外销毁时,onSaveInstanceState回调saveViewState,判断 isRetainInstance(),state 等再去保存数据。

//省略为空判断
    boolean retainingInstanceState = delegate.isRetainInstance();
    if (viewState != null && !retainingInstanceState
        && !(viewState instanceof RestorableViewState)) {
      throw new IllegalStateException(
          "ViewState " + viewState.getClass().getSimpleName() + " of " + /*...省略报错信息*/);
    }
    // Save the viewstate
    if (viewState != null && viewState instanceof RestorableViewState) {
      ((RestorableViewState) viewState).saveInstanceState(outState);//保存到 Bundle 中
    }

MvpLceViewStateFragment

它把 LCE 和 ViewState 合起来了,一般可以直接使用它。

MvpFrameLayout/MvpLinearLayout/MvpRelativeLayout

mosby 后来的版本才加对 Layout 的支持,这样意味这可以把功能模块缩小至 Layout,以前是 fragment+presenter,如果使用 layout+presenter,相比 fragment 更灵活。举个例子,知乎的回答详情页面,回答详情区域、点赞、收藏都是单独的功能逻辑,而且都具有 LCE 特点,采用 mvplayout 的话实现更为方便。 Mvp*Layout 和 Mvp*Fragment 原理类似,只不过 view 的生命周期和 fragment 不同。 目前 V2.0.1,官方提供了 mvpLayout、MvpViewState*Layout 的支持,未提供 MvpLce*Layout 的支持,可能正在更新吧,可以自己扩展下。

update:自定义增加了MvpLceViewStateFrameLayout.java

效果图:

MvpLceViewStateFrameLayout_gif

Apps
About Me
GitHub: Trinea
Facebook: Dev Tools