淺談Retrofit封裝-讓框架更加簡潔易用

尊重他人的勞動成果,轉載請標明出處:http://blog.csdn.net/gengqiquan/article/details/52329259, 本文出自:【gengqiquan的博客】

不知不覺在在這家公司快三個月了,剛接手項目的時候是真的被裏面的代碼噁心到了,網絡請求用的原生的httpURLconnection。這本來什麼,關鍵是根本就沒有進行任何封裝。activity裏面充斥着大量的handler,要找個oncreated()函數先得把類拉到一半,那種感覺豈止酸爽。由於項目週期緊張。沒時間去大規模的重構,新框架只能在新功能裏寫。採用了retrofit,初期做了簡單的封裝,斷斷續續一段時間的優化整理。現在分享出來給大家。

爲了方便大家,會形成依賴庫。引入和調用方式請看文章底部的github文檔
本次封裝需要做到的

1支持異步請求統一回調
2參數可配置
3鏈式調用
4支持基本get和post封裝
5支持rxjava返回
6支持一個接口Service 對應一種請求類型,而不是每個API都需要去定義一個接口方法
7支持取消單個請求
8支持請求打標籤,退出頁面時取消當前頁面所有請求
9支持動態配置緩存,緩存與具體的請求接口綁定,
10支持追加統一參數,登錄信息token、設備號等
11支持接口版本號配置
12流式下載文件,取消下載請求

下面我們來看下封裝過程,博客底部會貼出示例項目地址
retrofit獨樹一幟的把請求採用了接口,方法和註解參數(parameter annotations)來聲明式定義一個請求應該如何被創建的方式。
像這樣

public interface GitHub {
      @GET("/repos/{owner}/{repo}/contributors")
        List<Contributor> contributors(@Path("owner")
        String owner, @Path("repo")
        String repo);

然後去實例化並且調用請求

  GitHub github = restAdapter.create(GitHub.class);
        // Fetch and print a list of the contributors to this library.
        List<Contributor> contributors = github.contributors("square",
                "retrofit");

先不談retrofit到底做了多少優化、性能上有多少提升。光是這樣的調用方式我就受不了。我特麼得寫多少個 像上面的GitHub 一樣的Service,就算是把url註解方法都寫在一個裏面,那也得多少行?一個項目六七十行應該是沒什麼問題的了。嗯。反正我會看瘋了的。而且這樣的一種調用方式是直接面向框架層的,以後萬一我們換框架了怎麼辦?代碼挨個找出來全換一邊?你瘋不瘋?
那我們有沒有什麼辦法改變他?很簡單,我們在框架外面再套一層通用框架,作爲框架的設計者,我們應該讓調用者知道怎麼調用就可以了,而不應該讓調用者去考慮底層實現的細節。
好在retrofit提供了Url 參數替換註解@Url String url,通過這個註解我們可以動態的設置請求的url。
下面列出一些簡單的參數註解

@Url 替換url
@QueryMap  替換url中查詢參數
@Header  替換header
@FieldMap 替換post請求body中參數
@FormUrlEncoded post請求需要加的方法註解
@POST() 標示該方法爲post請求
@GET() 標示該方法爲get請求

瞭解了這些註解這樣我們就可以將我們項目的請求變成幾個基本的方法,由於我的項目的服務端返回的基本格式不是固定的,而GsonConverterFactory,對於retrofit的返回類型要求只能是: 具體對象、jsonObject、jsonArray這三種 ,爲了避免每個請求寫兩個方法,這裏我自己寫了個返回類型爲String的解析器,參考 Retrofit自定義Converter之StringConverterFactory

這樣我們的返回類型都是String

public interface RetrofitHttpService {


    @GET()
    Call<String> get(@Url String url, @QueryMap Map<String, String> params, @Header("Cache-Time") String time);
    @FormUrlEncoded

    @POST()
    Call<String> post(@Url String url, @FieldMap Map<String, String> params, @Header("Cache-Time") String time);


}

如果你項目的返回請求外層有固定的格式可以把 Call<JsonObject>替換成Call<Model>,這裏的model就是你的基礎數據返回類型。
如果你要使用rxjava的話需要額外兩個註解方法


