Retrofit 框架源碼學(xué)習(xí)

Retrofit黍聂,OkHttp,Okio Square 安卓平臺(tái)網(wǎng)絡(luò)層三板斧源碼學(xué)習(xí)
基于 retrofit 2.4.0-SNAPSHOT 版本 retrofit github 地址

Retrofit 是 Square 安卓平臺(tái)網(wǎng)絡(luò)層三板斧最后一個(gè)項(xiàng)目范咨,Retrofit 依賴(lài) OkHttp 稠屠。Retrofit 讓 http 網(wǎng)絡(luò)請(qǐng)求更加清晰。

使用方式

  1. 聲明一個(gè)接口质礼,并用接口描述 request

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

    方法上面的注釋表示 request 的接口名 ,方法的返回類(lèi)型就是 http 請(qǐng)求的返回值市栗,方法的參數(shù)就是 http 的請(qǐng)求參數(shù)缀拭。

  2. 創(chuàng)建一個(gè) Retrofit 客戶(hù)端

     Retrofit retrofit = new Retrofit.Builder()
     .baseUrl("https://api.github.com/")
     .build();
    
     GitHubService service = retrofit.create(GitHubService.class);
    

    Retrofit 創(chuàng)建的時(shí)候指定了 request 的接口地址,然后調(diào)用 retrofit.create 方法創(chuàng)建一個(gè) GitHubService 實(shí)例填帽。

  3. 發(fā)起網(wǎng)絡(luò)請(qǐng)求

     Call<List<Repo>> repos = service.listRepos("octocat");
     repos.execute().body()
    

Retrofit 創(chuàng)建 Service 實(shí)例

上面的例子可以看到蛛淋,retrofit.create() 方法會(huì)創(chuàng)建一個(gè) GitHubService 實(shí)例,但是 GitHubService 本身是一個(gè)接口篡腌。為了了解 retrofit.create() 方法褐荷,我們先看下 Retrofit 的創(chuàng)建過(guò)程。

創(chuàng)建 Retrofit 對(duì)象嘹悼。

Retrofit 和 OkHttp 一樣都是使用構(gòu)建者模式創(chuàng)建對(duì)象叛甫。先看下 Retrofit.Builder 的 build() 方法层宫。

    public Retrofit build() {
        if (baseUrl == null) {
            throw new IllegalStateException("Base URL required.");
        }

        okhttp3.Call.Factory callFactory = this.callFactory;
        if (callFactory == null) {
            callFactory = new OkHttpClient();
        }

        Executor callbackExecutor = this.callbackExecutor;
        if (callbackExecutor == null) {
            callbackExecutor = platform.defaultCallbackExecutor();
        }

        // Make a defensive copy of the adapters and add the default Call adapter.
        List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
        adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

        // Make a defensive copy of the converters.
        List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

        return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
                callbackExecutor, validateEagerly);
    }

創(chuàng)建 Retrofit 的時(shí)候需要傳遞一下幾個(gè)參數(shù)

1. callFactory 用來(lái)創(chuàng)建一個(gè)實(shí)現(xiàn)了 okhttp3.Call.Factory 的對(duì)象,如果沒(méi)有設(shè)置其监,默認(rèn)為 OkHttpClient萌腿。
2. baseUrl 網(wǎng)絡(luò)接口的地址。
3. converterFactories 用來(lái)把服務(wù)器返回的數(shù)據(jù)轉(zhuǎn)換為對(duì)象抖苦。
4. adapterFactories 用來(lái)發(fā)起網(wǎng)絡(luò)請(qǐng)求毁菱。
5. callbackExecutor 是一個(gè)調(diào)度器,用來(lái)接收返回的數(shù)據(jù)锌历,在 Android 上默認(rèn)是封裝了 handler 的 MainThreadExecutor
6. validateEagerly  是一個(gè)開(kāi)關(guān)鼎俘,如果為 true 會(huì)緩存創(chuàng)建的 ServiceMethod 。

retrofit.create()

public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
        eagerlyValidateMethods(service);
    }
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[]{service},
            new InvocationHandler() {
                private final Platform platform = Platform.get();

                @Override
                public Object invoke(Object proxy, Method method, @Nullable Object[] args)
                        throws Throwable {
                    // If the method is a method from Object then defer to normal invocation.
                    if (method.getDeclaringClass() == Object.class) {
                        return method.invoke(this, args);
                    }
                    if (platform.isDefaultMethod(method)) {
                        return platform.invokeDefaultMethod(method, service, proxy, args);
                    }
                    ServiceMethod<Object, Object> serviceMethod =
                            (ServiceMethod<Object, Object>) loadServiceMethod(method);
                    OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
                    return serviceMethod.callAdapter.adapt(okHttpCall);
                }
            });
}

