Android MVP schema base class structure

Keywords: Android Fragment network xml

Believed that MVP mode has been used extensively in Android applications, let me introduce a base class structure for MVP mode.




The whole structure takes Fragment as its core. First, it defines that BaseFragment inherits from Fragment. It implements the launch method which takes the Fragment to be loaded as a parameter, and actually loads the BaseActivity that binds the target Fragment in the launch method.In addition, a series of helper s, such as viewHelper, dialoagHelper, broadCastHelper, are instantiated and saved in BaseFragment so that related tasks can be handled easily in subclasses.

    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);
        }
    }


The subsequent subclass, BaseLinearFragment, loads a layout containing two ViewStub s on the basis of BaseFragment and defines methods based on the distinction between head and 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;
    }


At BaseRequestFragment, you begin processing tasks related to network requests.It implements the IRequestView interface to handle the results of network requests in three cases: successful data acquisition/success but empty data/failed request.


public interface IRequestView {

    void showLoading();

    void showContent();

    void showEmpty();

    @DrawableRes
    int getEmptyImageResId();

    String getEmptyText();

    void showError();
}

The corresponding BaseRequestPresenter also implements the IPresenter interface, which binds to and holds IRequestView.

public interface IPresenter<V extends IRequestView> {

    void attachView(V view);

    void detachView(V view);
}

SingleRequestFragment finally establishes the MVP model based on BaseRequestFragment, holding each other through SingleRequestContract s and SingleRequestPresenter to handle situations where an Api is issued to get a data structure to render when the Fragment is loaded.

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 is also inherited from BaseRequestFragment, but is more complex than SingleRequestFragment.It is used to process fragments that issue Api s to get a list, with listView as the main body.To do this, ListRequestFragment holds a listView, which implements drop-down refresh, load more, empty lists, list load errors, and many other methods related to this 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();
    }
}

The corresponding SingleRequestPresenter and ListRequestPresenter implement a default callback DefaultCallback, which handles the returned results, in addition to the methods in Contract.If the request succeeds, the data is parsed and the parsed structure is passed to Fragment for rendering.



To sum up, using this MVP base class structure, and using it with network request libraries (Volley, Retrofit), and data parsing libraries (gson), developers can focus on UI drawing, request sending, and processing the results of requests with little effort.Especially for lists-based interfaces, if you start from scratch, it can be cumbersome because there are many refresh mechanisms and many error mechanisms.And if you develop this structure, it feels like going from hell to heaven...

Posted by mightymaster on Sun, 16 Jun 2019 10:36:42 -0700