    @GET()
    Observable<String> Obget(@Url String url, @QueryMap Map<String, String> params, @Header("Cache-Time") String time);

    @FormUrlEncoded
    @POST()
    Observable<String> Obpost(@Url String url, @FieldMap Map<String, String> params, @Header("Cache-Time") String time);

構建網絡數據請求類,這裏爲了方便不同的項目使用,用一個builder類來構建單例

public class HttpUtil {
    private static volatile HttpUtil mInstance;
    private static volatile RetrofitHttpService mService;
    private Context mAppliactionContext;
    private static String mVersionApi;
    private ParamsInterceptor mParamsInterceptor;

    //構造函數私有,不允許外部調用
    private HttpUtil(RetrofitHttpService mService, Context mAppliactionContext, String mVersionApi, ParamsInterceptor mParamsInterceptor) {
        this.mService = mService;
        this.mAppliactionContext = mAppliactionContext;
        this.mVersionApi = mVersionApi;
        this.mParamsInterceptor = mParamsInterceptor;
    }

    public static class SingletonBuilder {
        private Context appliactionContext;
        private String baseUrl;
        private List<String> servers = new ArrayList<>();
        private String versionApi;
        private ParamsInterceptor paramsInterceptor;
        private List<Converter.Factory> converterFactories = new ArrayList<>();
        private List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
        OkHttpClient client;

        public SingletonBuilder(Context context) {
            try {//防止傳入的是activity的上下文
                Activity activity = (Activity) context;
                appliactionContext = context.getApplicationContext();
            } catch (Exception e) {
                e.printStackTrace();
                appliactionContext = context;
            }
        }

        public SingletonBuilder client(OkHttpClient client) {
            this.client = client;
            return this;
        }

        public SingletonBuilder versionApi(String versionApi) {
            this.versionApi = versionApi;
            return this;
        }

        public SingletonBuilder paramsInterceptor(ParamsInterceptor interceptor) {
            this.paramsInterceptor = interceptor;
            return this;
        }

        public SingletonBuilder baseUrl(String baseUrl) {
            this.baseUrl = baseUrl;
            return this;
        }

        public SingletonBuilder addServerUrl(String ipUrl) {
            this.servers.add(ipUrl);
            return this;
        }

        public SingletonBuilder serverUrls(List<String> servers) {
            this.servers = servers;
            return this;
        }

        public SingletonBuilder addConverterFactory(Converter.Factory factory) {
            this.converterFactories.add(factory);
            return this;
        }

        public SingletonBuilder addCallFactory(CallAdapter.Factory factory) {
            this.adapterFactories.add(factory);
            return this;
        }

        public HttpUtil build() {
            if (checkNULL(this.baseUrl)) {
                throw new NullPointerException("BASE_URL can not be null");
            }
            if (converterFactories.size() == 0) {
                converterFactories.add(StringConverterFactory.create());
            }
            if (adapterFactories.size() == 0) {
                adapterFactories.add(RxJavaCallAdapterFactory.create());
            }
            if (client == null) {
                client = OkhttpProvidede.okHttpClient(appliactionContext, baseUrl, servers);
            }
            Retrofit.Builder builder = new Retrofit.Builder();

            for (Converter.Factory converterFactory : converterFactories) {
                builder.addConverterFactory(converterFactory);
            }
            for (CallAdapter.Factory adapterFactory : adapterFactories) {
                builder.addCallAdapterFactory(adapterFactory);
            }
            Retrofit retrofit = builder
                    .baseUrl(baseUrl + "/")
                    .client(client).build();

            RetrofitHttpService retrofitHttpService =
                    retrofit.create(RetrofitHttpService.class);

            mInstance = new HttpUtil(retrofitHttpService, appliactionContext, versionApi, paramsInterceptor);
            return mInstance;
        }
    }

一般網絡請求都是異步的,那麼此時我們進行異步調用網絡請求的代碼是這樣的

