Retrofit使用中有關(guān)OkHttp的一些事

廢話部分

只是在工作上的一些無聊的事情,想閑扯的可以看下,不想的可以用直接看下一段的正題。
我是兩個(gè)月前換的一個(gè)工作,去的一個(gè)公司只有我一個(gè)人開發(fā),并且是從零開始,什么都沒有的情況下我還是去了,因?yàn)槲覀€(gè)人覺得這個(gè)機(jī)會(huì)不錯(cuò),以前工作了兩年都是copy別人的代碼或者維護(hù)別人的代碼。項(xiàng)目也是不倫不類的MVC,網(wǎng)絡(luò)請(qǐng)求、緩存、項(xiàng)目架構(gòu)現(xiàn)在看來都是糟的一塌糊涂(現(xiàn)在這么認(rèn)為,因?yàn)檫@兩個(gè)月自己感覺自己有了很多提高),不過當(dāng)時(shí)的我竟然那么安逸的選擇在那里一直待下去,很慶幸自己已經(jīng)出來了,不然面對(duì)現(xiàn)在這么激烈的競爭,我想我可能就被市場淘汰了。可能現(xiàn)在還有很多人在一個(gè)比較安逸的公司拿著不錯(cuò)的薪資一直沒有跳槽的想法,我只是給大家一個(gè)建議,如果你喜歡技術(shù),喜歡挑戰(zhàn),建議早些脫離這些公司,把每個(gè)公司當(dāng)成自己技術(shù)增長的跳板,不過不建議不到一年就換工作的,太頻繁了也不好,最起碼要把這個(gè)公司用到的技術(shù)全部學(xué)到了才能走,不然到最后還是很難學(xué)到東西。好了不廢話了。開始今天的正題了。

Retrofit??

現(xiàn)在Retrofit可能很多人都有了解,畢竟現(xiàn)在很多開發(fā)者社區(qū)到處可見的標(biāo)題都是Retrofit+RxJava+MVP這些文章,剛開始自己看這些的時(shí)候也有些懵,不過多研究一下,在項(xiàng)目中用一下就發(fā)現(xiàn)其實(shí)這些東西都不是很難,給開發(fā)者一些建議,別上來就去看一些開源項(xiàng)目的源碼,那樣你會(huì)一頭懵逼的,大家可以嘗試去了解應(yīng)用場景,嘗試在項(xiàng)目中或者自己寫個(gè)東西去實(shí)踐一下,然后再去看源碼。
Retrofit通俗的說就是一個(gè)HTTP請(qǐng)求的一個(gè)框架,通過一個(gè)Interface來定義api的實(shí)現(xiàn),通過注釋的方式進(jìn)行一些請(qǐng)求的設(shè)置。

public interface GitHubService 
{
    @GET("users/{user}/repos") 
    Call<List<Repo>> listRepos(@Path("user") String user);
}

