Retrofit(OKHttp)多BaseUrl情況下url實時自動替換完美解決方法

前言

多數(shù)項目中會出現(xiàn)用到多個BaseUrl的情況低剔,而Retrofit未提供實時切換BaseUrl的方法当窗,且我們在使用Retrofit以及OkHttp時,通常使用單例模式創(chuàng)建Retrofit和OkHttpClient喷斋,也不可能每個請求都創(chuàng)建一個Retrofit實例庸推。面對這個實際問題,我們需要做的不僅僅是解決當(dāng)前問題洋机,更應(yīng)該從根本上解決該問題坠宴,即實現(xiàn)任何情況下的自動切換BaseUrl。 主要用于個人積累及分享绷旗,如有錯誤請隨時指出喜鼓,文中可能引用其他大牛文章(僅引用鏈接不轉(zhuǎn)載),如有侵權(quán)請告知必妥善處理衔肢。

思路分析

問題
一個例子颠通,給出資源如下:

`//賬戶服地址`

`String base_url_user =` `"[https://www.111.com/](https://www.111.com/)"`

`//支付服地址`

`String base_url_pay =` `"[https://www.222.com/](https://www.222.com/)"`

`//**********賬戶接口方法(使用賬戶服地址base_url_user)`

`String method_path_user_01 =` `"user/login"``;`

`String method_path_user_02 =` `"user/register"``;`

`//**********支付接口方法(使用支付服地址base_url_pay)`

`String method_path_pay_01 =` `"pay/getorder"``;`

`String method_path_pay_02 =` `"pay/payment"``;`

通常情況下,RetrofitService.java中膀懈,我們這樣寫:

@POST("user/login")
Observable<jsonobject> login(@QueryMap
Map<string, object=""> paramMap);

@POST("user/register")
Observable<jsonobject> register(@QueryMap
Map<string, object=""> paramMap);

@POST("pay/getorder")
Observable<jsonobject> getOrder(@QueryMap
Map<string, object=""> paramMap);

@POST("pay/payment")
Observable<jsonobject> payment(@QueryMap
Map<string, object=""> paramMap);

RetrofitHelper.java中這樣定義:

package com.mooc.ppjoke.ui.publish;
 public  class RetrofitHelper {
        private  static final String BASE_URL_USER = "https://www.111.com/";
        private static  final String BASE_URL_PAY = "https://www.222.com/";
         private static  final long TIME_OUT = 5000;
        private RetrofitService retrofitService;
        public static RetrofitHelper getInstance() {
            return SingleHolder.INSTANCE;
        }
        
       private  static class SingleHolder {
        private    static final RetrofitHelper INSTANCE = new
                    RetrofitHelper();

        }
        private RetrofitHelper() {
            OkHttpClient okHttpClient = new
                    OkHttpClient
                             .Builder()
                             .connectTimeout(TIME_OUT, TimeUnit.MILLISECONDS)
                             .build();
            Retrofit retrofit = new
                    Retrofit.Builder()
                    .client(okHttpClient)
                    .baseUrl(BASE_URL_USER)
                    .addConverterFactory(GsonConverterFactory.create())
                    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                    .build();
                    retrofitService = retrofit.create(RetrofitService.class);
        }



    ......

    }

如上顿锰,當(dāng)有多個BaseUrl時,Retrofit并沒有提供切換BaseUrl的方法启搂。若是每一個請求均build一個Retrofit硼控,那么單例也就沒什么意義了;若通過在RetrofitService.java的@POST(…)注解中寫死整個url的方式胳赌,也缺乏靈活性牢撼,一旦項目升級再升級,接口數(shù)增多疑苫,或接口有變化時熏版,會特別混亂。

思路

要實現(xiàn)靈活的配置BaseUrl捍掺,首先需要了解OkHttpClient的應(yīng)用攔截器Interceptor撼短。應(yīng)用攔截器Interceptor是當(dāng)前app和okhttp之間的中間層,主要用于截獲app向okhttp的request請求挺勿,并可在回調(diào)中進(jìn)行自定義修改曲横、重置等(另外還有網(wǎng)絡(luò)攔截器NetworkInterceptor,相當(dāng)于okhttp和network之間的中間層,用于截獲okhttp和network之間的請求和響應(yīng))禾嫉。Interceptor通過如下方式添加

OkHttpClient okHttpClient = new
OkHttpClient.Builder()
                            ......
                            .addInterceptor(new Interceptor() {
                                @Override
                       public Response intercept(Chain chain) throws IOException {
                                    //獲取請求request
                                    Request request = chain.request();
                                    //獲取請求request的創(chuàng)建者builder實例
                                    Request.Builder builder = request.newBuilder();
                                    //通過builder.xxxx.build()修改相關(guān)配置等灾杰,并調(diào)用
                                    //chain.proceed(Request request)返回響應(yīng)response
                                    return
                                   chain.proceed(builder.xxxx.build());
                                }
                            })
                            .build();

上面代碼給我們提供了一個機(jī)會,在okhttp向network請求之前熙参,我們可以很方便的替換請求的內(nèi)容艳吠,只需要再修改builder后,重新build就會生成新的request實例孽椰。

現(xiàn)在已經(jīng)知道了如何在請求network之前修改請求讲竿,那么,剩下需要考慮的問題就是弄屡,我們?nèi)绾闻袛喈?dāng)前的request需要哪一個BaseUrl?
這里我們可以考慮使用@Headers({“xxx_key:yyy_value”})鞋诗,當(dāng)在RetrofitService.java的接口中配置了這個標(biāo)簽膀捷,我們就可以在Interceptor中獲取當(dāng)前請求的頭,并根據(jù)你配置的維一對應(yīng)的yyy_value削彬,來匹配當(dāng)前所需使用的BaseUrl全庸,然后將原有的url替換掉,當(dāng)okhttp向network請求時融痛,就是最新的url了壶笼。

