優(yōu)雅的使用MVP + RxJava +改造框架

image.png

最近看了不少M(fèi)VP的文章和項(xiàng)目,整合了一個(gè)關(guān)于MVP+retrofit2+RxJava的項(xiàng)目,話不多說直接上代碼

前言

首先,閱讀本篇文章前,建議你先去了解一下MVP這個(gè)設(shè)計(jì)模式。當(dāng)然,也可以先看看我前面的文章_ 傳送門

另外,還用到了RxJava、Retrofit、Okhttp。如果你已經(jīng)了解了他們的基本用法,請直接忽略這段,接著往下看~

不想看長長的文章的,可以直接看代碼。代碼地址已經(jīng)上傳到Github,https://github.com/jjxs/MvpPackage

工程結(jié)構(gòu)

按照慣例,先看看工程的主要結(jié)構(gòu):

image.png

簡單說一下幾個(gè)主要包下的功能。首先是api包,這是存放對Retrofit進(jìn)行包裝的類。Base包當(dāng)然是放各種Base類啦~ mvp包是將契約類Contract、Model的實(shí)現(xiàn)類和Presenter的實(shí)現(xiàn)類放一起,方便管理。其實(shí)你也可以按功能分包,個(gè)人喜好吧。ui包放一些界面的類,如Activity和Fragment。

下面正式開始~

契約類

同樣也是從Contract契約類開始說起:

public interface MainContract {

    interface View extends BaseView {

        void showDialog();

        void onSucceed(Gank data);

        void onFail(String err);

        void hideDialog();

    }

    interface Model extends BaseModel {
        Observable<Gank> getGank();
    }

    abstract class Presenter extends BasePresenter<View, Model> {
        public abstract void getGank();
    }
}

我們可以看到,整體上和Google的Demo差不多,都是把View和Presenter放到Contract類里面統(tǒng)一管理,我這里多加了個(gè)Model接口,我不推薦在Presenter進(jìn)行Model操作,本來很優(yōu)雅的一件事,在Presenter進(jìn)行Model操作的話,感覺就差了很多,要做一個(gè)優(yōu)雅的程序員。不同的地方是Model和View接口繼承了BaseModel接口和BaseView接口,Presenter變成了一個(gè)抽象類,繼承于BasePresenter抽象類,傳入兩個(gè)泛型View、Model。為啥呢?我們接著看Base包下的三個(gè)Base類。

Base類

BaseView:

public interface BaseView {
}

BaseModel;

public interface BaseModel {
}

BasePresenter:

public class BasePresenter<V extends BaseView,M extends BaseModel> {
    protected V mView;
    protected M mModel;

    private CompositeSubscription mCompositeSubscription;

    protected void addSubscribe(Subscription subscription) {
        if (mCompositeSubscription == null) {
            mCompositeSubscription = new CompositeSubscription();
        }
        mCompositeSubscription.add(subscription);
    }

    public void unSubscribe() {
        if (mView != null) {
            mView = null;
        }
        if (mCompositeSubscription != null && mCompositeSubscription.hasSubscriptions()) {
            mCompositeSubscription.clear();
        }
    }

BaseModel和BaseView接口里面是空的,在這里我只是為了在BasePresenter中提供一個(gè)約束。當(dāng)然,如果你有其它全局的需求,可以在里面添加一些方法。重點(diǎn)是BasePresenter這個(gè)抽象類,傳入一個(gè)View和Model,并將其用protected關(guān)鍵字修飾,這樣,在它的子類中就可以直接對其賦值和使用了。加入CompositeSubscription變量,是為了對RxJava進(jìn)行管理。unSubscribe方法對View進(jìn)行null賦值和清除Rx的Subscription(訂閱),防止內(nèi)存泄漏。

Presnter橋梁:

接下來看看這個(gè)很重要的類,作為連接Model和View的橋梁,這里又是怎么做的呢?

public class MainPresenter extends MainContract.Presenter {
    public MainPresenter(MainContract.View view) {
        mView = view;
        mModel = new MainModel();
    }

    @Override
    public void getGank() {

        Subscription subscribe = mModel.getGank()
                .subscribe(new Subscriber<Gank>() {

                    @Override
                    public void onStart() {
                        mView.showDialog();
                    }

                    @Override
                    public void onCompleted() {
                        mView.hideDialog();
                    }

                    @Override
                    public void onError(Throwable e) {
                        mView.onFail(e.getMessage());
                        onCompleted();
                    }

                    @Override
                    public void onNext(Gank gank) {
                        mView.onSucceed(gank);
                    }
                });


        addSubscribe(subscribe);
    }
}

構(gòu)造方法傳進(jìn)一個(gè)View,并且new了一個(gè)Model對象,直接賦值給父類中的View和Model。然后下面復(fù)寫的方法中調(diào)用Model中的方法,再將結(jié)果通過View中的方法傳出去,這是很原始的MVP方式。最后addSubscribe添加到訂閱隊(duì)列中。

Model處理數(shù)據(jù)

Model分出來,而不在Presenter處理,其實(shí)也是為了簡潔,當(dāng)你要處理很多數(shù)據(jù)的時(shí)候,Presenter就會(huì)變得很亂了。

public class MainModel implements MainContract.Model {