就像上面的代碼一樣,所有的網(wǎng)絡(luò)請(qǐng)求接口都是定義在一個(gè)Interface中,然后在使用的時(shí)候通過Retrofit中的create()方法即可實(shí)現(xiàn),詳細(xì)的可以看
[Retrofit官網(wǎng)](http://square.github.io/retrofit/
[Retrofit api文檔](http://square.github.io/retrofit/2.x/retrofit/
有關(guān)Retrofit的在這里不多說,因?yàn)楸酒饕莵碚f在使用過程中有關(guān)OkHttp的事情,所以想了解Retrofit的可以去查詢其他的資料

Retrofit中OkHttp使用

大家都知道Retrofit中默認(rèn)使用的網(wǎng)絡(luò)請(qǐng)求方式是使用OkHttp的,先了解下OkHttp
OkHttp的官網(wǎng)介紹和Retrofit很像,也是一個(gè)基于Http的一個(gè)請(qǐng)求客戶端,畢竟Retrofit和OkHttp都是square的開源項(xiàng)目,下面來說下我在項(xiàng)目里面使用的時(shí)候有關(guān)okhttp的配置問題。
首先okhttp的確非常棒,在接入到項(xiàng)目里面使用的時(shí)候日志輸入非常詳細(xì),類似Content-type,header、參數(shù)返回值這些全部都很詳細(xì)的顯示,Okhttp最棒的一個(gè)設(shè)計(jì)就是整個(gè)網(wǎng)絡(luò)請(qǐng)求過程中我們可以用在任意一個(gè)地方去做一個(gè)攔截處理,我們可以通過實(shí)現(xiàn)提供的接口[Interceptor](http://square.github.io/okhttp/3.x/okhttp/okhttp3/Interceptor.html
下面我來舉個(gè)例子:
我在和我們后臺(tái)人員溝通的時(shí)候發(fā)現(xiàn)我們每個(gè)接口都有兩個(gè)共同的參數(shù) version和device,我們不可能在定義的時(shí)候每個(gè)接口里面都去添加上這兩個(gè)參數(shù),為了方便我們會(huì)在一個(gè)地方統(tǒng)一的去添加,那么我們就可以通過自定義一個(gè)Interceptor來實(shí)現(xiàn),具體代碼如下:

public class CommonParamsInterceptor implements Interceptor
{
    @Override
    public Response intercept(Chain chain) throws IOException
    {
        Request request = chain.request();
        //get請(qǐng)求后面追加共同的參數(shù)
        HttpUrl httpUrl = request.url().newBuilder()
                                 .addQueryParameter("version", ConstantConfig.APP_VERSION_CODE)
                                 .addQueryParameter("token", ConstantConfig.APP_TOKEN)
                                 .addQueryParameter("device", "Android").build();
        request = request.newBuilder().url(httpUrl).build();
        return chain.proceed(request);
    }
}

這樣就可以實(shí)現(xiàn)每個(gè)請(qǐng)求上面添加上參數(shù),不過有些事情總是比你想象的蛋疼一些,因?yàn)楹笈_(tái)接口是兩個(gè)人開發(fā)的,一個(gè)人告訴我所有的請(qǐng)求方法是get和post都支持,當(dāng)時(shí)這樣寫也沒發(fā)現(xiàn)什么問題,后臺(tái)和另一個(gè)人調(diào)試的時(shí)候發(fā)現(xiàn)不行,找了半天原因才發(fā)現(xiàn)原來上面的寫法是吧這些參數(shù)都放到了url的后面,也就是get請(qǐng)求的傳參方式,所以如果限定了必須使用post請(qǐng)求那么這個(gè)方法就不行了。唉。。。沒辦法,只能去修改,然后就是一番查找,終于解決了,代碼如下:

public class CommonParamsInterceptor implements Interceptor
{
    @Override
    public Response intercept(Chain chain) throws IOException
    {
        Request request = chain.request();
        //post請(qǐng)求追加參數(shù)
        FormBody.Builder newBody = new FormBody.Builder();
        newBody.add("version", ConstantConfig.APP_VERSION_CODE)
               .add("token", ConstantConfig.APP_TOKEN).add("device", "Android").build();
        //攔截請(qǐng)求中用戶傳入的數(shù)據(jù),把參數(shù)遍歷放入新的body里面,然后進(jìn)行一塊提交
        FormBody oldBody = (FormBody) request.body();
        for (int i = 0; i < oldBody.size(); i++)
        {
            newBody.add(oldBody.encodedName(i), oldBody.encodedValue(i));
        }
        request = request.newBuilder().post(newBody.build()).build();
        chain.proceed(request);
    }
}

這樣就實(shí)現(xiàn)了表單數(shù)據(jù)的提交,get和post的設(shè)置共同參數(shù)的方式。從這一個(gè)例子就可以看出使用okhttp的時(shí)候我們可以在每一個(gè)步驟進(jìn)行攔截處理,比如設(shè)置緩存、頭部信息等等。
自定義的Interceptor可以在初始化okhttp的時(shí)候設(shè)置,如:

 /**
     * OkHttp的初始化,設(shè)置緩存目錄,設(shè)置請(qǐng)求最大時(shí)間,請(qǐng)求失敗重連
     */
    public void initOkHttp()
    {
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        CustomParamsInterceptor paramsInterceptor = new CustomParamsInterceptor();
        builder.addInterceptor(paramsInterceptor);
        if (BuildConfig.DEBUG)
        {
            HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
            loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
            builder.addInterceptor(loggingInterceptor);
        }
        // 緩存 http://www.itdecent.cn/p/93153b34310e
        File cacheFile = new File(ConstantConfig.CACHE_DIR, "/NetCache");
        Cache cache = new Cache(cacheFile, 1024 * 1024 * 50);
        Interceptor cacheInterceptor = new Interceptor()
        {
            @Override
            public Response intercept(Chain chain) throws IOException
            {
                Request request = chain.request();
                if (!Util.isNetworkConnected(App.getmAppContext()))
                {
                    request = request.newBuilder().cacheControl(CacheControl.FORCE_CACHE).build();
                }
                Response response = chain.proceed(request);
                if (Util.isNetworkConnected(App.getmAppContext()))
                {
                    int maxAge = 0;
                    // 有網(wǎng)絡(luò)時(shí) 設(shè)置緩存超時(shí)時(shí)間0個(gè)小時(shí)
                    response.newBuilder().header("Cache-Control", "public, max-age=" + maxAge)
                            .build();
                } else
                {
                    // 無網(wǎng)絡(luò)時(shí),設(shè)置超時(shí)為4周
                    int maxStale = 60 * 60 * 24 * 28;
                    response.newBuilder().header("Cache-Control",
                                                 "public, only-if-cached, max-stale=" + maxStale)
                            .build();
                }
                return response;
            }
        };
        builder.cache(cache).addInterceptor(cacheInterceptor);
        //設(shè)置超時(shí)
        builder.connectTimeout(15, TimeUnit.SECONDS);
        builder.readTimeout(20, TimeUnit.SECONDS);
        builder.writeTimeout(20, TimeUnit.SECONDS);
        //錯(cuò)誤重連
        builder.retryOnConnectionFailure(true);
        okHttpClient = builder.build();
    }

okhttp很方便,很靈活,我們可以隨意的折騰。不過,話說,最氣人的是因?yàn)閕os那邊的問題,接口竟然要求我參數(shù)的傳遞用json格式。