這里用到了一個(gè)公共技術(shù)點(diǎn)之 Java 動(dòng)態(tài)代理辩涝,create 方法傳入一個(gè) Class ,這個(gè) Class 對(duì)象就是上文的 GitHubService 的 Class 勘天。

GitHubService 的方法是由 InvocationHandler 代理實(shí)現(xiàn)的怔揩,重點(diǎn)看三行代碼

……
ServiceMethod<Object, Object> serviceMethod =(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
第一行 loadServiceMethod(method)
ServiceMethod<?, ?> loadServiceMethod(Method method) {
    ServiceMethod<?, ?> result = serviceMethodCache.get(method);
    if (result != null) return result;

    synchronized (serviceMethodCache) {
        result = serviceMethodCache.get(method);
        if (result == null) {
            result = new ServiceMethod.Builder<>(this, method).build();
            serviceMethodCache.put(method, result);
        }
    }
    return result;
}

這里創(chuàng)建了一個(gè) ServiceMethod 對(duì)象。

第二行 new OkHttpCall<>(serviceMethod, args)
OkHttpCall(ServiceMethod<T, ?> serviceMethod, @Nullable Object[] args) {
    this.serviceMethod = serviceMethod;
    this.args = args;
}

創(chuàng)建了一個(gè) OkHttpCall 脯丝,serviceMethod 和 args 是 OkHttpCall 的成員函數(shù)商膊。

所以,

第三行 serviceMethod.callAdapter.adapt(okHttpCall)

這里需要明白 serviceMethod.callAdapter 是怎么來(lái)的

1. 在 ServiceMethod.Builder.build() 中調(diào)用 createCallAdapter()
2. 在 createCallAdapter() 中會(huì)找到 
   (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations)
3. 在 callAdapter() 中調(diào)用 nextCallAdapter
4. nextCallAdapter 會(huì)遍歷 adapterFactories 返回一個(gè) CallAdapter宠进。

這里再回頭看下 adapterFactories Retrofit.Builder.build() 方法中

List<CallAdapter.Factory> adapterFactories = 
    new ArrayList<>(this.adapterFactories);
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

而在 Retrofit.nextCallAdapter() 中

int start = adapterFactories.indexOf(skipPast) + 1;
for (int i = start, count = adapterFactories.size(); i < count; i++) {
   CallAdapter<?, ?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
    if (adapter != null) {
        return adapter;
    }
}

如果沒(méi)有設(shè)置 AdapterFactory 將會(huì)使用一個(gè)默認(rèn)的 AdapterFactory

CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
    if (callbackExecutor != null) {
        return new ExecutorCallAdapterFactory(callbackExecutor);
    }
    return DefaultCallAdapterFactory.INSTANCE;
}

所以如果我們?cè)O(shè)置了一個(gè) RxJavaCallAdapterFactory晕拆,就會(huì)返回 RxJavaCallAdapterFactory。

retrofit_01.png

發(fā)起網(wǎng)絡(luò)請(qǐng)求

通過(guò) retrofit.create() 我們可以知道材蹬,retrofit.create() 返回的是一個(gè)代理對(duì)象InvocationHandler 实幕,那么在執(zhí)行

Call<List<Repo>> repos = service.listRepos("octocat");

方法時(shí),調(diào)用的實(shí)際上是 callAdapter.adapt(okHttpCall)堤器,以 DefaultCallAdapterFactory 為例

@Override
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != Call.class) {
        return null;
    }

    final Type responseType = Utils.getCallResponseType(returnType);
    return new CallAdapter<Object, Call<?>>() {
        @Override
        public Type responseType() {
            return responseType;
        }

        @Override
        public Call<Object> adapt(Call<Object> call) {
            return call;
        }
    };
}

結(jié)合 retrofit.create() 方法可以得知這里返回的是一個(gè) OkHttpCall 對(duì)象昆庇。

接下來(lái)使用 OkHttpCall.execute() 或者異步執(zhí)行 enqueue(Callback<T> callback)

這兩種方式都會(huì)調(diào)用 createRawCall() 創(chuàng)建一個(gè) okhttp3.Call

