Mvp+Rxjava+Retrofit2.0+Okhttp完善版

這個(gè)框架在項(xiàng)目的使用中已經(jīng)有好一段時(shí)間了济锄,于是把其中基本的模塊抽取出來寫出來。

github代碼直通車

demo結(jié)構(gòu)圖:

搭建流程:

引入所需依賴庫:
    //Rxjava
    implementation 'io.reactivex:rxjava:1.1.0'
    implementation 'io.reactivex:rxandroid:1.1.0'

    //Retrofit
    implementation 'com.squareup.retrofit2:retrofit:2.0.0-beta4'
    implementation 'com.squareup.retrofit2:converter-gson:2.0.0-beta4'
    implementation 'com.squareup.retrofit2:adapter-rxjava:2.0.0-beta4'

    //Gson
    implementation 'com.google.code.gson:gson:2.6.2'
使用Retrofit+Rxjava
1.請(qǐng)求接口地址以及聲明類:

功能是初始化retrofit和okhttp;設(shè)置okhttp的header攔截器踩娘、請(qǐng)求數(shù)據(jù)攔截器熙尉、請(qǐng)求緩存攔截器。

public interface ApiService {

    /** 正式服務(wù)器地址 */
    String SERVER_ADDRESS_RELEASE = "";

    /** 測(cè)試服務(wù)器地址 */
    String SERVER_ADDRESS_DEBUG = "";

    /** 服務(wù)器域名 */
    String SERVER_ADDRESS = isDebug ? SERVER_ADDRESS_DEBUG : SERVER_ADDRESS_RELEASE;

    @GET("")
    Observable<BaseModel<VideoBean>> getVideoData();
}
2.單例的Retrofit和Okhttp管理類

單例模式創(chuàng)建ApiService類窗轩,添加服務(wù)器地址,設(shè)置5秒請(qǐng)求超時(shí)座咆。ApiService默認(rèn)返回Call類型痢艺,但是需要直接轉(zhuǎn)化Observable類型,此時(shí)需要添加addCallAdapterFactory(RxJavaCallAdapterFactory.create()) 介陶。需要直接轉(zhuǎn)化為需要的實(shí)體類堤舒,需要添加addCallAdapterFactory(RxJavaCallAdapterFactory.create())

public class ApiManager {
    private static ApiManager mApiManager;
    private OkHttpClient mOkHttpClient;
    private static ApiService mApiService;
    private final int TIMEOUT = 10;

    private ApiManager() {
        initOkhttp();
        initRetrofit();
    }

    public static synchronized ApiService getApiService() {
        if (mApiService == null) {
            mApiManager = new ApiManager();
        }
        return mApiService;
    }

    private void initOkhttp() {
        OkHttpClient.Builder builder = new OkHttpClient.Builder()
                .connectTimeout(TIMEOUT, TimeUnit.SECONDS)  //連接超時(shí)設(shè)置
                .readTimeout(TIMEOUT, TimeUnit.SECONDS)  //寫入緩存超時(shí)10s
                .writeTimeout(TIMEOUT, TimeUnit.SECONDS)  //讀取緩存超時(shí)10s
                .retryOnConnectionFailure(true)  //失敗重連
                .addInterceptor(new HeaderInterceptor())  //添加header
                .addInterceptor(new NetCacheInterceptor());  //添加網(wǎng)絡(luò)緩存

                addLogIntercepter(builder);  //日志攔截器
                setCacheFile(builder);  //網(wǎng)絡(luò)緩存

        mOkHttpClient = builder.build();
    }

    private void initRetrofit() {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(ApiService.SERVER_ADDRESS)
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .client(mOkHttpClient)
                .build();

        mApiService = retrofit.create(ApiService.class);
    }

    /**
     * 設(shè)置緩存文件路徑
     */
    private void setCacheFile(OkHttpClient.Builder builder) {
        //設(shè)置緩存文件
        File cacheFile = new File(DirConfig.HTTP_CACHE);
        //緩存大小為10M
        int cacheSize = 10 * 1024 * 1024;
        Cache cache = new Cache(cacheFile,cacheSize);
        builder.cache(cache);
    }

    /**
     * 調(diào)試模式下加入日志攔截器
     * @param builder
     */
    private void addLogIntercepter(OkHttpClient.Builder builder) {
        if (AppConfig.isDebug) {
            builder.addInterceptor(new LoggingInterceptor());
        }
    }
}
3.給okhttp添加攔截器
  • 統(tǒng)一添加header的攔截器:
public class HeaderInterceptor implements Interceptor {

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request.Builder builder = chain.request().newBuilder();
        builder.addHeader("token", UserInfoCache.TOKEN);

        return chain.proceed(builder.build());
    }
}
  • Okhttp返回?cái)?shù)據(jù)日志攔截器:
public class LoggingInterceptor implements Interceptor {
    private final int byteCount = 1024*1024;

