你在使用 Retrofit 的時候揉忘,是否會有如下幾點(diǎn)疑惑跳座?
什么是動態(tài)代理?
整個請求的流程是怎樣的泣矛?
底層是如何用 OkHttp 請求的疲眷?
方法上的注解是什么時候解析的,怎么解析的乳蓄?
Converter 的轉(zhuǎn)換過程咪橙,怎么通過 Gson 轉(zhuǎn)成對應(yīng)的數(shù)據(jù)模型的?
CallAdapter 的替換過程虚倒,怎么轉(zhuǎn)成 RxJava 進(jìn)行操作的?
-
如何支持 Kotlin 協(xié)程的 suspend 掛起函數(shù)的产舞?
關(guān)于 Kotlin 協(xié)程請求網(wǎng)絡(luò)魂奥,首先寫一個 Demo 來看一下用協(xié)程是怎么進(jìn)行網(wǎng)絡(luò)請求的,然后會再具體分析怎么轉(zhuǎn)換成 Kotlin 的協(xié)程的請求
我會在文章中易猫,通過源碼耻煤,逐步解開疑惑,并且在最后文章結(jié)尾會再次總結(jié),回答上面的幾個問題哈蝇。
友情提示棺妓,本文略長但是沒有廢話,實(shí)打?qū)嵉母韶浥谏猓瑢W(xué)習(xí)需要耐心
Retrofit 和 OkHttp 是目前最廣泛使用的網(wǎng)絡(luò)請求庫了怜跑,所以有必要了解它的源碼,學(xué)習(xí)它的優(yōu)秀的代碼與設(shè)計吠勘,來提升自己性芬。
本文的整體思路
首先先看一下 Retrofit 的基本用法,根據(jù)示例代碼剧防,作為分析源碼的依據(jù)植锉,以及分析源碼的入口,來一步一步看一下 Retrofit 的工作機(jī)制峭拘。
本文的依賴
implementation 'com.squareup.okhttp3:okhttp:4.8.1'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.7.2'
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'io.reactivex.rxjava3:rxjava:3.0.0'
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
復(fù)制代碼
1.什么是Retrofit
Retrofit:A type-safe HTTP client for Android and Java俊庇。一個類型安全的 Http 請求的客戶端。
底層的網(wǎng)絡(luò)請求是基于 OkHttp 的鸡挠,Retrofit 對其做了封裝暇赤,提供了即方便又高效的網(wǎng)絡(luò)訪問框架。
2.Retrofit的基本用法
class RetrofitActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//初始化一個Retrofit對象
val retrofit = Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.build()
//創(chuàng)建出GitHubApiService對象
val service = retrofit.create(GitHubApiService::class.java)
//返回一個 Call 對象
val repos = service.listRepos("octocat")
//調(diào)用 enqueue 方法在回調(diào)方法里處理結(jié)果
repos.enqueue(object : Callback<List<Repo>?> {
override fun onFailure(call: Call<List<Repo>?>, t: Throwable) {
t.printStackTrace()
}
override fun onResponse(call: Call<List<Repo>?>, response: Response<List<Repo>?>) {
"response.code() = ${response.code()}".logE()
}
})
}
}
復(fù)制代碼
//自己定義的 API 請求接口
interface GitHubApiService {
@GET("users/{user}/repos")
fun listRepos(@Path("user") user: String?): Call<List<Repo>>
}
復(fù)制代碼
以上就是 Retrofit 的基本用法宵凌。
沒什么好講的鞋囊,寫這個例子就是為了分析源碼做準(zhǔn)備,有一個源碼分析的入口瞎惫。
3.源碼分析的準(zhǔn)備工作
先看幾個表面上的類
- Retrofit:總攬全局一個類溜腐,一些配置,需要通過其內(nèi)部 Builder 類構(gòu)建瓜喇,比如 CallAdapter挺益、Converter 等
- GitHubApiService:自己寫的 API 接口,通過 Retrofit 的
create
方法進(jìn)行實(shí)例化 - Call:Retrofit 的 Call乘寒,是執(zhí)行網(wǎng)絡(luò)請求的是一個頂層接口望众,需要看源碼,具體實(shí)現(xiàn)類實(shí)際是一個 OkHttpCall伞辛,下面會具體說
- Callback:請求結(jié)果回調(diào)
接下來重點(diǎn)來了烂翰,進(jìn)行源碼分析。
4.源碼分析
分析的入口是我們代碼例子中的repos.enqueue(object : Callback<List<Repo>?> {…})
方法
點(diǎn)進(jìn)去蚤氏,看到是 Call 的enqueue
方法
public interface Call<T> extends Cloneable {
void enqueue(Callback<T> callback);
}
復(fù)制代碼
這是一個接口甘耿,是我們 GitHubApiService 接口中定義的 listRepos
方法中返回的 Call 對象,現(xiàn)在就要看GitHubApiService 的初始化竿滨,以及具體返回的是 Call 對象是誰佳恬。
然后重點(diǎn)就要看 retrofit.create(GitHubApiService::class.java)
方法捏境,來看下 GitHubApiService 具體是怎么創(chuàng)建的仿贬,以及 Call 對象的實(shí)現(xiàn)類
5.Retrofit 的 create 方法
//Retrofit.java
public <T> T create(final Class<T> service) {
//1
validateServiceInterface(service);
//2
return (T)
Proxy.newProxyInstance(
service.getClassLoader(),
new Class<?>[] {service},
new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override
public @Nullable 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);
}
args = args != null ? args : emptyArgs;
return platform.isDefaultMethod(method)
? platform.invokeDefaultMethod(method, service, proxy, args)
: loadServiceMethod(method).invoke(args);
}
});
}
復(fù)制代碼
注釋 1:這個方法超歌,就是驗(yàn)證我們定義的 GitHubApiService 是一個接口,且不是泛型接口瞻讽,并且會判斷是否進(jìn)行方法的提前驗(yàn)證倾剿,為了更好的把錯誤暴露的編譯期筷频,這個不是我們的重點(diǎn)內(nèi)容,具體代碼就不看了柱告。
注釋 2 :是一個動態(tài)代理的方法截驮,來返回 GitHubApiService 的實(shí)例
動態(tài)代理?嗯际度?什么是動態(tài)代理葵袭,接下來,我就寫一個具體的例子來展示一個動態(tài)代理的具體用法乖菱,以及什么是動態(tài)代理坡锡?
先插播一段動態(tài)代理代碼,這個是理解 Retrofit 的工作機(jī)制所必須的窒所。
6.動態(tài)代理的示例
6.1.寫一個動態(tài)代理的 Demo
下面是一個 Java 項(xiàng)目鹉勒,模擬一個 Retrofit 的請求過程
//模擬 Retrofit,定義 API 請求接口
public interface GitHubApiService {
void listRepos(String user);
}
復(fù)制代碼
public class ProxyDemo {
//程序的入口方法
public static void main(String[] args) {
//通過動態(tài)代理獲取 ApiService 的對象
GitHubApiService apiService = (GitHubApiService) Proxy.newProxyInstance(
GitHubApiService.class.getClassLoader(),
new Class[]{GitHubApiService.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("method = " + method.getName() + " args = " + Arrays.toString(args));
return null;
}
});
System.out.println(apiService.getClass());
//調(diào)用 listRepos 方法
apiService.listRepos("octcat");
}
}
復(fù)制代碼
執(zhí)行 main
方法
當(dāng)我們調(diào)用 apiService.listRepos("octcat");
方法時,打印出來如下結(jié)果
class com.sun.proxy.$Proxy0
method = listRepos args = [octcat]
復(fù)制代碼
可以看到當(dāng)我們調(diào)用listRepos
方法的時候吵取,InvocationHandler 的 invoke
方法中攔截到了我們的方法禽额,參數(shù)等信息。Retrofit 的原理其實(shí)就是這樣皮官,攔截到方法脯倒、參數(shù),再根據(jù)我們在方法上的注解捺氢,去拼接為一個正常的OkHttp 請求藻丢,然后執(zhí)行。
日志的第一行摄乒,在運(yùn)行時這個類一個$Proxy0
的類悠反。實(shí)際上,在運(yùn)行期 GitHubApiService 的接口會動態(tài)的創(chuàng)建出實(shí)現(xiàn)類也就是這個 $Proxy0
類馍佑,它大概長下面這個樣子斋否,具體的看鴻洋這篇文章 從一道面試題開始說起 枚舉、動態(tài)代理的原理
我做了一個點(diǎn)改動挤茄,方便查看如叼,本質(zhì)上都是一樣的
class $Proxy0 extends Proxy implements GitHubApiService {
protected $Proxy0(InvocationHandler h) {
super(h);
}
@Override
public void listRepos(String user) {
Method method = Class.forName("GitHubApiService").getMethod("listRepos", String.class);
super.h.invoke(this, method, new Object[]{user});
}
}
復(fù)制代碼
我們在調(diào)用listRepos
方法的時候,實(shí)際上調(diào)用的是 InvocationHandler 的 invoke
方法穷劈。
6.2總結(jié)
- 在 ProxyDemo 代碼運(yùn)行中笼恰,會動態(tài)創(chuàng)建 GitHubApiService 接口的實(shí)現(xiàn)類,作為代理對象歇终,執(zhí)行InvocationHandler 的
invoke
方法社证。 - 動態(tài)指的是在運(yùn)行期,而代理指的是實(shí)現(xiàn)了GitHubApiService 接口的具體類评凝,實(shí)現(xiàn)了接口的方法追葡,稱之為代理
- 本質(zhì)上是在運(yùn)行期,生成了 GitHubApiService 接口的實(shí)現(xiàn)類奕短,調(diào)用了 InvocationHandler 的
invoke
方法宜肉。
現(xiàn)在解決了第一個疑問:什么是動態(tài)代理
好的,動態(tài)代理已經(jīng)知道是啥了翎碑,回到我們 retrofit.create(GitHubApiService::class.java)
方法
7.再看 Retrofit 的 create 方法
//Retrofit.java
public <T> T create(final Class<T> service) {
validateServiceInterface(service);
return (T)
Proxy.newProxyInstance(
service.getClassLoader(),//1
new Class<?>[] {service},//2
new InvocationHandler() {//3
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override
public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
//4
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
args = args != null ? args : emptyArgs;
//5
return platform.isDefaultMethod(method)
? platform.invokeDefaultMethod(method, service, proxy, args)
: loadServiceMethod(method).invoke(args);
}
});
}
復(fù)制代碼
注釋 1:獲取一個 ClassLoader 對象
注釋 2:GitHubApiService 的字節(jié)碼對象傳到數(shù)組中去谬返,也即是我們要代理的具體接口。
注釋 3:InvocationHandler 的 invoke
是關(guān)鍵日杈,從上面動態(tài)代理的 Demo 中遣铝,我們知道,在GitHubApiService聲明的 listRepos
方法在調(diào)用時莉擒,會執(zhí)行 InvocationHandler 的invoke
的方法體酿炸。
注釋 4:因?yàn)橛写眍惖纳桑J(rèn)繼承 Object 類涨冀,所以如果是 Object.class 走填硕,默認(rèn)調(diào)用它的方法
注釋 5:如果是默認(rèn)方法(比如 Java8 ),就執(zhí)行 platform 的默認(rèn)方法鹿鳖。否則執(zhí)行loadServiceMethod
方法的invoke
方法
loadServiceMethod(method).invoke(args);
這個方法是我們這個 Retrofit 最關(guān)鍵的代碼扁眯,也是分析的重點(diǎn)入口
7.1.先看loadServiceMethod方法
我們先看loadServiceMethod
方法返回的是什么對象,然后再看這個對象的 invoke
方法
//Retrofit.java
private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>();
ServiceMethod<?> loadServiceMethod(Method method) {
//1
ServiceMethod<?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
//2
result = ServiceMethod.parseAnnotations(this, method);
//3
serviceMethodCache.put(method, result);
}
}
return result;
}
復(fù)制代碼
注釋 1:從 ConcurrentHashMap 中取一個 ServiceMethod 如果存在直接返回
注釋 2:通過 ServiceMethod.parseAnnotations(this, method);
方法創(chuàng)建一個 ServiceMethod 對象
注釋 3:用 Map 把創(chuàng)建的 ServiceMethod 對象緩存起來栓辜,因?yàn)槲覀兊恼埱蠓椒赡軙{(diào)用多次恋拍,緩存提升性能。
看一下 ServiceMethod.parseAnnotations(this, method);
方法具體返回的對象是什么藕甩,然后再看它的 invoke
方法
7.2.ServiceMethod的parseAnnotations方法
這個方法接下來還會看施敢,這里我們只看現(xiàn)在需要的部分。
//ServiceMethod.java
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
...
//1
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
復(fù)制代碼
返回的是一個HttpServiceMethod對象狭莱,那么接下來看下它的 invoke 方法
7.3.HttpServiceMethod 的 invoke 方法
//HttpServiceMethod.java
@Override
final @Nullable ReturnT invoke(Object[] args) {
//1
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
//2
return adapt(call, args);
}
復(fù)制代碼
注釋 1:創(chuàng)建了一個Call對象僵娃,是 OkHttpCall,這個不就是在 GitHubApiService 這個接口聲明的 Call 對象嗎腋妙?
然后再看 OkHttpCall 的enqueue
方法默怨,不就知道是怎么進(jìn)行請求,怎么回調(diào)的了嗎骤素?
注釋 2:是一個 adapt 方法匙睹,在不使用 Kotlin 協(xié)程的情況下愚屁,其實(shí)調(diào)用的是子類 CallAdapted 的 adapt
,這個會在下面具體分析痕檬,包括 Kotlin 協(xié)程的 suspend 函數(shù)
現(xiàn)在我們已經(jīng)知道了 GitHubApiService 接口中定義的 listRepos
中的 Call 對象霎槐,是 OkHttpCall,接下里看OkHttpCall 的 enqueue
方法
8.OkHttpCall的enqueue方法
這段代碼比較長梦谜,但這個就是這個請求的關(guān)鍵丘跌,以及怎么使用 OkHttp 進(jìn)行請求的,如果解析 Response 的唁桩,如何回調(diào)的闭树。
//OkHttpCall.java
@Override
public void enqueue(final Callback<T> callback) {
Objects.requireNonNull(callback, "callback == null");
//1
okhttp3.Call call;
Throwable failure;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
call = rawCall;
failure = creationFailure;
if (call == null && failure == null) {
try {
//2
call = rawCall = createRawCall();
} catch (Throwable t) {
throwIfFatal(t);
failure = creationFailure = t;
}
}
}
if (failure != null) {
callback.onFailure(this, failure);
return;
}
if (canceled) {
call.cancel();
}
//3
call.enqueue(
new okhttp3.Callback() {
@Override
public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response<T> response;
try {
//4
response = parseResponse(rawResponse);
} catch (Throwable e) {
throwIfFatal(e);
callFailure(e);
return;
}
try {
//5
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
throwIfFatal(t);
t.printStackTrace(); // TODO this is not great
}
}
@Override
public void onFailure(okhttp3.Call call, IOException e) {
callFailure(e);
}
private void callFailure(Throwable e) {
try {
//6
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
throwIfFatal(t);
t.printStackTrace(); // TODO this is not great
}
}
});
}
復(fù)制代碼
注釋 1:聲明一個 okhttp3.Call 對象,用來進(jìn)行網(wǎng)絡(luò)請求
注釋 2:給 okhttp3.Call 對象進(jìn)行賦值荒澡,下面會具體看代碼报辱,如何創(chuàng)建了一個 okhttp3.Call 對象
注釋 3:調(diào)用 okhttp3.Call 的 enqueue
方法,進(jìn)行真正的網(wǎng)絡(luò)請求
注釋 4:解析響應(yīng)仰猖,下面會具體看代碼
注釋 5:成功的回調(diào)
注釋 6:失敗的回調(diào)
到現(xiàn)在捏肢,我們文章開頭兩個疑問得到解釋了
整個請求的流程是怎樣的?
底層是如何用 OkHttp 請求的饥侵?
我們還要看下一個 okhttp3.Call 對象是怎么創(chuàng)建的鸵赫,我們寫的注解參數(shù)是怎么解析的,響應(yīng)結(jié)果是如何解析的躏升,也就是我們在 Retrofit 中配置 addConverterFactory(GsonConverterFactory.create())
是如何直接拿到數(shù)據(jù)模型的辩棒。
8.1.okhttp3.Call 對象是怎么創(chuàng)建的
看下 call = rawCall = createRawCall();
方法
//OkHttpCall.java
private final okhttp3.Call.Factory callFactory;
private okhttp3.Call createRawCall() throws IOException {
//1 callFactory是什么
okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
復(fù)制代碼
通過 callFactory 創(chuàng)建的(callFactory應(yīng)該是 OkHttpClient),看一下 callFactory 的賦值過程
//OkHttpCall.java
OkHttpCall(
RequestFactory requestFactory,
Object[] args,
okhttp3.Call.Factory callFactory,
Converter<ResponseBody, T> responseConverter) {
this.requestFactory = requestFactory;
this.args = args;
//通過 OkHttpCall 構(gòu)造直接賦值
this.callFactory = callFactory;
this.responseConverter = responseConverter;
}
復(fù)制代碼
在 OkHttpCall 構(gòu)造中直接賦值膨疏,那接下來就繼續(xù)看 OkHttpCall 的初始化過程
//HttpServiceMethod.java
private final okhttp3.Call.Factory callFactory;
@Override
final @Nullable ReturnT invoke(Object[] args) {
//在 OkHttpCall 實(shí)例化時賦值一睁, callFactory 是 HttpServiceMethod 的成員變量
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}
//callFactory 是在 HttpServiceMethod 的構(gòu)造中賦值的
HttpServiceMethod(
RequestFactory requestFactory,
okhttp3.Call.Factory callFactory,
Converter<ResponseBody, ResponseT> responseConverter) {
this.requestFactory = requestFactory;
//通過 HttpServiceMethod 構(gòu)造直接賦值
this.callFactory = callFactory;
this.responseConverter = responseConverter;
}
復(fù)制代碼
發(fā)現(xiàn) callFactory 的值是在創(chuàng)建 HttpServiceMethod 時賦值的,繼續(xù)跟佃却!
在 7.2 小節(jié)者吁,有一行代碼HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
我們沒有跟進(jìn)去,現(xiàn)在看一下 HttpServiceMethod 是怎么創(chuàng)建的
//HttpServiceMethod.java
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
boolean continuationWantsResponse = false;
boolean continuationBodyNullable = false;
//1
okhttp3.Call.Factory callFactory = retrofit.callFactory;
if (!isKotlinSuspendFunction) {
//2
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
} else if (continuationWantsResponse) {
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
return (HttpServiceMethod<ResponseT, ReturnT>)
new SuspendForResponse<>(
requestFactory,
callFactory,
responseConverter,
(CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
} else {
//noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
return (HttpServiceMethod<ResponseT, ReturnT>)
new SuspendForBody<>(
requestFactory,
callFactory,
responseConverter,
(CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
continuationBodyNullable);
}
}
復(fù)制代碼
注釋 1:callFactory 的值是從 Retrofit 這個對象拿到的
注釋 2:如果不是 Kotlin 的掛起函數(shù)饲帅,返回是的 CallAdapted 對象
static final class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> {}
復(fù)制代碼
CallAdapted 是 HttpServiceMethod 的子類复凳,會調(diào)用
adapt
方法進(jìn)行 CallAdapter 的轉(zhuǎn)換,我們后面會詳細(xì)看灶泵。
繼續(xù)看 Retrofit 的 callFactory 的值Retrofit是通過Builder構(gòu)建的育八,看下Builder類
//Retrofit.java
public static final class Builder {
public Retrofit build() {
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
//1
callFactory = new OkHttpClient();
}
return new Retrofit(
callFactory,
baseUrl,
unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories),
callbackExecutor,
validateEagerly);
}
}
復(fù)制代碼
原來 callFactory 實(shí)際是一個 OkHttpClient 對象,也就是 OkHttpClient 創(chuàng)建了一個 Call 對象赦邻,嗯就是 OKHttp 網(wǎng)絡(luò)請求的那一套髓棋。
在創(chuàng)建okhttp3.Call 對象的 callFactory.newCall(requestFactory.create(args));
方法中的 requestFactory.create(args)
方法會返回一個 Request 的對象,這個我們也會在下面看是如何構(gòu)造一個 OkHttp 的 Request 請求對象的。
8.2.請求注解參數(shù)是怎么解析的
看 ServiceMethod.parseAnnotations(this, method);
方法
//ServiceMethod.java
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
//1
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
...
//2
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
復(fù)制代碼
注釋 1:通過 RequestFactory 解析注解按声,然后返回 RequestFactory 對象
注釋 2:把 RequestFactory 對象往 HttpServiceMethod 里面?zhèn)鬟f膳犹,下面會具體看 RequestFactory 對象具體干什么用了?
繼續(xù)跟代碼RequestFactory.parseAnnotations
//RequestFactory.java
final class RequestFactory {
static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
//看build方法
return new Builder(retrofit, method).build();
}
//build方法
RequestFactory build() {
//1
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
....
return new RequestFactory(this);
}
}
復(fù)制代碼
遍歷 GitHubApiService 這個 API 接口上定義的方法注解儒喊,然后解析注解
繼續(xù)跟代碼parseMethodAnnotation
//RequestFactory.java
private void parseMethodAnnotation(Annotation annotation) {
if (annotation instanceof DELETE) {
parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
} else if (annotation instanceof GET) {
parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
}
...
else if (annotation instanceof POST) {
parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
}
....
else if (annotation instanceof Multipart) {
if (isFormEncoded) {
throw methodError(method, "Only one encoding annotation is allowed.");
}
isMultipart = true;
} else if (annotation instanceof FormUrlEncoded) {
if (isMultipart) {
throw methodError(method, "Only one encoding annotation is allowed.");
}
isFormEncoded = true;
}
}
復(fù)制代碼
就是解析方法上的注解镣奋,來存到 RequestFactory 的內(nèi)部币呵。
其實(shí) RequestFactory 這個類還有 parseParameter
和 parseParameterAnnotation
這個就是解析方法參數(shù)聲明上的具體參數(shù)的注解怀愧,會在后面分析 Kotlin suspend 掛起函數(shù)具體講。
總之:具體代碼就是分析方法上注解上面的值余赢,方法參數(shù)上芯义,這個就是細(xì)節(jié)問題了
總結(jié)就是:分析方法上的各個注解,方法參數(shù)上的注解妻柒,最后返回 RequestFactory 對象扛拨,給下面使用。
Retrofit 的大框架簡單举塔,細(xì)節(jié)比較復(fù)雜绑警。
RequestFactory 對象返回出去,具體干嘛用了央渣?大膽猜一下计盒,解析出注解存到 RequestFactory 對象,這個對象身上可有各種請求的參數(shù)芽丹,然后肯定是類創(chuàng)建 OkHttp 的 Request請求對象啊北启,因?yàn)槭怯?OkHttp 請求的,它需要一個 Request 請求對象
8.3.RequestFactory 對象返回出去拔第,具體干嘛用了?
下面我就用一個代碼塊貼了咕村,看著更直接,我會具體表明屬于哪個類的
//ServiceMethod.java
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
//解析注解參數(shù)蚊俺,獲取 RequestFactory 對象
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
//把 RequestFactory 對象傳給 HttpServiceMethod
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
//注意換類了
//HttpServiceMethod.java
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
...
okhttp3.Call.Factory callFactory = retrofit.callFactory;
//不是 Kotlin 的掛起函數(shù)
if (!isKotlinSuspendFunction) {
//把requestFactory傳給 CallAdapted
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
}
....
}
//HttpServiceMethod.java
//CallAdapted 是 HttpServiceMethod 的內(nèi)部類也是 HttpServiceMethod 的子類
CallAdapted(
RequestFactory requestFactory,
okhttp3.Call.Factory callFactory,
Converter<ResponseBody, ResponseT> responseConverter,
CallAdapter<ResponseT, ReturnT> callAdapter) {
//這里把 requestFactory 傳給 super 父類的構(gòu)造參數(shù)里了懈涛,也就是 HttpServiceMethod
super(requestFactory, callFactory, responseConverter);
this.callAdapter = callAdapter;
}
//HttpServiceMethod.java
HttpServiceMethod(
RequestFactory requestFactory,
okhttp3.Call.Factory callFactory,
Converter<ResponseBody, ResponseT> responseConverter) {
// HttpServiceMethod 的 requestFactory 成員變量保存這個 RequestFactory 對象
this.requestFactory = requestFactory;
this.callFactory = callFactory;
this.responseConverter = responseConverter;
}
//因?yàn)闀{(diào)用 HttpServiceMethod 的 invoke 方法
//會把這個 RequestFactory 對象會繼續(xù)傳遞給 OkHttpCall 類中
//注意換類了
//OkHttpCall.java
OkHttpCall(
RequestFactory requestFactory,
Object[] args,
okhttp3.Call.Factory callFactory,
Converter<ResponseBody, T> responseConverter) {
//給 OkHttpCall 的requestFactory成員變量賦值
this.requestFactory = requestFactory;
this.args = args;
this.callFactory = callFactory;
this.responseConverter = responseConverter;
}
復(fù)制代碼
經(jīng)過層層傳遞 RequestFactory 這個實(shí)例終于是到了 HttpServiceMethod 類中,最終傳到了 OkHttpCall 中泳猬,那這個 RequestFactory 對象在什么時候使用呢批钠? RequestFactory 會繼續(xù)在OkHttpCall中傳遞,因?yàn)?OkHttpCall 才是進(jìn)行請求的暂殖。
在OkHttpCall的 創(chuàng)建 Call 對象時
//OkHttpCall.java
private okhttp3.Call createRawCall() throws IOException {
//1
okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
復(fù)制代碼
注釋 1:調(diào)用了requestFactory.create(args)
注意:此時的RequestFactory的各個成員變量在解析注解那一步都賦值了
//RequestFactory.java
okhttp3.Request create(Object[] args) throws IOException {
...
RequestBuilder requestBuilder =
new RequestBuilder(
httpMethod,
baseUrl,
relativeUrl,
headers,
contentType,
hasBody,
isFormEncoded,
isMultipart);
...
return requestBuilder.get().tag(Invocation.class, new Invocation(method, argumentList)).build();
}
復(fù)制代碼
最終 requestFactory 的值用來構(gòu)造 okhttp3.Request 的對象
以上就是解析注解价匠,構(gòu)造出okhttp3.Request的對象全過程了。
也就解答了方法上的注解是什么時候解析的呛每,怎么解析的踩窖?這個問題
8.4.請求響應(yīng)結(jié)果是如何解析的
比如我們在構(gòu)造 Retrofit 的時候加上 addConverterFactory(GsonConverterFactory.create())
這行代碼,我們的響應(yīng)結(jié)果是如何通過 Gson 直接解析成數(shù)據(jù)模型的晨横?
在 OkHttpCall 的enqueue
方法中
//OkHttpCall.java
@Override
public void enqueue(final Callback<T> callback) {
okhttp3.Call call;
...
call.enqueue(
new okhttp3.Callback() {
@Override
public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response<T> response;
try {
//1 解析響應(yīng)
response = parseResponse(rawResponse);
} catch (Throwable e) {
throwIfFatal(e);
callFailure(e);
return;
}
}
...
});
}
復(fù)制代碼
注釋 1:通過parseResponse
解析響應(yīng)返回給回調(diào)接口
//OkHttpCall.java
private final Converter<ResponseBody, T> responseConverter;
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
ResponseBody rawBody = rawResponse.body();
...
ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
try {
//1 通過 responseConverter 轉(zhuǎn)換 ResponseBody
T body = responseConverter.convert(catchingBody);
return Response.success(body, rawResponse);
} catch (RuntimeException e) {
// If the underlying source threw an exception, propagate that rather than indicating it was
// a runtime exception.
catchingBody.throwIfCaught();
throw e;
}
}
復(fù)制代碼
注釋 1:通過 responseConverter 調(diào)用convert
方法
首先那看 responseConverter 是什么以及賦值的過程洋腮,然后再看convert
方法
//OkHttpCall.java
private final Converter<ResponseBody, T> responseConverter;
OkHttpCall(
RequestFactory requestFactory,
Object[] args,
okhttp3.Call.Factory callFactory,
Converter<ResponseBody, T> responseConverter) {
this.requestFactory = requestFactory;
this.args = args;
this.callFactory = callFactory;
//在構(gòu)造中賦值
this.responseConverter = responseConverter;
}
// OkHttpCall 在 HttpServiceMethod 類中實(shí)例化
//注意換類了
//HttpServiceMethod.java
private final Converter<ResponseBody, ResponseT> responseConverter;
HttpServiceMethod(
RequestFactory requestFactory,
okhttp3.Call.Factory callFactory,
Converter<ResponseBody, ResponseT> responseConverter) {
this.requestFactory = requestFactory;
this.callFactory = callFactory;
//在構(gòu)造中賦值
this.responseConverter = responseConverter;
}
//HttpServiceMethod 在子類 CallAdapted 調(diào)用 super方法賦值
CallAdapted(
RequestFactory requestFactory,
okhttp3.Call.Factory callFactory,
Converter<ResponseBody, ResponseT> responseConverter,
CallAdapter<ResponseT, ReturnT> callAdapter) {
//在CallAdapted中調(diào)用super賦值
super(requestFactory, callFactory, responseConverter);
this.callAdapter = callAdapter;
}
復(fù)制代碼
繼續(xù)看 CallAdapted 的初始化中 responseConverter 的賦值過程
//HttpServiceMethod.java
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
...
CallAdapter<ResponseT, ReturnT> callAdapter =
createCallAdapter(retrofit, method, adapterType, annotations);
//1 實(shí)例化responseConverter
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
okhttp3.Call.Factory callFactory = retrofit.callFactory;
if (!isKotlinSuspendFunction) {
//2 CallAdapted的實(shí)例化賦值
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
}
...
}
復(fù)制代碼
繼續(xù)跟代碼 createResponseConverter
方法
//HttpServiceMethod.java
private static <ResponseT> Converter<ResponseBody, ResponseT> createResponseConverter(
Retrofit retrofit, Method method, Type responseType) {
Annotation[] annotations = method.getAnnotations();
try {
//調(diào)用的是 retrofit的方法
return retrofit.responseBodyConverter(responseType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(method, e, "Unable to create converter for %s", responseType);
}
}
//注意換類了
//Retrofit.java
final List<Converter.Factory> converterFactories;
public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) {
//繼續(xù)跟 nextResponseBodyConverter
return nextResponseBodyConverter(null, type, annotations);
}
public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
@Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
...
//1 從 converterFactories工廠中遍歷取出
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) {
//noinspection unchecked
return (Converter<ResponseBody, T>) converter;
}
}
...
}
復(fù)制代碼
注釋 1:從 converterFactories 遍歷取出一個來調(diào)用 responseBodyConverter
方法箫柳,注意根據(jù) responseType 返回值類型來取到對應(yīng)的 Converter,如果不為空啥供,直接返回此 Converter 對象
看一下 converterFactories 這個對象的賦值過程
//Retrofit.java
final List<Converter.Factory> converterFactories;
Retrofit(
okhttp3.Call.Factory callFactory,
HttpUrl baseUrl,
List<Converter.Factory> converterFactories,
List<CallAdapter.Factory> callAdapterFactories,
@Nullable Executor callbackExecutor,
boolean validateEagerly) {
this.callFactory = callFactory;
this.baseUrl = baseUrl;
this.converterFactories = converterFactories; // Copy+unmodifiable at call site.
//通過 Retrofit 的構(gòu)造賦值悯恍,Retrofit的 初始化是通過內(nèi)部 Builder 類的build方法
this.callAdapterFactories = callAdapterFactories; // Copy+unmodifiable at call site.
this.callbackExecutor = callbackExecutor;
this.validateEagerly = validateEagerly;
}
//Retrofit.java 內(nèi)部類 Builder 類的build方法
//Builder.java
public Retrofit build() {
...
// Make a defensive copy of the converters.
//1
List<Converter.Factory> converterFactories =
new ArrayList<>(
1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());
//2
converterFactories.add(new BuiltInConverters());
//3
converterFactories.addAll(this.converterFactories);
//4
converterFactories.addAll(platform.defaultConverterFactories());
return new Retrofit(
callFactory,
baseUrl,
unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories),
callbackExecutor,
validateEagerly);
}
復(fù)制代碼
注釋 1:初始化 converterFactories 這個 list
注釋 2:添加默認(rèn)的構(gòu)建的轉(zhuǎn)換器,其實(shí)是 StreamingResponseBodyConverter 和 BufferingResponseBodyConverter
注釋 3:就是自己添加的轉(zhuǎn)換配置 addConverterFactory(GsonConverterFactory.create())
//Retrofit.java 內(nèi)部類 Builder.java
public Builder addConverterFactory(Converter.Factory factory) {
converterFactories.add(Objects.requireNonNull(factory, "factory == null"));
return this;
}
復(fù)制代碼
注釋 4:如果是 Java8 就是一個 OptionalConverterFactory 的轉(zhuǎn)換器否則就是一個空的
注意:是怎么找到GsonConverterFactory來調(diào)用 Gson 的 convert方法的呢伙狐?在遍歷converterFactories時會根據(jù) responseType來找到對應(yīng)的轉(zhuǎn)換器涮毫。
具體 GsonConverterFactory 的 convert
方法就是 Gson 的邏輯了,就不是 Retrofit 的重點(diǎn)了贷屎。
到現(xiàn)在Converter 的轉(zhuǎn)換過程罢防,我們也就清楚了。
還有一個問題唉侄,我們寫的 API 接口是如何支持 RxJava 的
9.CallAdapter的替換過程
9.1.使用 RxJava 進(jìn)行網(wǎng)絡(luò)請求
怎么轉(zhuǎn)成 RxJava
比如:我們在初始化一個Retrofit時加入 addCallAdapterFactory(RxJava2CallAdapterFactory.create())
這行
//初始化一個Retrofit對象
val retrofit = Retrofit.Builder()
.baseUrl("https://api.github.com/")
//加入 RxJava2CallAdapterFactory 支持
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build()
復(fù)制代碼
加入 RxJava2 的配置支持后咒吐,把 RxJava2CallAdapterFactory 存到 callAdapterFactories 這個集合中,記住這一點(diǎn)属划,下面要用到恬叹。
interface GitHubApiService {
@GET("users/{user}/repos")
fun listReposRx(@Path("user") user: String?): Single<Repo>
}
復(fù)制代碼
我們就可以這么請求接口了
//創(chuàng)建出GitHubApiService對象
val service = retrofit.create(GitHubApiService::class.java)
service.listReposRx("octocat")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ repo ->
"response name = ${repo[0].name}".logE()
}, { error ->
error.printStackTrace()
})
復(fù)制代碼
我們可以在自己定義的 API 接口中直接返回一個 RxJava 的 Single 對象的,來進(jìn)行操作了同眯。
我們下面就來看下是如何把請求對象轉(zhuǎn)換成一個 Single 對象的
//Retrofit.java 內(nèi)部類 Builder.java
public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
callAdapterFactories.add(Objects.requireNonNull(factory, "factory == null"));
return this;
}
復(fù)制代碼
把 RxJava2CallAdapterFactory 存到了callAdapterFactories 這個 list 中了绽昼。
接下來我們看下是如何使用 callAdapterFactories 的 RxJava2CallAdapterFactory 中的這個 CallAdapter 的吧
這就要看我們之前看到了一個類了 HttpServiceMethod 的parseAnnotations
之前看過它的代碼,只是上次看的是Converter是如何賦值的也就是第 8.4 小節(jié)嗽测,這次看 CallAdapter 是如何被賦值使用的绪励。
9.2CallAdapter是如何被賦值過程
HttpServiceMethod的parseAnnotations
方法
//HttpServiceMethod.java
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
....
//1
CallAdapter<ResponseT, ReturnT> callAdapter =
createCallAdapter(retrofit, method, adapterType, annotations);
okhttp3.Call.Factory callFactory = retrofit.callFactory;
if (!isKotlinSuspendFunction) {
//2
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
}
...
}
復(fù)制代碼
注釋 1:初始化 CallAdapter
注釋 2:給 CallAdapted 中的 callAdapter 變量賦值,然后調(diào)用它的adapt
方法唠粥。
我們先找到具體 CallAdapter 賦值的對象疏魏,然后看它的adapt
就知道了,是如何轉(zhuǎn)換的了
接下來就是跟代碼的過程了
//HttpServiceMethod.java
private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(
Retrofit retrofit, Method method, Type returnType, Annotation[] annotations) {
try {
//noinspection unchecked
//調(diào)用retrofit的callAdapter方法
return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);
} catch (RuntimeException e) { // Wide exception range because factories are user code.
throw methodError(method, e, "Unable to create call adapter for %s", returnType);
}
}
//Retrofit.java
public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
//調(diào)用nextCallAdapter
return nextCallAdapter(null, returnType, annotations);
}
public CallAdapter<?, ?> nextCallAdapter(
@Nullable CallAdapter.Factory skipPast, Type returnType, Annotation[] annotations) {
...
//遍歷 callAdapterFactories
int start = callAdapterFactories.indexOf(skipPast) + 1;
for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
//是具體CallAdapterFactory的 get 方法
CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
if (adapter != null) {
return adapter;
}
}
...
}
復(fù)制代碼
遍歷 callAdapterFactories 根據(jù) returnType類型 來找到對應(yīng)的 CallAdapter 返回
比如:我們在 GitHubApiService 的 returnType 類型為 Single晤愧,那么返回的就是 RxJava2CallAdapterFactory 所獲取的 CallAdapter
interface GitHubApiService {
@GET("users/{user}/repos")
fun listReposRx(@Path("user") user: String?): Single<Repo>
}
復(fù)制代碼
RxJava2CallAdapterFactory的 get
方法
//RxJava2CallAdapterFactory.java
@Override public @Nullable CallAdapter<?, ?> get(
Type returnType, Annotation[] annotations, Retrofit retrofit) {
Class<?> rawType = getRawType(returnType);
if (rawType == Completable.class) {
return new RxJava2CallAdapter(Void.class, scheduler, isAsync, false, true, false, false,
false, true);
}
boolean isFlowable = rawType == Flowable.class;
//當(dāng)前是Single類型
boolean isSingle = rawType == Single.class;
boolean isMaybe = rawType == Maybe.class;
if (rawType != Observable.class && !isFlowable && !isSingle && !isMaybe) {
return null;
}
...
//返回 RxJava2CallAdapter對象大莫,isSingle參數(shù)為 true
return new RxJava2CallAdapter(responseType, scheduler, isAsync, isResult, isBody, isFlowable,
isSingle, isMaybe, false);
}
復(fù)制代碼
返回的是 RxJava2CallAdapter 對象,并且根據(jù) rawType 判斷當(dāng)前是個什么類型
看下 RxJava2CallAdapter 的adapt
方法
//RxJava2CallAdapter.java
@Override public Object adapt(Call<R> call) {
//1 把Call包裝成一個Observable對象
Observable<Response<R>> responseObservable = isAsync
? new CallEnqueueObservable<>(call)
: new CallExecuteObservable<>(call);
Observable<?> observable;
if (isResult) {
observable = new ResultObservable<>(responseObservable);
} else if (isBody) {
observable = new BodyObservable<>(responseObservable);
} else {
observable = responseObservable;
}
if (scheduler != null) {
observable = observable.subscribeOn(scheduler);
}
if (isFlowable) {
return observable.toFlowable(BackpressureStrategy.LATEST);
}
//2
if (isSingle) {
return observable.singleOrError();
}
if (isMaybe) {
return observable.singleElement();
}
if (isCompletable) {
return observable.ignoreElements();
}
return RxJavaPlugins.onAssembly(observable);
}
復(fù)制代碼
注釋 1:把 Call 包裝成一個 Observable 對象
注釋2:如果是 Single 則調(diào)用observable.singleOrError();
方法
到目前為止官份,CallAdapter 怎么變成一個 RxJava2CallAdapter 以及它的具體調(diào)用只厘,我們也就清楚了。
10.Retrofit 如何支持 Kotlin 協(xié)程的 suspend 掛起函數(shù)的舅巷?
整個流程中還有一點(diǎn)我們沒有分析 Retrofit 如何支持 Kotlin 協(xié)程的 suspend 掛起函數(shù)的羔味?
首先寫一個 Demo 來看一下協(xié)程是怎么進(jìn)行網(wǎng)絡(luò)請求的
10.1.Kotlin 協(xié)程請求網(wǎng)絡(luò)的 Demo
添加依賴
def kotlin_coroutines = '1.3.7'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines"
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
復(fù)制代碼
定義請求接口,寫一個掛起函數(shù)
interface GitHubApiService {
//使用 Kotlin 協(xié)程 钠右,定義一個掛起函數(shù)
@GET("users/{user}/repos")
suspend fun listReposKt(@Path("user") user: String?): List<Repo>
}
復(fù)制代碼
請求接口
//創(chuàng)建出GitHubApiService對象
val service = retrofit.create(GitHubApiService::class.java)
//lifecycle提供的協(xié)程的Scope赋元,因?yàn)?suspend 函數(shù)需要運(yùn)行在協(xié)程里面
lifecycleScope.launchWhenResumed {
try {
val repo = service.listReposKt("octocat")
"response name = ${repo[0].name}".logE()
} catch (e: Exception) {
e.printStackTrace()
//出錯邏輯
//ignore
}
}
復(fù)制代碼
以上就是一個,用 Kotlin 協(xié)程進(jìn)行網(wǎng)絡(luò)請求的,Retrofit 是支持 Kotlin 協(xié)程的搁凸,接下來看下媚值,Retrofit 是怎么支持的。
10.2.分析Kotlin 協(xié)程的掛起函數(shù)的準(zhǔn)備工作
首先在開始之前护糖,我們得先得從代碼角度知道褥芒,Kotlin 的 suspend 函數(shù)對應(yīng)的 Java 類是什么樣子,不然嫡良,就一個 suspend 關(guān)鍵字根本就沒法進(jìn)行分析锰扶。
我寫一個 suspend 的測試方法,然后轉(zhuǎn)換成 java 方法看一下皆刺,這個 suspend 函數(shù)是個啥少辣。
寫一個 Top Level 的Suspend.kt文件(在文章最后我會給出源碼,一看就明白)
在文件中寫了一個測試的 suspend 函數(shù)
suspend fun test(name: String) {
}
復(fù)制代碼
我們通過 Android Studio 再帶的工具羡蛾,如下圖:把 Kotlin 方法轉(zhuǎn)成 Java 方法
點(diǎn)這個按鈕
結(jié)果如下
public final class SuspendKt {
@Nullable
public static final Object test(@NotNull String name, @NotNull Continuation $completion) {
return Unit.INSTANCE;
}
}
復(fù)制代碼
看到了,我們的 suspend 的關(guān)鍵字锨亏,變成了 test
方法的一個Continuation參數(shù)痴怨,且為最后一個參數(shù)
看一下這個Continuation類,記住這個類器予,下面在分析的時候會遇到
@SinceKotlin("1.3")
public interface Continuation<in T> {
public val context: CoroutineContext
public fun resumeWith(result: Result<T>)
}
復(fù)制代碼
好目前的準(zhǔn)備工作都已經(jīng)完成浪藻,開始分析 Retrofit 是怎么支持 Kotlin 協(xié)程的掛起函數(shù)的。
10.3.Retrofit 是怎么支持 Kotlin 協(xié)程的掛起函數(shù)的乾翔。
經(jīng)過前面的源碼解讀爱葵,我們知道,最終會調(diào)用到 HttpServiceMethod 的 parseAnnotations
方法
10.3.1.我們再看下這個方法反浓,這次只看有關(guān)協(xié)程的部分
//HttpServiceMethod.java
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
//1 獲取 isKotlinSuspendFunction 的值萌丈,這個會在下面具體分析
boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
boolean continuationWantsResponse = false;
boolean continuationBodyNullable = false;
Annotation[] annotations = method.getAnnotations();
Type adapterType;
//2 如果是 Kotlin 掛起函數(shù)
if (isKotlinSuspendFunction) {
Type[] parameterTypes = method.getGenericParameterTypes();
Type responseType =
Utils.getParameterLowerBound(
0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]);
if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);
//3 continuationWantsResponse 賦值為 true
continuationWantsResponse = true;
} else {
}
adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
} else {
adapterType = method.getGenericReturnType();
}
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
okhttp3.Call.Factory callFactory = retrofit.callFactory;
if (!isKotlinSuspendFunction) {
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
} else if (continuationWantsResponse) {
//4 返回 SuspendForResponse 它是 HttpServiceMethod的子類
return (HttpServiceMethod<ResponseT, ReturnT>)
new SuspendForResponse<>(
requestFactory,
callFactory,
responseConverter,
(CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
} else {
return (HttpServiceMethod<ResponseT, ReturnT>)
new SuspendForBody<>(
requestFactory,
callFactory,
responseConverter,
(CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
continuationBodyNullable);
}
}
復(fù)制代碼
注釋 1:獲取 isKotlinSuspendFunction 的值,這個會在下面具體分析
注釋 2:如果是 Kotlin 掛起函數(shù)雷则,進(jìn)入此代碼塊
注釋 3:把 continuationWantsResponse 賦值為 true
注釋 4:返回 SuspendForResponse 它是 HttpServiceMethod 的子類辆雾,然后看它的 adapt
方法,這個會在下面具體分析
獲取 isKotlinSuspendFunction 的值的過程
10.3.2.看 requestFactory 的 isKotlinSuspendFunction 賦值
requestFactory 這個類月劈,我們之前分析過度迂,就是解析注解的,但是有一部分沒看猜揪,就是解析方法參數(shù)上的注解惭墓,這次就看下。
//RequestFactory.java
private @Nullable ParameterHandler<?> parseParameter(
int p, Type parameterType, @Nullable Annotation[] annotations, boolean allowContinuation) {
ParameterHandler<?> result = null;
if (annotations != null) {
for (Annotation annotation : annotations) {
ParameterHandler<?> annotationAction =
//1 遍歷解析參數(shù)的注解而姐,就是 @Path @Query @Field 等注解腊凶,具體就不看了,不是協(xié)程的重點(diǎn)
parseParameterAnnotation(p, parameterType, annotations, annotation);
if (annotationAction == null) {
continue;
}
if (result != null) {
throw parameterError(
method, p, "Multiple Retrofit annotations found, only one allowed.");
}
result = annotationAction;
}
}
if (result == null) {
//2 如果是協(xié)程 ,其實(shí)比的就是最后一個值
if (allowContinuation) {
try {
//3 判斷參數(shù)類型是 Continuation吭狡,這個接口尖殃,前面在 10.2 小節(jié)寫 Demo 時提過
if (Utils.getRawType(parameterType) == Continuation.class) {
// 4 isKotlinSuspendFunction 賦值為 true
isKotlinSuspendFunction = true;
return null;
}
} catch (NoClassDefFoundError ignored) {
}
}
throw parameterError(method, p, "No Retrofit annotation found.");
}
return result;
}
復(fù)制代碼
注釋 1:遍歷解析參數(shù)的注解,就是 @Path @Query @Field 等注解划煮,具體就不看了送丰,不是協(xié)程的重點(diǎn)
注釋 2:如果是協(xié)程 ,其實(shí)比的就是最后一個值
注釋 3:判斷參數(shù)類型是 Continuation弛秋,這個接口器躏,前面在 10.2 小節(jié)寫 Demo 時提過
注釋 4:isKotlinSuspendFunction 賦值為 true
如果isKotlinSuspendFunction 為 true 時,返回就是 SuspendForResponse 類
接下來就要 SuspendForResponse 以及它的 adapt
方法了
10.3.3.看一下SuspendForResponse類
static final class SuspendForResponse<ResponseT> extends HttpServiceMethod<ResponseT, Object> {
private final CallAdapter<ResponseT, Call<ResponseT>> callAdapter;
SuspendForResponse(
RequestFactory requestFactory,
okhttp3.Call.Factory callFactory,
Converter<ResponseBody, ResponseT> responseConverter,
CallAdapter<ResponseT, Call<ResponseT>> callAdapter) {
super(requestFactory, callFactory, responseConverter);
this.callAdapter = callAdapter;
}
@Override
protected Object adapt(Call<ResponseT> call, Object[] args) {
//1
call = callAdapter.adapt(call);
//noinspection unchecked Checked by reflection inside RequestFactory.
//2
Continuation<Response<ResponseT>> continuation =
(Continuation<Response<ResponseT>>) args[args.length - 1];
// See SuspendForBody for explanation about this try/catch.
try {
//3
return KotlinExtensions.awaitResponse(call, continuation);
} catch (Exception e) {
//4
return KotlinExtensions.suspendAndThrow(e, continuation);
}
}
}
復(fù)制代碼
注釋 1:調(diào)用 callAdapter 代理 call 方法
注釋 2:取出最后一個參數(shù)蟹略,強(qiáng)轉(zhuǎn)成 Continuation 類型登失,想想我們寫的 Demo
注釋 3:Call 的擴(kuò)展函數(shù)(Kotlin 的寫法)下面具體看下 awaitResponse
注釋 4:出現(xiàn)異常,拋出異常挖炬。所以我們要在代碼中揽浙,要主動 try catch,來處理錯誤
10.3.4.看一下Call的擴(kuò)展函數(shù)
//KotlinExtensions.kt
suspend fun <T> Call<T>.awaitResponse(): Response<T> {
return suspendCancellableCoroutine { continuation ->
continuation.invokeOnCancellation {
cancel()
}
//調(diào)用 Call的enqueue方法
enqueue(object : Callback<T> {
override fun onResponse(call: Call<T>, response: Response<T>) {
//成功回調(diào)
continuation.resume(response)
}
override fun onFailure(call: Call<T>, t: Throwable) {
//失敗回調(diào)
continuation.resumeWithException(t)
}
})
}
}
復(fù)制代碼
到現(xiàn)在意敛,整個用 Kotlin 協(xié)程的請求過程我們也就看完了馅巷。
11.總結(jié)
至此,整個 Retrofit 的整體流程就分析完了草姻,具體細(xì)節(jié)還需要好好研究钓猬,我們再總結(jié)一下,回答開始的問題
11.1.什么是動態(tài)代理撩独?
分兩點(diǎn)動態(tài)指的是在運(yùn)行期敞曹,而代理指的是實(shí)現(xiàn)了某個接口的具體類,稱之為代理综膀,會調(diào)用了 InvocationHandler 的 invoke
方法澳迫。
Retrofit 中的動態(tài)代理:
- 在代碼運(yùn)行中,會動態(tài)創(chuàng)建 GitHubApiService 接口的實(shí)現(xiàn)類僧须,作為代理對象纲刀,代理接口的方法
- 在我們調(diào)用GitHubApiService 接口的實(shí)現(xiàn)類的
listRepos
方法時,會調(diào)用了 InvocationHandler 的invoke
方法担平。 - 本質(zhì)上是在運(yùn)行期示绊,生成了 GitHubApiService 接口的實(shí)現(xiàn)類,調(diào)用了 InvocationHandler 的
invoke
方法暂论。
具體看第 6 節(jié)
11.2.整個請求的流程是怎樣的
- 我們在調(diào)用 GitHubApiService 接口的
listRepos
方法時面褐,會調(diào)用 InvocationHandler 的invoke
方法 - 然后執(zhí)行
loadServiceMethod
方法并返回一個 HttpServiceMethod 對象并調(diào)用它的invoke
方法 - 然后執(zhí)行 OkHttpCall的
enqueue
方法 - 本質(zhì)執(zhí)行的是 okhttp3.Call 的
enqueue
方法 - 當(dāng)然這期間會解析方法上的注解,方法的參數(shù)注解取胎,拼成 okhttp3.Call 需要的 okhttp3.Request 對象
- 然后通過 Converter 來解析返回的響應(yīng)數(shù)據(jù)展哭,并回調(diào) CallBack 接口
以上就是這個Retrofit 的請求流程
11.3.底層是如何用 OkHttp 請求的湃窍?
看下第 11.2小節(jié)的解釋吧
具體看第 8 節(jié)
11.4.方法上的注解是什么時候解析的,怎么解析的匪傍?
- 在 ServiceMethod.parseAnnotations(this, method); 方法中開始的
- 具體內(nèi)容是在 RequestFactory 類中您市,進(jìn)行解析注解的
- 調(diào)用 RequestFactory.parseAnnotations(retrofit, method); 方法實(shí)現(xiàn)的
具體看第 8.2 小節(jié)
11.5.Converter 的轉(zhuǎn)換過程,怎么通過 Gson 轉(zhuǎn)成對應(yīng)的數(shù)據(jù)模型的役衡?
- 通過成功回調(diào)的
parseResponse(rawResponse);
方法開始 - 通過 responseConverter 的
convert
方法 - responseConverter 是通過 converterFactories 通過遍歷茵休,根據(jù)返回值類型來使用對應(yīng)的 Converter 解析
具體看第 8.4 小節(jié)
11.6.CallAdapter 的替換過程,怎么轉(zhuǎn)成 RxJava 進(jìn)行操作的手蝎?
- 通過配置 addCallAdapterFactory(RxJava2CallAdapterFactory.create()) 在 callAdapterFactories 這個 list 中添加 RxJava2CallAdapterFactory
- 如果不是 Kotlin 掛起函數(shù)最終調(diào)用的是 CallAdapted 的
adapt
方法 - callAdapter 的實(shí)例是通過 callAdapterFactories 這個 list 通過遍歷榕莺,根據(jù)返回值類型來選擇合適的CallAdapter
具體看第 9 節(jié)
11.7.如何支持 Kotlin 協(xié)程的 suspend 掛起函數(shù)的?
- 通過 RequestFactory 解析方法上的參數(shù)值來判斷是不是一個掛起函數(shù)棵介,并把 isKotlinSuspendFunction 變量置為 true
- 根據(jù) isKotlinSuspendFunction 這個變量來判斷響應(yīng)類型是否是 Response 類型钉鸯,然后把continuationWantsResponse 置為 true
- 根據(jù) continuationWantsResponse 這個變量,來返回 SuspendForResponse 對象
- 并調(diào)用 SuspendForResponse 的 invoke 方法
- 通過 Call 的擴(kuò)展函數(shù)邮辽,來調(diào)用 Call 的 enqueue方法
- 通過協(xié)程來返回
具體看第 10 節(jié)
到此為止唠雕,這篇文章算寫完了,當(dāng)然還有很多具體細(xì)節(jié)沒有研究逆巍,但對 Retrofit 的各個方面都進(jìn)行了閱讀及塘。
作者:AboBack
鏈接:https://juejin.im/post/6869584323079569415