出來混遲早要還,先記個賬吧
馬瘦毛長蹄子肥,兒子偷爹不算賊,瞎大爺娶個瞎大奶奶,老兩口過了多半輩兒誰也沒看見誰!
準備
val INSTANCE: Retrofit = Retrofit.Builder()
.baseUrl("http://www.wanandroid.com/")
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(httpClient)
.build()
複製程式碼
先build()
一個Retrofit物件
open interface HomeList {
@GET("article/list/{pag}/json")
fun getHomeList(@Path("pag") pag:Int): Observable<HomeBean>
}
複製程式碼
準備一個介面
呼叫
var homeList: HomeList? = retrofit.create(HomeList::class.java)
val subscribe = homeList!!.getHomeList(HomeListState.page)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe( success, failed)
複製程式碼
建立代理
為什麼介面可以建立物件?為什麼介面的方法可以呼叫? 動態代理登場
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);
}
});
}
複製程式碼
撇開驗證,看看代理幹嘛了
Proxy 提供用於建立動態代理類和例項的靜態方法,它還是由這些方法建立的所有動態代理類的超類。
建立某一介面
Foo
的代理:InvocationHandler handler = new MyInvocationHandler(...); Class proxyClass = Proxy.getProxyClass( Foo.class.getClassLoader(), new Class[] { Foo.class }); Foo f = (Foo) proxyClass. getConstructor(new Class[] { InvocationHandler.class }). newInstance(new Object[] { handler }); 複製程式碼
或使用以下更簡單的方法:
Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class[] { Foo.class }, handler); 複製程式碼
動態代理類(
dynamic proxy class
)(以下簡稱為代理類(proxy
))是一個在執行時動態implements多個介面的類(也就是可以動態建立同時實現了多個介面的物件,同時這個物件還是Proxy類的子類),該類具有下面描述的行為。代理介面是代理類實現的一個介面。 代理例項 是代理類的一個例項。每個代理例項都可以通過實現介面
InvocationHandler
獲得一個關聯的呼叫處理程式(invocation handler
) 物件。通過其中某個代理介面的代理例項上的方法呼叫將被指派到例項的InvocationHandler.invoke
方法,並傳遞代理例項、識別呼叫方法的java.lang.reflect.Method
物件以及包含引數的Object
型別的陣列。呼叫處理程式以適當的方式處理編碼的方法呼叫,並且它返回的結果將作為代理例項上方法呼叫的結果返回。代理類具用以下屬性:
- 如果所有代理介面都是public的那麼所建立出來的代理類就都是public的final的,而且不是abstract的如果有任何一個代理介面不是public的那麼所建立出來的代理類就都不是public的
- 原文有兩條可以直接翻譯成程式碼,但是簡直不是人話
- Proxy classes are public, final, and not abstract if all proxy interfaces are public.
- Proxy classes are non-public, final, and not abstract if any of the proxy interfaces is non-public.
- 代理類在建立前名稱沒有限制。但是,以字串 "$Proxy" 開頭的類名空間應該為代理類保留。
- 代理類繼承自
java.lang.reflect.Proxy
。- 代理類會按建立時指定的介面的順序準確地實現每個介面。
- 如果代理類實現了非public介面,那麼它將會被定義在與該介面相同的包中。否則,代理類的包也是未指定的。注意,包密封將不阻止代理類在執行時在特定包中的成功定義,也不會阻止相同類載入器和帶有特定簽名的包所定義的類。
- 由於代理類將實現所有在其建立時指定的介面,所以對其
Class
物件呼叫getInterfaces
將返回一個包含相同介面列表的陣列(按其建立時指定的順序),對其Class
物件呼叫getMethods
將返回一個包括這些介面中所有方法的Method
物件的陣列,並且呼叫getMethod
將會在代理介面中找到期望的一些方法。- 如果給
Proxy.isProxyClass
方法傳遞的代理Class是由Proxy.getProxyClass
返回的Class,或是由Proxy.newProxyInstance
返回的物件的Class,則該方法返回true
,否則返回false
。- 代理類的
java.security.ProtectionDomain
與由引導類載入器載入的系統類相同,好比java.lang.Object
,原因是代理類的程式碼由受信任的系統程式碼生成。此保護域通常被授予java.security.AllPermission
。- 每個代理類都有一個公共構造方法,它接收一個引數實現自介面
InvocationHandler
此方法用於設定代理例項的呼叫處理程式,但是並非必須使用反射 API 才能訪問公共構造方法,通過呼叫Proxy.newInstance
方法其中利用了Proxy.getProxyClass
來呼叫處理程式後呼叫構造方法,也可以建立代理例項。代理例項具有以下屬性:
- 給定一個代理例項
proxy
和一個由其代理類Foo
實現的介面之一,下面的表示式將返回true:並且以下的強制轉換操作將會成功(而不丟擲proxy instanceof Foo 複製程式碼
ClassCastException
):(Foo) proxy 複製程式碼
- 每個代理例項都有一個關聯的呼叫處理程式,即傳遞給它的建構函式的呼叫處理程式(invocation handler)。靜態方法
Proxy.getInvocationHandler
將返回與代理例項關聯的呼叫處理程式作為其引數。- 代理例項上的介面方法呼叫將被編碼,並按照
InvocationHandler#invoke
方法的文件中所描述的那樣,傳送到呼叫處理程式(invocation handler)的InvocationHandler.invoke
方法中。- 在代理物件中呼叫
hashCode
、equals
或toString
這種在java.lang.Object
中宣告的方法同樣會按照上述規則編碼併傳送。被宣告為Method
型別的物件會視作java.lang.Object
物件傳送給invoke
方法。繼承自Object
的代理例項的其他公共方法不會被代理類覆蓋,因此這些方法的呼叫行為就像它們對java.lang.Object
的例項一樣。
這貼怎麼變成翻譯貼了(╯°Д°)╯︵ ┻━┻總之剩下的註釋就是說當有多個同名方法的情況下的處理規則實在懶得翻了——斜體字掀桌子更有氣勢了(  ̄ 3 ̄)y▂ξ
所以Retrofit .create
方法其本質在執行時建立一個物件,而這個物件呼叫介面宣告的方法時,會派發給InvocationHandler
介面的實現類並呼叫invoke
方法,而引數中的Method method
也就是當前呼叫方法的Method
物件,其中會包括方法在宣告時所寫的很多資訊,比如各種註解,不管是方法上的還是引數上的,而用到這些資訊的地方:
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
複製程式碼
就是這句建立了一個ServiceMethod
物件,這個類的程式碼太長了他的主要作用就是根據方法和引數上的註解生成request和response:
/** Builds an HTTP request from method arguments. */
Request toRequest(@Nullable Object... args) throws IOException {
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
contentType, hasBody, isFormEncoded, isMultipart);
@SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
int argumentCount = args != null ? args.length : 0;
if (argumentCount != handlers.length) {
throw new IllegalArgumentException("Argument count (" + argumentCount
+ ") doesn't match expected count (" + handlers.length + ")");
}
for (int p = 0; p < argumentCount; p++) {
handlers[p].apply(requestBuilder, args[p]);
}
return requestBuilder.build();
}
/** Builds a method return value from an HTTP response body. */
R toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
複製程式碼
生成回撥器
那麼這兩個方法是在哪裡會呼叫呢OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
這句程式碼中將serviceMethod
物件設定給了OkHttpCall
這個類可以說是OkHttp
庫中Call
類的包裝類由於都是自家產物兩者抽象層的方法末班都是一樣的:
Response<T> execute()
void enqueue(Callback responseCallback)
boolean isExecuted()
void cancel()
boolean isCanceled()
Call clone()
Request request()
複製程式碼
這些方法中enqueue
、execute
這些方法都會發起請求這時就會呼叫createRawCall
方法:
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.toRequest
方法生成request
物件並利用此物件生成OkHttp
庫中的Call
物件然後在request
、enqueue
、execute
方法中各種呼叫OkHttp
庫的對應的方法,也就是到這一步真正發起了請求。
請求
但是現在這個OkHttpCall
物件只是被建立出來其中的邏輯尚未被執行,它被放到了serviceMethod.callAdapter.adapt(okHttpCall);
中,而跟蹤程式碼可知callAdapt
物件是根據呼叫方法的返回值型別從最初構建Retrofit物件時.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
設定進去的介面卡列表中取出的,這也是為什麼他可以適配多種回撥方式的原因,此時我們才得到了真正的Call
根據不同介面卡的呼叫方法執行程式碼之後實際上會呼叫到okhttp3.Call
這個類中的enqueue
或者execute
方法此時真正發起請求。
響應
而當請求響應時在OkHttpCall
類中會先使用Response<T> parseResponse(okhttp3.Response rawResponse)
方法將響應包裝成自己的Response
在這個過程中如果響應code就會呼叫serviceMethod.toResponse
方法跟蹤程式碼這個方法中也就是利用構建Retrofit物件時.addConverterFactory(GsonConverterFactory.create())
設定進去的變換器來解析Response
並將結果返回,然後傳送給回撥介面。
在鄰居穿牆而過的歌聲中算完了帳,突然覺得沒有充QQ音樂的會員真是賺了,本文從頭流到尾,應該不適合飯後觀看,看久了會暈,暈了會想吐,吐了就會浪費糧食,所以我把提示寫在了最後,不要客氣,我是紅領巾。