   HttpUtil.getmInstance(mContext).get("url",params,"3600*24").enqueue(new Callback<JsonObject>() {
            @Override
            public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {

            }

            @Override
            public void onFailure(Call<JsonObject> call, Throwable t) {

            }
        });

以上的代碼已經採用了回調的方式,保證了onResponse()和 onFailure()裏的代碼都是在主線程裏執行的,這樣的代碼相對於handler那種已經簡潔易用了很多,不過我們不能滿足於此,比如判斷單例是否實例化並去獲取單例的方法根本沒必要每次調用都去做,我們只需要在application裏初始化就可以了。回調的參數是Call 和Response ,我們肯定需要對他做同樣的處理拿出裏面的json值才能用的。這樣這些代碼就可以進一步封裝。
retrofit裏面提供了異步回調接口類Callback<T>,不過我們不能直接拿來用,因爲一旦我們換網絡請求框架。肯定是不能再使用舊的框架裏面的回調接口的,所以需要我們定義自己的外層回調接口,一般有成功的回調和失敗的回調兩種。由於我支持lambda,所以我會把不同的情況分別定義成接口。如何在android中使用lambda請看這篇Androidstudio使用java8、lambda表達式教程
成功回調,一般就是返回的json串,一個參數就夠了

@FunctionalInterface
public interface Success {
    void Success( String model);
}

失敗回調,失敗回調不同的網絡框架返回的參數個數不一樣,這裏我們定義成Object數組

@FunctionalInterface
public interface Error {
    void Error(Object... values);
}

下面我們封裝post請求

  public static void post(String url, Map<String, String> params, String cacheTime,Success mSuccessCallBack ,Error mErrorCallBack){
   mInstance.post(url, params, cacheTime).enqueue(new Callback<String>() {
                @Override
                public void onResponse(Call<String> call, Response<String> response) {
                    if (response.code() == 200) {
                        mSuccessCallBack.Success(response.body().toString());
                    } else {
                        mErrorCallBack.Error(response.code(), message(response.message()), null);
                    }
                }

                @Override
                public void onFailure(Call<String> call, Throwable t) {
                    mErrorCallBack.Error(200, message(t.getMessage()), t);
                }
            });}

此時我們調用post請求時是這樣的

  HttpUtil.get("url",params,"3600*24", new Success() {
            @Override
            public void Success(String model) {

            }
        },new Error() {
            @Override
            public void Error(Object... value) {

            }
        });

獨立回調的方式,使用過Volley的人應該熟悉這種方式,這樣我們每次調用post的時候只要調用這個方法就可以了,其他幾個請求方法一樣的封裝方式。
但是這樣我還是覺得不夠簡潔,不夠優雅,必傳參數太多了(哪怕傳入null)。兩個回調函數看起來也太臃腫了(接口方法增加了代碼長度,當然可以用lambda優化,不過那樣看起來不倫不類,其實並不利於閱讀)。有時候我們是隻有一個url的,有時候我們卻還會有查詢參數的,有時候我們的url是要追加版本號的,有時候我們是需要當前接口支持緩存的,而有的項目是需要統一追加各種配置參數的,如設備號,手機號碼等等。這樣我們的請求方法的參數個數就是不固定的,比較暴力的方法就是寫多個不同參數的重載方法。我以前一般是這麼幹的,不過太low,這裏我們採用builder模式。

  public static class Builder {
        Map<String, String> params = new HashMap<>();
        String url;
        Error mErrorCallBack;
        Success mSuccessCallBack;
        String cacheTime;
        boolean addVersion = false;

        public Builder CacheTime(String time) {
            this.cacheTime = time;
            return this;
        }

        public Builder Url(String url) {
            this.url = url;
            return this;
        }

        public Builder Params(Map<String, String> params) {
            this.params.putAll(params);
            return this;
        }

        public Builder Params(String key, String value) {
            this.params.put(key, value);
            return this;
        }

        public Builder Success(Success success) {
            this.mSuccessCallBack = success;
            return this;
        }

        public Builder Version() {
            this.addVersion = true;
            return this;
        }

        public Builder Error(Error error) {
            this.mErrorCallBack = error;
            return this;
        }

        public Builder() {
            this.setParams();
        }

        public Builder(String url) {
            this.setParams(url);
        }

        private void setParams() {
            this.setParams(null);
        }

        private void setParams(String url) {
         if (mInstance == null) {
                throw new NullPointerException("HttpUtil has not be initialized");
            }
            this.url = url;
            this.params = new HashMap<>();
            this.mErrorCallBack = (v) -> {
            };
            this.mSuccessCallBack = (s) -> {
            };
        }