解決

以下將做簡單示例,你可以在項目中根據(jù)實際情況做優(yōu)化雁刷。還是上面的例子覆劈,我們?nèi)缦绿幚砑纯桑?br> RetrofitService.java中

//添加Headers:使用同樣的鍵url_name;使用不同值user或pay沛励,對應(yīng)賬戶服地址和支付服地址责语。下同。
@Headers({"url_name:user"})
@POST("user/login")
Observable<jsonobject> login(@QueryMap
Map<string, object=""> paramMap);
@Headers({"url_name:user"})
@POST("user/register")
Observable<jsonobject> register(@QueryMap
Map<string, object=""> paramMap);
@Headers({"url_name:pay"})
@POST("pay/getorder")
Observable<jsonobject> getOrder(@QueryMap
Map<string, object=""> paramMap);
@Headers({"url_name:pay"})
@POST("pay/payment")
Observable<jsonobject> payment(@QueryMap
Map<string, object=""> paramMap);

RetrofitHelper.java中:



public
class RetrofitHelper {
    private static final String BASE_URL_USER = "https://www.111.com/";
    private static final String BASE_URL_PAY = "https://www.222.com/";
    private static final long TIME_OUT = 5000;
    private RetrofitService retrofitService;

    public
    static RetrofitHelper getInstance() {
        return SingleHolder.INSTANCE;
    }

    private
    static class SingleHolder {
        private
        static final RetrofitHelper INSTANCE = new
                RetrofitHelper();
    }

    private RetrofitHelper() {
        OkHttpClient okHttpClient = new
                OkHttpClient.Builder()
                .connectTimeout(TIME_OUT, TimeUnit.MILLISECONDS)
                //添加應(yīng)用攔截器
                .addInterceptor(new Interceptor() {
                    @Override
                    public Response intercept(Chain chain) throws IOException {
                        //獲取request
                        Request request = chain.request();
                        //獲取request的創(chuàng)建者builder
                        Request.Builder builder = request.newBuilder();
                        //從request中獲取headers目派,通過給定的鍵url_name
                        List<string> headerValues = request.headers("url_name");
                        if
                        (headerValues != null
                                && headerValues.size() > 0) {
                            //如果有這個header坤候,先將配置的header刪除,因此header僅用作app和okhttp之間使用
                            builder.removeHeader(HttpConfig.HEADER_KEY);
                            //匹配獲得新的BaseUrl
                            String headerValue = headerValues.get(0);
                            HttpUrl newBaseUrl = null;
                            if
                            ("user".equals(headerValue)) {
                                newBaseUrl = HttpUrl.parse(BASE_URL_USER);
                            } else if ("pay".equals(headerValue)) {
                                newBaseUrl = HttpUrl.parse(BASE_URL_PAY);
                            } else {
                                newBaseUrl = oldHttpUrl;
                            }
                            //從request中獲取原有的HttpUrl實例oldHttpUrl
                            HttpUrl oldHttpUrl = request.url();
                            //重建新的HttpUrl企蹭,修改需要修改的url部分
                            HttpUrl newFullUrl = oldHttpUrl
                                    .newBuilder()
                                    .scheme(newBaseUrl.scheme())
                                    .host(newBaseUrl.host())
                                    .port(newBaseUrl.port())
                                    .build();
                            //重建這個request白筹,通過builder.url(newFullUrl).build();
                            //然后返回一個response至此結(jié)束修改
                            return
                                    chain.proceed(builder.url(newFullUrl).build());
                        } else {
                            return
                                    chain.proceed(request);

                        }

                    }

                })

                .build();

        Retrofit retrofit = new
                Retrofit
                        .Builder()
                .client(okHttpClient)
                //創(chuàng)建retrofit時的baseUrl可以不需擔(dān)心谅摄、隨意指定了
                .baseUrl(BASE_URL_USER)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .build();
        retrofitService = retrofit.create(RetrofitService.class);

    }
    ......
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末徒河,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子送漠,更是在濱河造成了極大的恐慌虚青,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件螺男,死亡現(xiàn)場離奇詭異棒厘,居然都是意外死亡纵穿,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進(jìn)店門奢人,熙熙樓的掌柜王于貴愁眉苦臉地迎上來谓媒,“玉大人,你說我怎么就攤上這事何乎【涔撸” “怎么了?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵支救,是天一觀的道長抢野。 經(jīng)常有香客問我,道長各墨,這世上最難降的妖魔是什么指孤? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮贬堵,結(jié)果婚禮上恃轩,老公的妹妹穿的比我還像新娘。我一直安慰自己黎做,他們只是感情好叉跛,可當(dāng)我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蒸殿,像睡著了一般筷厘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上宏所,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天敞掘,我揣著相機(jī)與錄音,去河邊找鬼楣铁。 笑死玖雁,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的盖腕。 我是一名探鬼主播赫冬,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼溃列!你這毒婦竟也來了劲厌?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤听隐,失蹤者是張志新(化名)和其女友劉穎补鼻,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡风范,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年咨跌,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片硼婿。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡锌半,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出寇漫,到底是詐尸還是另有隱情刊殉,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布州胳,位于F島的核電站记焊,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏栓撞。R本人自食惡果不足惜遍膜,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望腐缤。 院中可真熱鬧,春花似錦肛响、人聲如沸岭粤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽剃浇。三九已至,卻和暖如春猎物,著一層夾襖步出監(jiān)牢的瞬間虎囚,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工蔫磨, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留淘讥,地道東北人。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓堤如,卻偏偏與公主長得像蒲列,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子搀罢,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,834評論 2 345