1. 程式人生 > >Android MVP模式基類結構

Android MVP模式基類結構

相信MVP模式在Android程式中已經使用的非常廣泛了,下面我來介紹一種MVP模式的基類結構。


整個結構以Fragment為核心,首先定義了BaseFragment繼承於Fragment,主要是實現了把要載入的Fragment作為引數的launch方法,並在launch方法中實際載入綁定了目標Fragment的BaseActivity。除此之外,BaseFragment中還例項化並儲存了一系列helper,比如viewHelper,dialoagHelper,broadCastHelper等等,從而可以在子類中方便的處理相關任務。

    public void launch(Class<? extends Fragment> fragment,
                       Bundle args4fragment, int reqCode) {
        launch(BaseActivity.class, fragment, args4fragment, reqCode);
    }

    public void launch(Class<? extends BaseActivity> activity,
                       Class<? extends Fragment> fragment, Bundle args4fragment, int reqCode) {
        if (getActivity() == null || isDetached()) {
            return;
        }
        Intent in = BaseActivityHelper.builder(this, activity).setFragment(fragment, args4fragment).build();
        if (reqCode != 0) {
            getHostFragment().startActivityForResult(in, reqCode);
        } else {
            getHostFragment().startActivity(in);
        }
    }


隨後的子類BaseLinearFragment,則是在BaseFragment的基礎上載入了一個包含兩個ViewStub的layout,並依照head和body的區分又定義了一些方法。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    android:id="@+id/content"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ViewStub
        android:id="@+id/head"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout="@layout/navbar"/>

    <ViewStub
        android:id="@+id/body"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>
    @Override
    protected final void initInflateViewStub(View parent) {
        super.initInflateViewStub(parent);
        int headLayoutId = getHeadLayoutId();
        if (headLayoutId != EmptyConst.EMPTY_ID) {
            setupHead(viewHelper.inflateViewStub(R.id.head, headLayoutId));
        }
        setupBody(viewHelper.inflateViewStub(R.id.body, getBodyLayoutId()));
    }

    @LayoutRes
    protected int getHeadLayoutId() {
        return R.layout.navbar;
    }

到了BaseRequestFragment這,就開始處理網路請求相關的任務了。它實現了IRequestView介面,可以依照 成功獲取資料/成功但資料為空/請求失敗 三種情況來處理網路請求的結果。


public interface IRequestView {

    void showLoading();

    void showContent();

    void showEmpty();

    @DrawableRes
    int getEmptyImageResId();

    String getEmptyText();

    void showError();
}

同時相應的BaseRequestPresenter實現了IPresenter介面,可以對IRequestView進行繫結,並持有IRequestView。
public interface IPresenter<V extends IRequestView> {

    void attachView(V view);

    void detachView(V view);
}

SingleRequestFragment則在BaseRequestFragment的基礎上終於建立起MVP模式,通過SingleRequestContract來和SingleRequestPresenter相互持有,用來處理載入Fragment時要發Api來獲取一個數據結構進行渲染的情形。
public interface SingleRequestContract {

    interface View<T> extends IRequestView {

        Context getContext();

        void render(@NonNull T data);
    }

    interface Presenter<T> extends IPresenter<View<T>> {

        void onSaveInstanceState(Bundle outState);

        void restoreSavedInstance(Bundle savedInstanceState);
        
        void load(@Nullable RequestCallback<T> callback);
    }
}
ListRequestFragment同樣是繼承於BaseRequestFragment,但卻比SingleRequestFragment複雜的多。它是用來處理要發Api來獲取一個列表,並以listView為主體的Fragment。為此,ListRequestFragment持有一個listView,實現了與此listView相關的下拉重新整理、載入更多、列表為空、列表載入錯誤等諸多方法。
public interface RequestListContract {

    interface View<ListItem> extends IRequestView {

        void renderDataList(List<ListItem> dataList, boolean canLoadMore, boolean refresh);

        void toastNetworkError(NetApiException error);

        void showNetworkError(boolean refresh);

        boolean showListLoading();

        void dismissListLoading();
    }

    interface Presenter<ListItem> extends IPresenter<View<ListItem>> {

        void load();

        void refresh(boolean showListLoading);

        void loadMore();
    }
}

而相應的SingleRequestPresenter和ListRequestPresenter除了實現Contract中的方法之外,主要是實現了一個預設回撥DefaultCallback,在其中對返回的結果進行了處理。如果請求成功,則進行資料解析,並把解析後的結構傳給Fragment進行渲染。

綜上所述,使用這套MVP基類結構,並把它與網路請求庫(Volley、Retrofit)、資料解析庫(gson)搭配使用後,開發者可以專注於UI繪製、請求傳送,對於請求的結果處理幾乎不用再花費什麼工作量。特別是對於以列表為主體的介面而言,由於存在多種重新整理機制,同時也存在多種出錯機制,如果從頭寫非常麻煩。而如果在此結構上開發,感覺就會像由地獄來到了天堂...