    @Override
    public Response intercept(Chain chain) throws IOException {
        //chain里面包含了request和response,按需獲取
        Request request = chain.request();
        Response response = chain.proceed(request);

        LogUtils.d(String.format("發(fā)送請(qǐng)求  %s",request.url()));

        ResponseBody responseBody = response.peekBody(byteCount);

        LogUtils.d(String.format("接收響應(yīng)  %s", responseBody.string()));

        return response;
    }
}
  • 網(wǎng)絡(luò)攔截器進(jìn)行網(wǎng)絡(luò)緩存:
public class NetCacheInterceptor implements Interceptor {
    
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        Response response = chain.proceed(request);
        int onlineCacheTime = 60;

        return response.newBuilder()
                .header("Cache-Control", "public, max-age="+onlineCacheTime)
                .removeHeader("Pragma")
                .build();
    }
}
4.自定義請(qǐng)求數(shù)據(jù)回調(diào)和過濾:

封裝泛型實(shí)體類的觀察者哺呜,統(tǒng)一處理結(jié)果舌缤。請(qǐng)求成功給下一層返回實(shí)體類。請(qǐng)求失敗從HttpException中獲取code設(shè)置對(duì)應(yīng)的errorMsg

public abstract class ApiCallBack<M extends BaseModel> extends Subscriber<M> {

    @Override
    public void onCompleted() { }

    @Override
    public void onError(Throwable e) {
        e.printStackTrace();

        if (e instanceof HttpException) {
            HttpException httpException = (HttpException) e;
            int code = httpException.code();

            //Http狀態(tài)碼提示
            if (code >=400  && code< 500) {
                LogUtils.e("請(qǐng)求錯(cuò)誤");
            } else if (code >= 500) {
                LogUtils.e("服務(wù)器錯(cuò)誤");
            }
        }
    }

    @Override
    public void onNext(M baseModel) {
        handleStatusCode(baseModel);
    }

    /**
     * 后臺(tái)statuscode狀態(tài)碼處理弦牡,狀態(tài)碼類型封裝在StatusCodeEnum中
     * @param baseModel
     */
    private void handleStatusCode(M baseModel) {
        String statusCode = baseModel.code;
        StatusCodeEnum statusCodeEnum = StatusCodeEnum.getByCode(statusCode);
        switch (statusCodeEnum) {
            case RESULT_OK:  //請(qǐng)求成功
                onSuccess(baseModel);
                break;
            case RESULT_TOKENINVALID:  //token失效
                // TODO: 2018/11/23 token失效友驮,刪除本地用戶信息儲(chǔ)存并退出登錄
                break;
            default:
                LogUtils.e("請(qǐng)求失敗錯(cuò)誤");
                break;
        }
    }

    /**
     * onSuccess回調(diào)的數(shù)據(jù)為程序具體需要的業(yè)務(wù)狀態(tài)碼,具體數(shù)據(jù)等
     */
    public abstract void onSuccess(M m);
}

MVP加入:

1.View

Iview類:規(guī)定所有Activity,fragment等容器的公共行為驾锰,例如顯示加載框和隱藏

public interface IView {

    void showLoading();

    void hideLoading();
}

IBaseView:

public interface IBaseView<M extends BaseModel> extends IView {
    
}
2.presenter

Ipresenter:

public interface Ipresenter<V extends IView> {
    /**
     * 關(guān)聯(lián)P與V
     * @param v
     */
    void attachView(V v);

    /**
     * 取消關(guān)聯(lián)P與V
     */
    void detachView();

    /**
     * Rx訂閱
     */
    void subscribe(Observable observable, Subscriber subscriber);

    /**
     * Rx取消訂閱
     */
    void unSubscribe();
}

BasePresenter:
Basepresenter,抽象的persenter業(yè)務(wù)處理層卸留。關(guān)聯(lián)抽象層view和抽象model,通過CompositeSubscription關(guān)聯(lián)數(shù)據(jù)請(qǐng)求椭豫,并通過subscriber回調(diào)回view層耻瑟。

public class BasePresenter<V extends IBaseView> implements Ipresenter<V> {
    protected V view;
    protected ApiService apiService;
    protected CompositeSubscription mCompositeSubscription;

    @Override
    public void attachView(IBaseView v) {
        this.view = (V) v;
        apiService = ApiManager.getApiService();
    }

    @Override
    public void detachView() {
        this.view = null;
        unSubscribe();
    }

    @Override
    public void subscribe(Observable observable, Subscriber subscriber) {
        if (mCompositeSubscription == null) {
            mCompositeSubscription = new CompositeSubscription();
        }

        //綁定observable與subscriber
        Subscription subscription = observable.observeOn(AndroidSchedulers.mainThread())
                  .subscribeOn(Schedulers.io())
                  .subscribe(subscriber);

        mCompositeSubscription.add(subscription);
    }

    @Override
    public void unSubscribe() {
        if (mCompositeSubscription != null && mCompositeSubscription.hasSubscriptions()) {
            mCompositeSubscription.unsubscribe();
        }
    }
}
3.Model

BaseModel:請(qǐng)求數(shù)據(jù)實(shí)體類基類旨指,對(duì)需要的data數(shù)據(jù)進(jìn)行泛型處理

public class BaseModel<T> implements IModel,Serializable {