    @Override
    public Observable<Gank> getGank() {
        return ApiEngine.getInstance().getApiService()
                .getGank("1")
                .compose(RxSchedulers.<Gank>switchThread());
    }
}

我這里很簡單,就獲取ApiService對象,然后調(diào)用API。最后compose傳進(jìn)我自己定義的線程切換器:


public class RxSchedulers {

    public static <T> Observable.Transformer<T, T> switchThread() {
        return new Observable.Transformer<T, T>() {
            @Override
            public Observable<T> call(Observable<T> tObservable) {
                return tObservable
                        .subscribeOn(Schedulers.io())
                        .unsubscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread());
            }
        };
    }
}

將網(wǎng)絡(luò)請求的IO線程切換回Android的UI主線程,才能繼續(xù)進(jìn)行在Presenter中的操作。

BaseActivity中的封裝

在前面我們可以看到BasePresenter中有兩個(gè)方法,一個(gè)是添加訂閱addSubscribe,另一個(gè)是unSubscribe解除訂閱。我們只看到了在Presenter中使用了addSubscribe,而沒有看到unSubscribe在哪使用了。因?yàn)橐乐箖?nèi)存泄漏,所以當(dāng)然要在和生命周期相關(guān)的地方進(jìn)行釋放資源,這個(gè)地方只有我們所說的View了,也就是Activity和Fragment中。我們先開看一下相關(guān)代碼:

public abstract class BaseActivity<P extends BasePresenter> extends AppCompatActivity {

    protected P mPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (onCreatePresenter() != null) {
            mPresenter = onCreatePresenter();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mPresenter != null) {
            mPresenter.unSubscribe();
        }
    }

    protected abstract P onCreatePresenter();
}

通過泛型確定一個(gè)Presenter的類型,然后使用抽象方法onCreatePresenter對其進(jìn)行賦值,最后在onDestroy方法中進(jìn)行資源的釋放。繼承這個(gè)BaseActivity類的Activity,就不用每次都在onDestroy進(jìn)行同樣的操作啦~達(dá)到簡潔的目的。同理,F(xiàn)ragment中也是同樣的,只是在生命周期的onResume和onPause中分別進(jìn)行Presenter的賦值和資源的釋放。這里我就不貼代碼,可以上我的Github看。\

Retrofit引擎封裝

/**
 * Created by Fangzheng on 2017/9/21.
 */

public class ApiEngine {
    private volatile static ApiEngine apiEngine;
    private Retrofit retrofit;

    private ApiEngine() {

        //日志攔截器
        HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
        loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

        //緩存
        int size = 1024 * 1024 * 100;
        File cacheFile = new File(App.getContext().getCacheDir(), "OkHttpCache");
        Cache cache = new Cache(cacheFile, size);

        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(12, TimeUnit.SECONDS)
                .writeTimeout(12, TimeUnit.SECONDS)
                .writeTimeout(12, TimeUnit.SECONDS)
                .addNetworkInterceptor(new NetworkInterceptor())
                .addInterceptor(loggingInterceptor)
                .cache(cache)
                .build();

        retrofit = new Retrofit.Builder()
                .baseUrl(ApiService.BASE_URL)
                .client(client)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .build();
    }

    public static ApiEngine getInstance() {
        if (apiEngine == null) {
            synchronized (ApiEngine.class) {
                if (apiEngine == null) {
                    apiEngine = new ApiEngine();
                }
            }
        }
        return apiEngine;
    }

    public ApiService getApiService() {
        return retrofit.create(ApiService.class);
    }

}

用了單例模式,在構(gòu)造方法中只初始化一次Retrofit和Okhttp。雙重鎖的方式獲取單例,然后再根據(jù)需要獲取ApiService,如果你有很多個(gè)不同源的API,那就可以創(chuàng)建多個(gè)getXXXXApiService。

結(jié)語

看下運(yùn)行效果

image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 首先,閱讀本篇文章前,建議你先去了解一下MVP這個(gè)設(shè)計(jì)模式。當(dāng)然,也可以先看看我前面的文章_ 傳送門 另外,...
    Fi7z閱讀 4,526評(píng)論 4 37
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,946評(píng)論 25 709
  • 青石板街的邂逅 你一襲青衣,面紗微揚(yáng) 拔劍,銀蛇游動(dòng) 割裂的頸項(xiàng),你踩著鮮血而過 虛空顫動(dòng),幾里外的紫竹林 我翻轉(zhuǎn)...
    蘇格拉風(fēng)掠影閱讀 774評(píng)論 43 30
  • 1、感恩父母給予我生命。 2、感恩祖輩將我養(yǎng)育。 3、感恩母親、祖母為家的巨大付出。 4、感恩妻子對我一直的包容與...
    朱曉軍閱讀 196評(píng)論 0 0
  • Hi,你好哇,暖先生。請?jiān)试S我這么稱呼你,在遇見你之前。 今天是個(gè)特別的日子,5月20號(hào),上午9點(diǎn)21分,并沒有刻...
    趙無稽閱讀 361評(píng)論 0 2

友情鏈接更多精彩內(nèi)容