這里為大家說下數(shù)據(jù)傳輸格式和數(shù)據(jù)格式是兩個(gè)不同的東西,我們一般認(rèn)為數(shù)據(jù)格式使我們手機(jī)在接收接口api返回的數(shù)據(jù)是一個(gè)json的String串,但是和后臺(tái)接口傳遞數(shù)據(jù)的格式有以下幾種:

  • application/xhtml+xml :XHTML格式
  • application/xml : XML數(shù)據(jù)格式
  • application/atom+xml :Atom XML聚合格式
  • application/json : JSON數(shù)據(jù)格式
  • application/pdf :pdf格式
  • application/msword : Word文檔格式
  • application/octet-stream : 二進(jìn)制流數(shù)據(jù)(如常見的文件下載)
  • application/x-www-form-urlencoded : <form encType=””>中默認(rèn)的encType,form表單數(shù)據(jù)被編碼為key/value格式發(fā)送到服務(wù)器(表單默認(rèn)的提交數(shù)據(jù)的格式)

我們?cè)诓辉O(shè)置默認(rèn)的情況下http請(qǐng)求的數(shù)據(jù)傳輸格式是最后一個(gè)application/x-www-form-urlencoded ,這個(gè)設(shè)置是一key value的形式傳遞的參數(shù),但是我們的后臺(tái)要求我使用application/json的格式去傳遞數(shù)據(jù),唉。。。。那以上兩種方法就不可行了,沒辦法,接著一番,最終也是搞出來了,代碼如下:

public class CustomParamsInterceptor implements Interceptor
{
    private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");

    @Override
    public Response intercept(Chain chain) throws IOException
    {
        Request request = chain.request();
        ArrayMap<String, String> paramsMap = new ArrayMap<String, String>();
        paramsMap.put("version", "1.0");
        paramsMap.put("token", "");
        paramsMap.put("device", "Android");
        if (request.body() instanceof FormBody)
        {
            FormBody oldBody = (FormBody) request.body();
            for (int i = 0; i < oldBody.size(); i++)
            {
                paramsMap.put(oldBody.encodedName(i), oldBody.encodedValue(i));
            }
        }
        Gson gson = new Gson();
        AppLog.i("Gson參數(shù)格式---" + gson.toJson(paramsMap));

        RequestBody body = RequestBody.create(JSON, gson.toJson(paramsMap));
        request = request.newBuilder().post(body).build();
        return chain.proceed(request);
    }

}

這就是我今天所有的經(jīng)歷了,我每天都會(huì)寫一篇關(guān)于在開發(fā)中遇到的問題和學(xué)到的技術(shù)點(diǎn),如果有興趣的可以關(guān)注,雖說現(xiàn)在還是個(gè)小白,不過努力總不會(huì)白費(fèi)的,加油吧騷年們。
我的項(xiàng)目使用了MVP+RxJava+Retrofit 很多東西都還在持續(xù)的完善中,如果有興趣的大家可以一起討論。感謝大家的閱讀。

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

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,872評(píng)論 25 709
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,533評(píng)論 19 139
  • 國家電網(wǎng)公司企業(yè)標(biāo)準(zhǔn)(Q/GDW)- 面向?qū)ο蟮挠秒娦畔?shù)據(jù)交換協(xié)議 - 報(bào)批稿:20170802 前言: 排版 ...
    庭說閱讀 12,332評(píng)論 6 13
  • 阿喪來我家快有一個(gè)月了。他第一次出現(xiàn)在院子里時(shí),著實(shí)把大家嚇了一跳。手臂上的傷口已經(jīng)腐爛,可怖的青筋爬到了太陽穴,...
    子愈閱讀 668評(píng)論 2 2
  • 失敗的人,往往有十誤 一聰明誤 有些人失敗,是因?yàn)椴粔蚵斆?。有些人失敗,則是因?yàn)樘斆?。后一種人的失敗,就叫聰明誤...
    酷聊析曼閱讀 470評(píng)論 0 0

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