    /** 自定義錯(cuò)誤碼 */
    public String code;
    /** 自定義業(yè)務(wù)狀態(tài)碼 */
    public String bizcode;
    /** 消息提示 */
    public String msg;
    /** 泛型實(shí)體類 */
    public T data;

    public boolean isOk() {
        return bizcode.equals(AppConfig.BIZCODE_OK);
    }
}
4.MvpActivity基類:

MvpActivity綁定presenter,在銷毀時(shí)解綁喳整,調(diào)用onUnsubscribe()將rxjava統(tǒng)一取消注冊(cè)谆构,以避免內(nèi)存泄露。至于BaseActivity功能根據(jù)項(xiàng)目業(yè)務(wù)需求進(jìn)行封裝框都。比如跳轉(zhuǎn)搬素,加載彈出框,是否全屏魏保,頻繁調(diào)用工具類等熬尺。

public abstract class BaseMvpActivity<P extends BasePresenter> extends AppCompatActivity {
    protected P mPresenter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        mPresenter = createPresenter();
        super.onCreate(savedInstanceState);
    }

    protected abstract P createPresenter();

    @Override
    protected void onDestroy() {
        super.onDestroy();

        //頁面銷毀時(shí)取消presenter綁定
        if (mPresenter != null) {
            mPresenter.detachView();
        }
    }
}
實(shí)現(xiàn)MainActivity的功能:
1.MainView:
public interface MainView extends IBaseView {

    void getVideoData(BaseModel<VideoBean> videoBeanBaseModel);
}
2.MainPresenter:
public class MainPresenter extends BasePresenter<MainView> {

    public MainPresenter(MainView mainView) {
        attachView(mainView);
    }

    public void getVideoData() {
        view.showLoading();
        subscribe(apiService.getVideoData(), new ApiCallBack<BaseModel<VideoBean>>() {
            @Override
            public void onSuccess(BaseModel<VideoBean> videoBeanBaseModel) {
                view.hideLoading();
                view.getVideoData(videoBeanBaseModel);
            }
        });
    }
}
2.MainActivity:
public class MainActivity extends BaseMvpActivity<MainPresenter> implements MainView{
    private TextView tvContent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        tvContent = findViewById(R.id.tv_content);
        mPresenter.getVideoData();
    }

    @Override
    protected MainPresenter createPresenter() {
        return new MainPresenter(this);
    }

    @Override
    public void showLoading() {

    }

    @Override
    public void hideLoading() {

    }

    @Override
    public void getVideoData(BaseModel<VideoBean> videoBeanBaseModel) {
        tvContent.setText(videoBeanBaseModel.data.toString());
    }
}

好的,打完收工谓罗。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末粱哼,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子檩咱,更是在濱河造成了極大的恐慌揭措,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,590評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件刻蚯,死亡現(xiàn)場(chǎng)離奇詭異绊含,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)芦倒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門艺挪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來不翩,“玉大人兵扬,你說我怎么就攤上這事】隍穑” “怎么了器钟?”我有些...
    開封第一講書人閱讀 169,301評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)妙蔗。 經(jīng)常有香客問我傲霸,道長(zhǎng),這世上最難降的妖魔是什么眉反? 我笑而不...
    開封第一講書人閱讀 60,078評(píng)論 1 300
  • 正文 為了忘掉前任昙啄,我火速辦了婚禮,結(jié)果婚禮上寸五,老公的妹妹穿的比我還像新娘梳凛。我一直安慰自己,他們只是感情好梳杏,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,082評(píng)論 6 398
  • 文/花漫 我一把揭開白布韧拒。 她就那樣靜靜地躺著淹接,像睡著了一般。 火紅的嫁衣襯著肌膚如雪叛溢。 梳的紋絲不亂的頭發(fā)上塑悼,一...
    開封第一講書人閱讀 52,682評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音楷掉,去河邊找鬼厢蒜。 笑死,一個(gè)胖子當(dāng)著我的面吹牛烹植,可吹牛的內(nèi)容都是我干的郭怪。 我是一名探鬼主播,決...
    沈念sama閱讀 41,155評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼刊橘,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼鄙才!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起促绵,我...
    開封第一講書人閱讀 40,098評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤攒庵,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后败晴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體浓冒,經(jīng)...
    沈念sama閱讀 46,638評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,701評(píng)論 3 342
  • 正文 我和宋清朗相戀三年尖坤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了稳懒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,852評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡慢味,死狀恐怖场梆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情纯路,我是刑警寧澤或油,帶...
    沈念sama閱讀 36,520評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站驰唬,受9級(jí)特大地震影響顶岸,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜叫编,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,181評(píng)論 3 335
  • 文/蒙蒙 一辖佣、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧搓逾,春花似錦卷谈、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽藕施。三九已至,卻和暖如春凸郑,著一層夾襖步出監(jiān)牢的瞬間裳食,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評(píng)論 1 274
  • 我被黑心中介騙來泰國打工芙沥, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留诲祸,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,279評(píng)論 3 379
  • 正文 我出身青樓而昨,卻偏偏與公主長(zhǎng)得像救氯,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子歌憨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,851評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容