Retrofit網(wǎng)絡(luò)請(qǐng)求和響應(yīng)處理重點(diǎn)分析講解
在上一篇文章中,我們?cè)敿?xì)分析了 Retrofit 中的注解解析和動(dòng)態(tài)代理實(shí)現(xiàn),本篇文章將繼續(xù)深入研究 Retrofit 的核心源碼,重點(diǎn)分析 Retrofit 如何進(jìn)行網(wǎng)絡(luò)請(qǐng)求和響應(yīng)處理。
網(wǎng)絡(luò)請(qǐng)求
在使用 Retrofit 發(fā)起網(wǎng)絡(luò)請(qǐng)求時(shí),我們可以通過(guò)定義一個(gè)接口并使用 Retrofit 的注解來(lái)描述這個(gè)接口中的請(qǐng)求,Retrofit 會(huì)自動(dòng)生成一個(gè)實(shí)現(xiàn)該接口的代理對(duì)象。當(dāng)我們調(diào)用這個(gè)代理對(duì)象的方法時(shí),Retrofit 會(huì)根據(jù)注解的描述構(gòu)建一個(gè) Request 對(duì)象,并使用 OkHttp 將這個(gè) Request 發(fā)送出去。
在 Retrofit 中,我們可以通過(guò) Retrofit#execute
或 Retrofit#enqueue
方法來(lái)發(fā)送請(qǐng)求。這兩個(gè)方法的區(qū)別在于,execute
方法會(huì)阻塞當(dāng)前線程直到請(qǐng)求完成,而 enqueue
方法會(huì)將請(qǐng)求加入到 OkHttp 的請(qǐng)求隊(duì)列中,并在請(qǐng)求完成時(shí)通過(guò)回調(diào)通知我們。
我們先來(lái)看一下 execute
方法的實(shí)現(xiàn):
public <T> T execute(Call<T> call) throws IOException { Utils.validateServiceInterface(call.request().tag(), call.request().url().toString()); return (T) callAdapter(call, call.request().tag()).adapt(call).execute(); }
在這個(gè)方法中,首先會(huì)對(duì)接口進(jìn)行校驗(yàn),確保這個(gè)接口是有效的。然后我們會(huì)根據(jù)請(qǐng)求的 Tag 和 URL 來(lái)獲取適配器callAdapter
,并使用適配器來(lái)執(zhí)行請(qǐng)求。
適配器的作用是將請(qǐng)求的參數(shù)適配成 OkHttp 能夠識(shí)別的形式,并將 OkHttp 的響應(yīng)適配成我們需要的形式。Retrofit 提供了一系列的適配器,包括 Call 適配器、RxJava 適配器、CompletableFuture 適配器等。
我們來(lái)看一下 callAdapter
方法的實(shí)現(xiàn):
private CallAdapter<?, ?> callAdapter(Call<?> call, Object tag) { Type responseType = call.request().method().equals("HEAD") ? Void.class : getParameterUpperBound(0, (ParameterizedType) call.request().tag()); return callAdapter(tag, responseType); }
在這個(gè)方法中,我們首先根據(jù)請(qǐng)求的方法來(lái)判斷響應(yīng)的類型,如果是 HEAD 方法,那么響應(yīng)的類型就是 Void;否則我們會(huì)通過(guò)反射來(lái)獲取請(qǐng)求的響應(yīng)類型,并使用這個(gè)響應(yīng)類型來(lái)獲取適配器。
獲取適配器的方法是 callAdapter
:
public <R, T> CallAdapter<R, T> callAdapter(Object tag, Type returnType) { // ... for (CallAdapter.Factory factory : adapterFactories) { CallAdapter<?, ?> adapter = factory.get(returnType, annotations, this); if (adapter != null) { return (CallAdapter<R, T>) adapter; } } // ... }
在這個(gè)方法中,我們會(huì)遍歷所有的適配器工廠,嘗試獲取適配器。在獲取適配器時(shí),我們會(huì)將請(qǐng)求的響應(yīng)類型、注解和 Retrofit 實(shí)例作為參數(shù)傳入。每個(gè)適配器工廠都會(huì)判斷這些參數(shù)是否符合自己的適配條件,如果符合,就返回一個(gè)適配器實(shí)例,否則返回 null。在遍歷完所有的適配器工廠之后,如果還沒(méi)有獲取到適配器,那么就會(huì)拋出一個(gè)異常。
獲取到適配器之后,我們就可以使用適配器來(lái)執(zhí)行請(qǐng)求了。在適配器中,我們會(huì)將請(qǐng)求參數(shù)轉(zhuǎn)換成 OkHttp 的 Request 對(duì)象,并將 OkHttp 的 Response 對(duì)象轉(zhuǎn)換成我們需要的響應(yīng)類型。具體的實(shí)現(xiàn)可以參考 Retrofit 提供的 CallAdapter
接口。
對(duì)于 enqueue
方法,我們可以先來(lái)看一下 enqueue
方法的實(shí)現(xiàn):
public <T> void enqueue(Call<T> call, Callback<T> callback) { Utils.validateServiceInterface(call.request().tag(), call.request().url().toString()); callAdapter(call, call.request().tag()).adapt(call).enqueue(new CallbackRunnable<>(callback)); }
在這個(gè)方法中,我們首先進(jìn)行接口校驗(yàn),然后根據(jù)請(qǐng)求的 Tag 和 URL 來(lái)獲取適配器,并使用適配器來(lái)執(zhí)行請(qǐng)求。不同的是,在 enqueue
方法中,我們將一個(gè) Callback 對(duì)象作為參數(shù)傳入適配器的 enqueue
方法中,以便在請(qǐng)求完成后回調(diào)通知我們。
在適配器中,我們可以看到 enqueue
方法的實(shí)現(xiàn):
public void enqueue(final Callback<T> callback) { delegate.enqueue(new Callback<Response<T>>() { @Override public void onResponse(Call<Response<T>> call, Response<Response<T>> response) { Response<T> body; try { body = response.body(); } catch (Throwable t) { if (response.code() == 204) { body = null; } else { callback.onFailure(call, t); return; } } if (response.isSuccessful()) { callback.onResponse(call, Response.success(body, response.raw())); } else { callback.onFailure(call, Response.error(response.errorBody(), response.raw())); } } @Override public void onFailure(Call<Response<T>> call, Throwable t) { callback.onFailure(call, t); } }); }
在這個(gè)方法中,我們會(huì)將傳入的 Callback 對(duì)象轉(zhuǎn)換成一個(gè) Callback<Response<T>>
對(duì)象,并使用這個(gè)對(duì)象來(lái)調(diào)用 OkHttp 的 enqueue 方法。在請(qǐng)求完成后,我們會(huì)將 OkHttp 的 Response 對(duì)象轉(zhuǎn)換成 Retrofit 的 Response 對(duì)象,并根據(jù)響應(yīng)碼來(lái)判斷請(qǐng)求的結(jié)果。如果響應(yīng)碼表示請(qǐng)求成功,那么我們就調(diào)用 Callback 對(duì)象的 onResponse
方法;否則就調(diào)用 Callback 對(duì)象的 onFailure
方法。
響應(yīng)處理
在 Retrofit 中,我們可以通過(guò)定義一個(gè)接口并使用注解來(lái)描述我們期望的請(qǐng)求格式和響應(yīng)格式。例如,我們可以通過(guò) @GET
注解來(lái)描述一個(gè) GET 請(qǐng)求,使用 @Query
注解來(lái)描述請(qǐng)求參數(shù),使用 @Body
注解來(lái)描述請(qǐng)求體,使用 @Headers
注解來(lái)描述請(qǐng)求頭等。
在執(zhí)行請(qǐng)求時(shí),Retrofit 會(huì)根據(jù)這些注解來(lái)自動(dòng)生成一個(gè)對(duì)應(yīng)的請(qǐng)求對(duì)象,并將請(qǐng)求對(duì)象轉(zhuǎn)換成 OkHttp 的 Request 對(duì)象。在接收響應(yīng)時(shí),Retrofit 會(huì)將 OkHttp 的 Response 對(duì)象轉(zhuǎn)換成一個(gè)對(duì)應(yīng)的響應(yīng)對(duì)象,并將響應(yīng)對(duì)象中的數(shù)據(jù)轉(zhuǎn)換成我們需要的數(shù)據(jù)類型。這些轉(zhuǎn)換工作是通過(guò) Retrofit 的轉(zhuǎn)換器來(lái)完成的,Retrofit 中默認(rèn)提供了兩個(gè)轉(zhuǎn)換器:GsonConverterFactory
和 JacksonConverterFactory
。我們也可以自定義一個(gè)轉(zhuǎn)換器來(lái)實(shí)現(xiàn)我們期望的數(shù)據(jù)轉(zhuǎn)換。
在 Retrofit 類的構(gòu)造方法中,我們可以看到 Retrofit 默認(rèn)使用了 Platform.get()
方法來(lái)獲取當(dāng)前運(yùn)行平臺(tái)的默認(rèn)轉(zhuǎn)換器工廠,并將其添加到 converterFactories
中。然后,我們可以使用 addConverterFactory
方法來(lái)添加自定義的轉(zhuǎn)換器工廠。
public Retrofit(Builder builder) { // ... if (builder.converterFactories == null) { converterFactories.add(Platform.get().defaultConverterFactory()); } else { converterFactories.addAll(builder.converterFactories); } // ... } public interface Platform { // ... Converter.Factory defaultConverterFactory(); }
在execute
方法中,我們會(huì)調(diào)用適配器的 adapt 方法來(lái)執(zhí)行請(qǐng)求,并將返回的 Call 對(duì)象轉(zhuǎn)換成一個(gè)響應(yīng)對(duì)象。在轉(zhuǎn)換過(guò)程中,我們會(huì)根據(jù)響應(yīng)類型來(lái)選擇對(duì)應(yīng)的轉(zhuǎn)換器來(lái)進(jìn)行轉(zhuǎn)換。具體的轉(zhuǎn)換實(shí)現(xiàn)可以參考 Retrofit 提供的 Converter
接口和 Converter.Factory
接口。
public <T> T execute(Call<T> call) throws IOException { // ... Response<T> response = call.execute(); if (response.isSuccessful()) { return response.body(); } else { Converter<ResponseBody, ErrorResponse> converter = retrofit.responseBodyConverter( ErrorResponse.class, new Annotation[0]); throw new ApiException(converter.convert(response.errorBody())); } } @SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety. public <T> T adapt(Call<T> call) { return (T) new OkHttpCall<>(requestFactory, callFactory, converter, call); } public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) { return nextResponseBodyConverter(null, type, annotations); } public <T> Converter<ResponseBody, T> nextResponseBodyConverter( @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) { Objects.requireNonNull(type, "type == null"); Objects.requireNonNull(annotations, "annotations == null"); int start = converterFactories.indexOf(skipPast) + 1; for (int i = start, count = converterFactories.size(); i < count; i++) { Converter<ResponseBody, ?> converter = converterFactories.get(i).responseBodyConverter(type, annotations, this); if (converter != null) { return (Converter<ResponseBody, T>) converter; } } throw new IllegalArgumentException( "Could not locate ResponseBody converter for " + type + " with annotations " + Arrays.toString(annotations)); }
以上是 Retrofit 中處理響應(yīng)的核心代碼。當(dāng)我們執(zhí)行一個(gè)請(qǐng)求時(shí),Retrofit 會(huì)先將請(qǐng)求轉(zhuǎn)換成 OkHttp 的 Request 對(duì)象并發(fā)送出去,然后等待響應(yīng)返回。當(dāng)響應(yīng)返回時(shí),Retrofit 會(huì)將響應(yīng)轉(zhuǎn)換成一個(gè)響應(yīng)對(duì)象,并將響應(yīng)對(duì)象中的數(shù)據(jù)轉(zhuǎn)換成我們期望的數(shù)據(jù)類型。這個(gè)過(guò)程中,我們可以使用 Retrofit 提供的轉(zhuǎn)換器來(lái)自定義數(shù)據(jù)的轉(zhuǎn)換規(guī)則。
下面是一個(gè)示例,演示了如何使用 Retrofit 來(lái)發(fā)送一個(gè) GET 請(qǐng)求并將響應(yīng)中的 JSON 數(shù)據(jù)轉(zhuǎn)換成一個(gè) Java 對(duì)象:
public interface ApiService { @GET("users/{user}/repos") Call<List<Repo>> listRepos(@Path("user") String user); } Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") .addConverterFactory(GsonConverterFactory.create()) .build(); ApiService apiService = retrofit.create(ApiService.class); Call<List<Repo>> call = apiService.listRepos("smallmarker"); List<Repo> repos = call.execute().body();
在上面的示例中,我們首先使用 Retrofit 構(gòu)建器創(chuàng)建一個(gè) Retrofit 實(shí)例,并指定了請(qǐng)求的基礎(chǔ) URL 和轉(zhuǎn)換器工廠。然后,我們通過(guò)調(diào)用 create
方法來(lái)創(chuàng)建一個(gè) ApiService
的代理對(duì)象。最后,我們調(diào)用 listRepos
方法來(lái)發(fā)送一個(gè) GET 請(qǐng)求。
在上面的示例中,我們使用了 Retrofit 的 GsonConverterFactory
來(lái)將響應(yīng)體中的 JSON 數(shù)據(jù)轉(zhuǎn)換成 Java 對(duì)象。具體實(shí)現(xiàn)可以查看 Retrofit 提供的 GsonConverterFactory
類。
public final class GsonConverterFactory extends Converter.Factory { private final Gson gson; private GsonConverterFactory(Gson gson) { this.gson = gson; } public static GsonConverterFactory create() { return create(new Gson()); } public static GsonConverterFactory create(Gson gson) { if (gson == null) throw new NullPointerException("gson == null"); return new GsonConverterFactory(gson); } @Override public @Nullable Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) { TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type)); return new GsonResponseBodyConverter<>(gson, adapter); } @Override public @Nullable Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) { TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type)); return new GsonRequestBodyConverter<>(gson, adapter); } }
可以看到,GsonConverterFactory
繼承了 Retrofit 的 Converter.Factory
類,并重寫(xiě)了其中的 responseBodyConverter
方法和 requestBodyConverter
方法。在 responseBodyConverter
方法中,我們將響應(yīng)體中的 JSON 數(shù)據(jù)轉(zhuǎn)換成 Java 對(duì)象,而在 requestBodyConverter
方法中,我們將 Java 對(duì)象轉(zhuǎn)換成請(qǐng)求體中的 JSON 數(shù)據(jù)。
除了 GsonConverterFactory
以外,Retrofit 還提供了其他的轉(zhuǎn)換器,如 JacksonConverterFactory、MoshiConverterFactory
等,我們可以根據(jù)需要選擇適合自己的轉(zhuǎn)換器。
總的來(lái)說(shuō),Retrofit 中網(wǎng)絡(luò)請(qǐng)求和響應(yīng)處理的核心代碼非常簡(jiǎn)潔明了。我們只需要通過(guò)定義接口來(lái)描述請(qǐng)求和響應(yīng),然后使用 Retrofit 的動(dòng)態(tài)代理機(jī)制來(lái)將接口轉(zhuǎn)換成一個(gè)實(shí)際的實(shí)現(xiàn)類,并通過(guò) Retrofit 的配置來(lái)指定請(qǐng)求和響應(yīng)的轉(zhuǎn)換器即可。這種方式大大簡(jiǎn)化了網(wǎng)絡(luò)請(qǐng)求的流程,使得我們可以更加專注于業(yè)務(wù)邏輯的處理。
到此這篇關(guān)于Retrofit網(wǎng)絡(luò)請(qǐng)求和響應(yīng)處理重點(diǎn)分析講解的文章就介紹到這了,更多相關(guān)Retrofit網(wǎng)絡(luò)請(qǐng)求和響應(yīng)處理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android?Flutter實(shí)現(xiàn)任意拖動(dòng)的控件
使用flutter開(kāi)發(fā)是需要控件能拖動(dòng),比如畫(huà)板中的元素,或者工具條等,所以本文為大家準(zhǔn)備了Flutter實(shí)現(xiàn)任意拖動(dòng)控件的示例代碼,希望對(duì)大家有所幫助2023-07-07Android監(jiān)聽(tīng)鍵盤(pán)狀態(tài)獲取鍵盤(pán)高度的實(shí)現(xiàn)方法
這篇文章主要給大家介紹了關(guān)于Android監(jiān)聽(tīng)鍵盤(pán)狀態(tài)獲取鍵盤(pán)高度的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)各位Android開(kāi)發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12Compose自定義View實(shí)現(xiàn)宇智波斑寫(xiě)輪眼
這篇文章主要為大家介紹了Compose自定義View實(shí)現(xiàn)宇智波斑寫(xiě)輪眼示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02android實(shí)現(xiàn)數(shù)獨(dú)游戲機(jī)器人
這篇文章主要為大家詳細(xì)介紹了android實(shí)現(xiàn)數(shù)獨(dú)游戲機(jī)器人,文中安裝步驟介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03Android實(shí)現(xiàn)根據(jù)評(píng)分添加星級(jí)條
這篇文章主要介紹了Android實(shí)現(xiàn)根據(jù)評(píng)分添加星級(jí)條,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-10-10