不忘初心 砥礪前行, Tomorrow Is Another Day !
相關文章
本文概要:
- 引言
- okHttp、urlConnection、httpClient、volley、retrofit之間的關係
- Retrofit的使用-簡單介紹
- Retrofit的原理-重點分析
一. 引言
在分析Retrofit之前,我們需要對基本的網路以及網路框架有個綜合的對比認識.
okHttp、urlConnection、httpClient、volley、retrofit之間的關係
- okHttp、urlConnection、httpClient 這三者是底層的網路庫.
- volley與retrofit屬於更高層的網路庫
- volley對urlConnection和httpClient的進行了封裝,由於volley良好的擴充套件性,它還能配置使用okHttp作為底層庫
- retrofit則是在okhttp的基礎上封裝了執行緒的切換及網路資料解析
二. Retrofit的使用
Retrofit的使用比較簡單,基本是就是通過三類註解方法註解,引數註解. 這裡就不再講述了,找了一篇個人感覺比較全面的文章你真的會用Retrofit2嗎?Retrofit2完全教程進行參考學習.我們重點進行原理分析.
三. 基本原理
3.1 Retrofit的建立
原始碼版本2.5.0
Retrofit是通過動態代理的方式建立各種網路介面的代理.
關於動態代理,筆者另起了篇講述.設計模式之代理模式如有興趣的可以看一下.最後我們迴歸正題.
使用過程一般都是通過建造者的build方法來構建Retorfit物件.我們就從Retorfit物件的建立開始,
使用示例-1
mRetrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.callFactory(okHttpClient)
.build();
複製程式碼
直接看build方法的對應原始碼
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
// 1.初始化callFactory,如果未設定則預設設定一個OkHttpClient
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
// 2.初始化回撥執行器,用於回撥到UI執行緒
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
// 3.初始化介面卡工廠集合,新增一個預設介面卡工廠(ExecutorCallAdapterFactory)
//用於適配返回型別,如預設支援的Call<T>
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
// 4.初始化轉換器集合
//用於網路響應的解析
List<Converter.Factory> converterFactories = new ArrayList<>(
1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());
//...省略部分程式碼
return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
}
複製程式碼
在build方法裡,初始化時會通過platform物件新增一些預設的介面卡,轉換器,以及回撥執行器.我們看具體的新增操作.
對應原始碼
static class Android extends Platform {
@IgnoreJRERequirement // Guarded by API check.
@Override boolean isDefaultMethod(Method method) {
if (Build.VERSION.SDK_INT < 24) {
return false;
}
return method.isDefault();
}
//新增預設回撥執行器
@Override public Executor defaultCallbackExecutor() {
return new MainThreadExecutor();
}
//新增預設的介面卡工廠(ExecutorCallAdapterFactory)
@Override List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
@Nullable Executor callbackExecutor) {
if (callbackExecutor == null) throw new AssertionError();
ExecutorCallAdapterFactory executorFactory = new ExecutorCallAdapterFactory(callbackExecutor);
return Build.VERSION.SDK_INT >= 24
? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
: singletonList(executorFactory);
}
@Override int defaultCallAdapterFactoriesSize() {
return Build.VERSION.SDK_INT >= 24 ? 2 : 1;
}
//新增預設的轉換器工廠
@Override List<? extends Converter.Factory> defaultConverterFactories() {
return Build.VERSION.SDK_INT >= 24
? singletonList(OptionalConverterFactory.INSTANCE)
: Collections.<Converter.Factory>emptyList();
}
@Override int defaultConverterFactoriesSize() {
return Build.VERSION.SDK_INT >= 24 ? 1 : 0;
}
static class MainThreadExecutor implements Executor {
//繫結主執行緒的looper
private final Handler handler = new Handler(Looper.getMainLooper());
@Override public void execute(Runnable r) {
//回撥到主執行緒執行
handler.post(r);
}
}
}
複製程式碼
至此Retorfit物件建立過程就分析完了,做一個小結.
對於Retorfit的建立,進行一系列的初始化操作.
- 初始化callFactory
- 初始化"三器"
- 回撥執行器,用於回撥到UI執行緒
- 介面卡工廠,用於適配返回的型別
- 轉換器工廠,用於解析網路響應
3.2 Call的建立
Call的建立過程實質是網路介面的實現過程
Rerofit物件建立完成後,呼叫create方法時來實現我們定義的網路介面ApiService,採用的就是動態代理.
使用示例-2
定義的網路介面類
public interface ApiService{
@GET
Observable<IDocViewBean> oaFileUpload(@Url String url,
@Query("token") String token,
@Query("mode") String mode,
@Query("url") String urlFile);
}
ApiService apiService = retrofit.create(ApiService.class);
複製程式碼
對應原始碼
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
/*
* 返回了網路介面的代理物件
*/
//當我們呼叫代理物件的介面方法時,會觸發InvocationHandler的invoke方法.
//如呼叫apiService.oaFileUpload(...)時,會完成oaFileUpload的實現,並返回一個Call<T>
//如呼叫apiService.oaFileUpload(...)時,會完成oaFileUpload的實現,並返回一個Call<T>
//如呼叫apiService.oaFileUpload(...)時,會完成oaFileUpload的實現,並返回一個Call<T>
//重要話說三遍,這一句理解了後面就容易了.
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 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);
}
//載入網路介面的方法,如載入oaFileUpload方法並呼叫.
/**
* 第一大步:先分析loadServiceMethod(method)獲取到網路介面方法物件(ServiceMethod物件)
* 第二大步:呼叫ServiceMethod的invoke.
*/
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
}
});
}
複製程式碼
3.2.1 第一大步
接著我們看loadServiceMethod的具體實現
對應原始碼
ServiceMethod<?> loadServiceMethod(Method method) {
//先從快取中獲取.
ServiceMethod<?> result = serviceMethodCache.get(method);
//有快取,直接返回.
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
//無快取,對網路介面的方法進行解析,封裝成ServiceMethod並新增進快取.
result = ServiceMethod.parseAnnotations(this, method);
serviceMethodCache.put(method, result);
}
}
return result;
}
複製程式碼
對網路介面的方法進行解析呼叫的了ServiceMethod.parseAnnotations,實際呼叫的是它的子類HttpServiceMethod.parseAnnotations方法.這裡我們直接看子類的方法.
對應原始碼
//HttpServiceMethod.java
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
//建立CallAdapter
CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method);
Type responseType = callAdapter.responseType();
if (responseType == Response.class || responseType == okhttp3.Response.class) {
throw methodError(method, "'"
+ Utils.getRawType(responseType).getName()
+ "' is not a valid response body type. Did you mean ResponseBody?");
}
if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
throw methodError(method, "HEAD method must use Void as response type.");
}
//建立轉換器
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
okhttp3.Call.Factory callFactory = retrofit.callFactory;
return new HttpServiceMethod<>(requestFactory, callFactory, callAdapter, responseConverter);
}
private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(
Retrofit retrofit, Method method) {
Type returnType = method.getGenericReturnType();
Annotation[] annotations = method.getAnnotations();
try {
//noinspection unchecked
//裡面通過retrofit物件的callAdapter方法獲取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);
}
}
複製程式碼
createCallAdapter方法呼叫了retrofit物件的callAdapter方法獲取CallAdapter物件.接著看retrofit的CallAdapter方法.
對應原始碼
//retrofit.java
public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
//再接著往下看
return nextCallAdapter(null, returnType, annotations);
}
public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
Annotation[] annotations) {
checkNotNull(returnType, "returnType == null");
checkNotNull(annotations, "annotations == null");
int start = callAdapterFactories.indexOf(skipPast) + 1;
//先遍歷callAdapterFactories集合
for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
//再通過網路介面的返回型別(returnType)來獲取介面卡工廠(CallAdapter.Factory),最終獲取到CallAdapter物件.
//再通過網路介面的返回型別(returnType)來獲取介面卡工廠(CallAdapter.Factory),最終獲取到CallAdapter物件.
//再通過網路介面的返回型別(returnType)來獲取介面卡工廠(CallAdapter.Factory),最終獲取到CallAdapter物件.
//重要的話說三遍.
//由於之前在Retrofit物件建立階段時,有預設設定CallAdapter.Factory為ExecutorCallAdapterFactory,那麼遍歷時就會獲取到它.
//最後一起看下ExecutorCallAdapterFactory的get方法.
CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
if (adapter != null) {
return adapter;
}
}
複製程式碼
//ExecutorCallAdapterFactory.java
@Override
public @Nullable CallAdapter<?, ?> get(
Type returnType, Annotation[] annotations, Retrofit retrofit) {
//檢測返回型別是不是CALL
if (getRawType(returnType) != Call.class) {
return null;
}
final Type responseType = Utils.getCallResponseType(returnType);
//返回一個CallAdapter物件,終於找到源頭了,不容易啊.至此第一大步到此為止
return new CallAdapter<Object, Call<?>>() {
@Override
public Type responseType() {
return responseType;
}
//...省略部分程式碼
};
}
複製程式碼
3.2.2 第二大步
呼叫ServiceMethod實際呼叫了HttpServiceMethod的invoke方法,我們直接看這個方法.
//HttpServiceMethod.java
@Override ReturnT invoke(Object[] args) {
/*因為之前第一大步我們已經清楚,預設情況下callAdapter是ExecutorCallAdapterFactory介面卡工廠下返回的,
那麼繼續往下看介面卡工廠找adapt
*/
return callAdapter.adapt(
new OkHttpCall<>(requestFactory, args, callFactory, responseConverter));
}
//ExecutorCallAdapterFactory.java
public @Nullable CallAdapter<?, ?> get(
Type returnType, Annotation[] annotations, Retrofit retrofit) {
//...省略部分程式碼
@Override
public Call<Object> adapt(Call<Object> call) {
//返回的Call實際上就是ExecutorCallbackCall
return new ExecutorCallbackCall<>(callbackExecutor, call);
}
};
}
複製程式碼
Call的建立過程相對來說比較複雜,需要反覆多瀏覽幾遍,關鍵在於冷靜下來根據線索去尋找.最後對Call的建立過程做一個總結.
- 當呼叫代理物件的網路介面方法時,會觸發InvocationHandler的invoke方法,最終返回一個Call.
- 在invoke方法裡面主要包含兩大步驟.
- 第一大步,獲取到網路介面方法物件(loadServiceMethod)
- 檢查是否有快取,有直接返回,無,重新解析生成新的物件後,快取再返回
- 解析時獲取介面卡(CallAdapter),遍歷介面卡工廠集合,通過網路介面的返回型別(returnType)來獲取對應介面卡工廠,最終獲得介面卡(CallAdapter).
- 第二大步,呼叫ServiceMethod的invoke傳送請求.
- 同樣利用獲取到的CallAdapter呼叫adapt方法,返回一個Call物件.最後傳送請求.
3.3 網路請求的傳送過程
最後我們看ExecutorCallbackCall是如何發請求和回撥的.
對應原始碼
static final class ExecutorCallbackCall<T> implements Call<T> {
//初始化時設定的執行緒回撥執行器
final Executor callbackExecutor;
//為OkHttpCall
final Call<T> delegate;
ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
this.callbackExecutor = callbackExecutor;
this.delegate = delegate;
}
//傳送網路請求
@Override public void enqueue(final Callback<T> callback) {
checkNotNull(callback, "callback == null");
delegate.enqueue(new Callback<T>() {//使用okhttpClient進行非同步請求
//網路回撥成功
@Override public void onResponse(Call<T> call, final Response<T> response) {
callbackExecutor.execute(new Runnable() {
@Override public void run() {
if (delegate.isCanceled()) {
//主執行緒回撥失敗
callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
} else {
//主執行緒回撥成功
callback.onResponse(ExecutorCallbackCall.this, response);
}
}
});
}
//網路回撥失敗
@Override public void onFailure(Call<T> call, final Throwable t) {
callbackExecutor.execute(new Runnable() {
@Override public void run() {
//主執行緒回撥失敗
callback.onFailure(ExecutorCallbackCall.this, t);
}
});
}
});
}
//...省略部分程式碼
}
複製程式碼
最後老規矩,對網路請求的傳送過程做一個小結.
通過enqueue方法,利用Okhttp傳送網路請求,網路響應成功或失敗通過回撥執行器(MainThreadExecutor)分發到主執行緒執行.
由於本人技術有限,如有錯誤的地方,麻煩大家給我提出來,本人不勝感激,大家一起學習進步.