        private String checkUrl(String url) {
            if (Util.checkNULL(url)) {
                throw new NullPointerException("absolute url can not be empty");
            }
            if (addVersion && !url.contains(mVersionApi)) {
                url = V(url);
            }
            return url;
        }

        public void get() {
            this.url = checkUrl(this.url);
            this.params = checkParams(this.params);
            if (cacheTime == null) {
                cacheTime = "";
            }
            mInstance.get(url, checkParams(params), cacheTime).enqueue(new Callback<String>() {
                @Override
                public void onResponse(Call<String> call, Response<String> response) {
                    if (response.code() == 200) {
                        mSuccessCallBack.Success(response.body().toString());
                    } else {
                        mErrorCallBack.Error(response.code(), message(response.message()), null);
                    }
                }

                @Override
                public void onFailure(Call<String> call, Throwable t) {
                  mErrorCallBack.Error(200, message(t.getMessage()), t);
                }
            });
        }}

checkParams()方法就是對參數的統一處理,追加參數什麼的可以在這裏面做,Version()方法 是決定要不要給url追加開發版本號。寫了這麼一大堆,貌似代碼變多了啊。別擔心,下面我們看看當我們調用請求的時候的代碼是多少

  new HttpUtil.Builder("favorite_authorized/list?page=1")
                    .Version()
                    .CacheTime("3600*24")
                    .Params("carId", sb.toString())
                    .Params(sortMap_)
                    .Success((s) -> {
                        ld_.dismiss();
                        BaseModel model = new BaseModel(s);
                    })
                    .Error((v) -> {
                        ld_.dismiss();
                        handler_.obtainMessage(MSG, v[1]).sendToTarget();
                    })
                    .get();
        });

就這麼多,採用鏈式調用,上面我們動態設置了請求的url,參數,追加開發版本號,追加參數,設置了緩存一天,代碼簡潔明瞭,實際使用中我們可以根據單個請求的不同需求決定添加什麼方法鏈。
如果你的項目需要支持緩存,請參照okhttp之自定義Interceptor:緩存攔截器 這篇博客
如果你項目需要失敗重試和切換服務器IP並且服務端不支持需要客戶端配置實現,請參照 okhttp之自定義Interceptor:請求失敗切換IP重試攔截器

下面我們添加取消請求

/*
*添加某個請求
*@author Administrator
*@date 2016/10/12 11:00
*/
    private static synchronized void putCall(Object tag, String url, Call call) {
        if (tag == null)
            return;
        synchronized (CALL_MAP) {
            CALL_MAP.put(tag.toString() + url, call);
        }
    }

    /*
    *取消某個界面都所有請求,或者是取消某個tag的所有請求
    * 如果要取消某個tag單獨請求,tag需要轉入tag+url
    *@author Administrator
    *@date 2016/10/12 10:57
    */
    public static synchronized void cancel(Object tag) {
        if (tag == null)
            return;
        List<String> list = new ArrayList<>();
        synchronized (CALL_MAP) {
            for (String key : CALL_MAP.keySet()) {
                if (key.startsWith(tag.toString())) {
                    CALL_MAP.get(key).cancel();
                    list.add(key);
                }
            }
        }
        for (String s : list) {
            removeCall(s);
        }

    }

    /*
    *移除某個請求
    *@author Administrator
    *@date 2016/10/12 10:58
    */
    private static synchronized void removeCall(String url) {
        synchronized (CALL_MAP) {
            for (String key : CALL_MAP.keySet()) {
                if (key.contains(url)) {
                    url = key;
                    break;
                }
            }
            CALL_MAP.remove(url);
        }
    }

在請求類builder構建時添加進tag

  Object tag;
        public Builder Tag(Object tag) {
            this.tag = tag;
            return this;
        }