private okhttp3.Call createRawCall() throws IOException {
    Request request = serviceMethod.toRequest(args);
    okhttp3.Call call = serviceMethod.callFactory.newCall(request);
    if (call == null) {
        throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
}

此處的 serviceMethod.callFactory 就是 retrofit.create() 中創(chuàng)建的 OkHttpClient()
后面的內(nèi)容都是由 Okhttp 模塊接管,進(jìn)行網(wǎng)絡(luò)請(qǐng)求闸溃,參考okHttp 框架源碼學(xué)習(xí)

然后調(diào)用 parseResponse(call.execute())

Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
    ResponseBody rawBody = rawResponse.body();

    ……
    省略一些 http 返回值處理邏輯
    ……
    try {
        T body = serviceMethod.toResponse(catchingBody);
        return Response.success(body, rawResponse);
    } ……
}

okHttp 請(qǐng)求網(wǎng)絡(luò)的返回?cái)?shù)據(jù)整吆,會(huì)交給 serviceMethod.toResponse

R toResponse(ResponseBody body) throws IOException {
    return responseConverter.convert(body);
}

在 ServiceMethod.Builder.build() 方法中可以找到 responseConverter 是通過(guò) createResponseConverter() 方法的返回對(duì)象。

createResponseConverter() 只是報(bào)包裹了 retrofit.responseBodyConverter(responseType, annotations) 方法辉川。

retrofit.responseBodyConverter() 繼續(xù)跟蹤下去會(huì)得知表蝙,返回的是 converterFactories 數(shù)組的第 0 個(gè)對(duì)象,也就是內(nèi)置的 BuiltInConverters.responseBodyConverter() 方法返回的 BufferingResponseBodyConverter

static final class BufferingResponseBodyConverter
        implements Converter<ResponseBody, ResponseBody> {
    static final BufferingResponseBodyConverter INSTANCE = new BufferingResponseBodyConverter();

    @Override
    public ResponseBody convert(ResponseBody value) throws IOException {
        try {
            // Buffer the entire body to avoid future I/O.
            return Utils.buffer(value);
        } finally {
            value.close();
        }
    }
}

再看一下 Utils.buffer(value)

static ResponseBody buffer(final ResponseBody body) throws IOException {
    Buffer buffer = new Buffer();
    body.source().readAll(buffer);
    return ResponseBody.create(body.contentType(), body.contentLength(), buffer);
}

最終會(huì)返回一個(gè)重新封裝的 Okhttp 框架的 ResponseBody 對(duì)象乓旗。

retrofit_02.png

參考資料

Retrofit 官方文檔

retrofit github 地址

拆輪子系列:拆 Retrofit

Android:手把手帶你深入剖析 Retrofit 2.0 源碼

Retrofit分析-經(jīng)典設(shè)計(jì)模式案例

Retrofit分析-漂亮的解耦套路

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末府蛇,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子寸齐,更是在濱河造成了極大的恐慌欲诺,老刑警劉巖抄谐,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異扰法,居然都是意外死亡蛹含,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)塞颁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)浦箱,“玉大人,你說(shuō)我怎么就攤上這事祠锣】峥” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵伴网,是天一觀的道長(zhǎng)蓬推。 經(jīng)常有香客問(wèn)我,道長(zhǎng)澡腾,這世上最難降的妖魔是什么沸伏? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮动分,結(jié)果婚禮上毅糟,老公的妹妹穿的比我還像新娘。我一直安慰自己澜公,他們只是感情好姆另,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著坟乾,像睡著了一般迹辐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上糊渊,一...
    開(kāi)封第一講書(shū)人閱讀 51,754評(píng)論 1 307
  • 那天右核,我揣著相機(jī)與錄音,去河邊找鬼渺绒。 笑死贺喝,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的宗兼。 我是一名探鬼主播躏鱼,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼殷绍!你這毒婦竟也來(lái)了染苛?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎茶行,沒(méi)想到半個(gè)月后躯概,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡畔师,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年娶靡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片看锉。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡姿锭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出伯铣,到底是詐尸還是另有隱情呻此,我是刑警寧澤,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布腔寡,位于F島的核電站焚鲜,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏放前。R本人自食惡果不足惜恃泪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望犀斋。 院中可真熱鬧,春花似錦情连、人聲如沸叽粹。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)虫几。三九已至,卻和暖如春挽拔,著一層夾襖步出監(jiān)牢的瞬間辆脸,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工螃诅, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留啡氢,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓术裸,卻偏偏與公主長(zhǎng)得像倘是,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子袭艺,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

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