在call構建時將添加進全局靜態map,請求返回時移除掉,免得持有引用導致內存泄漏

  public void get() {
            if (cacheTime == null) {
                cacheTime = "";
            }
            Call call = mInstance.get(checkUrl(this.url), checkParams(params), cacheTime);
            putCall(tag, url, call);//將call添加進map
            call.enqueue(new Callback<String>() {
                @Override
                public void onResponse(Call<String> call, Response<String> response) {
                    if (response.code() == 200) {
                        mSuccessCallBack.Success(response.body().toString());
                    } else {
                        mFailureCallBack.Failure(response.code(), message(response.message()), null);
                        mErrorCallBack.Error(response.code(), message(response.message()), null);
                    }
                    if (tag != null)
                        removeCall(url);//移除call
                }

                @Override
                public void onFailure(Call<String> call, Throwable t) {
                    mFailureCallBack.Failure(200, message(t.getMessage()), t);
                    mErrorCallBack.Error(200, message(t.getMessage()), t);
                    if (tag != null)
                        removeCall(url);//移除call
                }
            });
        }

下面貼出完整的httputil類

package com.sunshine.retrofit;

import android.app.Activity;
import android.content.Context;
import android.widget.Toast;

import com.sunshine.retrofit.converter.StringConverterFactory;
import com.sunshine.retrofit.interfaces.Error;
import com.sunshine.retrofit.interfaces.ParamsInterceptor;
import com.sunshine.retrofit.interfaces.Success;
import com.sunshine.retrofit.utils.OkhttpProvidede;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import okhttp3.OkHttpClient;
import retrofit2.Call;
import retrofit2.CallAdapter;
import retrofit2.Callback;
import retrofit2.Converter;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import rx.Observable;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;

/**
 * Created by 耿 on 2016/6/28.
 */
public class HttpUtil {
    private static volatile HttpUtil mInstance;
    private static volatile RetrofitHttpService mService;
    private Context mAppliactionContext;
    private static String mVersionApi;
    private ParamsInterceptor mParamsInterceptor;

    //構造函數私有,不允許外部調用
    private HttpUtil(RetrofitHttpService mService, Context mAppliactionContext, String mVersionApi, ParamsInterceptor mParamsInterceptor) {
        this.mService = mService;
        this.mAppliactionContext = mAppliactionContext;
        this.mVersionApi = mVersionApi;
        this.mParamsInterceptor = mParamsInterceptor;
    }

    public static RetrofitHttpService getService() {
        if (mInstance == null) {
            throw new NullPointerException("HttpUtil has not be initialized");
        }
        return mService;
    }

    public static class SingletonBuilder {
        private Context appliactionContext;
        private String baseUrl;
        private List<String> servers = new ArrayList<>();
        private String versionApi;
        private ParamsInterceptor paramsInterceptor;
        private List<Converter.Factory> converterFactories = new ArrayList<>();
        private List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
        OkHttpClient client;

        public SingletonBuilder(Context context) {
            try {//防止傳入的是activity的上下文
                Activity activity = (Activity) context;
                appliactionContext = context.getApplicationContext();
            } catch (Exception e) {
                e.printStackTrace();
                appliactionContext = context;
            }
        }

        public SingletonBuilder client(OkHttpClient client) {
            this.client = client;
            return this;
        }

        public SingletonBuilder versionApi(String versionApi) {
            this.versionApi = versionApi;
            return this;
        }

        public SingletonBuilder paramsInterceptor(ParamsInterceptor interceptor) {
            this.paramsInterceptor = interceptor;
            return this;
        }

        public SingletonBuilder baseUrl(String baseUrl) {
            this.baseUrl = baseUrl;
            return this;
        }

        public SingletonBuilder addServerUrl(String ipUrl) {
            this.servers.add(ipUrl);
            return this;
        }

        public SingletonBuilder serverUrls(List<String> servers) {
            this.servers = servers;
            return this;
        }

        public SingletonBuilder addConverterFactory(Converter.Factory factory) {
            this.converterFactories.add(factory);
            return this;
        }

        public SingletonBuilder addCallFactory(CallAdapter.Factory factory) {
            this.adapterFactories.add(factory);
            return this;
        }

        public HttpUtil build() {
            if (checkNULL(this.baseUrl)) {
                throw new NullPointerException("BASE_URL can not be null");
            }
            if (converterFactories.size() == 0) {
                converterFactories.add(StringConverterFactory.create());
            }
            if (adapterFactories.size() == 0) {
                adapterFactories.add(RxJavaCallAdapterFactory.create());
            }
            if (client == null) {
                client = OkhttpProvidede.okHttpClient(appliactionContext, baseUrl, servers);
            }
            Retrofit.Builder builder = new Retrofit.Builder();

            for (Converter.Factory converterFactory : converterFactories) {
                builder.addConverterFactory(converterFactory);
            }
            for (CallAdapter.Factory adapterFactory : adapterFactories) {
                builder.addCallAdapterFactory(adapterFactory);
            }
            Retrofit retrofit = builder
                    .baseUrl(baseUrl + "/")
                    .client(client).build();

            RetrofitHttpService retrofitHttpService =
                    retrofit.create(RetrofitHttpService.class);

            mInstance = new HttpUtil(retrofitHttpService, appliactionContext, versionApi, paramsInterceptor);
            return mInstance;
        }
    }


    public static String V(String url) {
        if (checkNULL(mVersionApi)) {
            throw new NullPointerException("can not add VersionApi ,because of VersionApi is null");
        }
        if (!url.contains(mVersionApi)) {
            return mVersionApi + url;
        }
        return url;
    }


    public static Map<String, String> checkParams(Map<String, String> params) {
        if (params == null) {
            params = new HashMap<>();
        }
        if (mInstance.mParamsInterceptor != null) {
            params = mInstance.mParamsInterceptor.checkParams(params);
        }
        //retrofit的params的值不能爲null,此處做下校驗,防止出錯
        for (Map.Entry<String, String> entry : params.entrySet()) {
            if (entry.getValue() == null) {
                params.put(entry.getKey(), "");
            }
        }
        return params;
    }

    // 判斷是否NULL
    public static boolean checkNULL(String str) {
        return str == null || "null".equals(str) || "".equals(str);

    }

    // 判斷是否NULL
    public static void Error(Context context, String msg) {
        if (checkNULL(msg)) {
            msg = "似乎已斷開與互聯網連接";
        }
        Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
    }

    public static String message(String mes) {
        if (checkNULL(mes)) {
            mes = "似乎已斷開與互聯網連接";
        }

        if (mes.equals("timeout") || mes.equals("SSL handshake timed out")) {
            return "網絡請求超時";
        } else {
            return mes;
        }

    }

    static Map<String, Call> CALL_MAP = new HashMap<>();

    /*
    *添加某個請求
    *@author Administrator
    *@date 2016/10/12 11:00
    */
    private static synchronized void putCall(Object tag, String url, Call call) {
        if (tag == null)
            return;
        synchronized (CALL_MAP) {
            CALL_MAP.put(tag.toString() + url, call);
        }
    }

    /*
    *取消某個界面都所有請求,或者是取消某個tag的所有請求
    * 如果要取消某個tag單獨請求,tag需要轉入tag+url
    *@author Administrator
    *@date 2016/10/12 10:57
    */
    public static synchronized void cancel(Object tag) {
        if (tag == null)
            return;
        List<String> list = new ArrayList<>();
        synchronized (CALL_MAP) {
            for (String key : CALL_MAP.keySet()) {
                if (key.startsWith(tag.toString())) {
                    CALL_MAP.get(key).cancel();
                    list.add(key);
                }
            }
        }
        for (String s : list) {
            removeCall(s);
        }

    }

    /*
    *移除某個請求
    *@author Administrator
    *@date 2016/10/12 10:58
    */
    private static synchronized void removeCall(String url) {
        synchronized (CALL_MAP) {
            for (String key : CALL_MAP.keySet()) {
                if (key.contains(url)) {
                    url = key;
                    break;
                }
            }
            CALL_MAP.remove(url);
        }
    }

    public static class Builder {
        Map<String, String> params = new HashMap<>();
        String url;
        Error mErrorCallBack;
        Success mSuccessCallBack;
        String cacheTime;
        boolean addVersion = false;
        Object tag;

        public Builder CacheTime(String time) {
            this.cacheTime = time;
            return this;
        }

        public Builder Url(String url) {
            this.url = url;
            return this;
        }

        public Builder Tag(Object tag) {
            this.tag = tag;
            return this;
        }


        public Builder Params(Map<String, String> params) {
            this.params.putAll(params);
            return this;
        }

        public Builder Params(String key, String value) {
            this.params.put(key, value);
            return this;
        }

        public Builder Success(Success success) {
            this.mSuccessCallBack = success;
            return this;
        }

        public Builder Version() {
            this.addVersion = true;
            return this;
        }

        public Builder Error(Error error) {
            this.mErrorCallBack = error;
            return this;
        }

        public Builder() {
            this.setParams();
        }

        public Builder(String url) {
            this.setParams(url);
        }

        private void setParams() {
            this.setParams(null);
        }

        private void setParams(String url) {
            if (mInstance == null) {
                throw new NullPointerException("HttpUtil has not be initialized");
            }
            this.url = url;
            this.params = new HashMap<>();
            this.mErrorCallBack = (v) -> {
            };
            this.mSuccessCallBack = (s) -> {
            };
        }


        private String checkUrl(String url) {
            if (checkNULL(url)) {
                throw new NullPointerException("absolute url can not be empty");
            }
            if (addVersion) {
                url = mInstance.V(url);
            }
            return url;
        }

        public void get() {
            if (cacheTime == null) {
                cacheTime = "";
            }
            Call call = mService.get(checkUrl(this.url), checkParams(params), cacheTime);
            putCall(tag, url, call);
            call.enqueue(new Callback<String>() {
                @Override
                public void onResponse(Call<String> call, Response<String> response) {
                    if (response.code() == 200) {
                        mSuccessCallBack.Success(response.body().toString());
                    } else {
                        mErrorCallBack.Error(response.code(), message(response.message()), null);
                    }
                    if (tag != null)
                        removeCall(url);
                }

                @Override
                public void onFailure(Call<String> call, Throwable t) {
                    mErrorCallBack.Error(200, message(t.getMessage()), t);
                    if (tag != null)
                        removeCall(url);
                }
            });
        }

        public void post() {
            if (cacheTime == null) {
                cacheTime = "";
            }
            Call call = mService.post(checkUrl(this.url), checkParams(params), cacheTime);
            putCall(tag, url, call);
            call.enqueue(new Callback<String>() {
                @Override
                public void onResponse(Call<String> call, Response<String> response) {
                    if (response.code() == 200) {
                        mSuccessCallBack.Success(response.body().toString());
                    } else {
                        mErrorCallBack.Error(response.code(), message(response.message()), null);
                    }
                    if (tag != null)
                        removeCall(url);
                }

                @Override
                public void onFailure(Call<String> call, Throwable t) {
                    mErrorCallBack.Error(200, message(t.getMessage()), t);
                    if (tag != null)
                        removeCall(url);
                }
            });
        }

        public Observable<String> Obget() {
            this.url = checkUrl(this.url);
            this.params = checkParams(this.params);
            if (cacheTime == null) {
                cacheTime = "";
            }
            return mService.Obget(url, checkParams(params), cacheTime);
        }


        public Observable<String> Obpost() {
            this.url = checkUrl(this.url);
            this.params = checkParams(this.params);
            if (cacheTime == null) {
                cacheTime = "";
            }
            return mService.Obpost(url, checkParams(params), cacheTime);
        }

        /*
            *按基礎格式返回的數據進行預處理
            *只返回status爲true的情況下的data
            *@author Administrator
            *@date 2016/10/20 11:31
            */
        public Observable<String> getModelData(Context context) {
            return Obget()
                    .map(s -> new BaseModel(s))
                    .subscribeOn(Schedulers.newThread())
                    .observeOn(AndroidSchedulers.mainThread())
                    .filter(model -> model.trueStatus(context))//可能有toast操作,必須在主線程
                    .map(model -> model.data);
        }

        public Observable<String> postModelData(Context context) {
            return Obpost()
                    .map(s -> new BaseModel(s))
                    .subscribeOn(Schedulers.newThread())
                    .observeOn(AndroidSchedulers.mainThread())
                    .filter(model -> model.trueStatus(context))
                    .map(model -> model.data);
        }

    }
}

以上就是本次retrofit的封裝的完整實現了

完整的示例項目地址https://github.com/gengqiquan/HttpUtil.git

有什麼建議的可以留言喔

如果我的博客對您有幫助,請留言鼓勵下或者點個贊吧!

我建了一個QQ羣(羣號:121606151),用於大家討論交流Android技術問題,有興趣的可以加下,大家一起進步。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章