Retrofit原始碼分析三 原始碼分析
使用方法
我們先來看一下Retrofit的常見使用方法:
//建立網路請求介面類
public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
//建立Retrofit例項物件
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
//通過動態代理建立網路介面代理物件
GitHubService service = retrofit.create(GitHubService.class);
//獲取Call物件
Call<List<Repo>> repos = service.listRepos("octocat");
//執行同步請求或非同步請求
repos.execute();
repos.enqueue(callback)
複製程式碼
上面是Retrofit的最基本使用方法,當然現在使用最多的還是RxJava2+Retrofit搭配使用,關於RxJava2,大家可以看我的另一篇 RxJava2原始碼分析 ,當然RxJava2與Retrofit搭配使用的解析我會在稍後分析,這裡我們先關注最基本的使用方法。
建立網路介面類
這一步的目的就是封裝我們網路請求相關的一些引數,沒什麼好多說的。
建立Retrofit例項物件
Retrofit例項物件的建立很明顯是採用了Builder模式,Builder模式在 Java語言中 建立一個 有很多可選配置引數 的物件的時候是很好的一種設計模式。Builder模式有兩個重點,一個是在 Java語言中 中,另一個是 有很多可選配置引數,其實在現在的Android開發中,使用Kotlin開發Android已經很普遍了,熟悉Kotlin語法的小夥伴可能很熟悉了,由於Kotlin中 預設引數 的存在,所以在Kotlin中使用Builder模式的意義不大。但由於Java語法的限制,在建立一個 有很多可選配置引數 的時候,Builder模式還是首要選擇。
我們來看一下Retrofit中的成員變數:
public final class Retrofit {
//快取封裝好的ServiceMethod
private final Map<Method, ServiceMethod<?, ?>> serviceMethodCache = new ConcurrentHashMap<>();
//OKHttp中的網路請求工廠
final okhttp3.Call.Factory callFactory;
//BaseUrl
final HttpUrl baseUrl;
//資料轉換器工廠集合
final List<Converter.Factory> converterFactories;
//網路請求介面卡工廠集合
final List<CallAdapter.Factory> callAdapterFactories;
//處理執行緒切換
final @Nullable Executor callbackExecutor;
//不需要關注,預設為false
final boolean validateEagerly;
//忽略無關程式碼......
}
複製程式碼
serviceMethodCache 是一個HashMap,它的Key是Method,代表我們定義的網路請求介面類中的方法,它的Value是ServiceMethod,代表對網路請求介面類中的方法的一個封裝,簡單看一下它就明白了:
final class ServiceMethod<R, T> {
private final okhttp3.Call.Factory callFactory;
private final CallAdapter<R, T> callAdapter;
private final HttpUrl baseUrl;
private final Converter<ResponseBody, R> responseConverter;
private final String httpMethod;
private final String relativeUrl;
private final Headers headers;
private final MediaType contentType;
private final boolean hasBody;
private final boolean isFormEncoded;
private final boolean isMultipart;
private final ParameterHandler<?>[] parameterHandlers;
//忽略無關程式碼.......
}
複製程式碼
很明顯了,ServiceMethod就是對網路請求引數的封裝類,包含請求頭、相對url、GET請求或者是POST請求等等對請求的配置資訊。serviceMethodCache 就是一個對ServiceMethod的快取,可以提高一定的效率。
在Retrofit中,預設的資料轉換器工廠就是 GsonConverterFactory ,所以其實如果我們是使用 Gson 來做資料轉換的,其實沒有必要去配置。在Android平臺,Retrofit中預設的網路請求介面卡工廠是 ExecutorCallAdapterFactory ,我們簡單瞅一眼它是如何被建立的:
static class Android extends Platform {
@Override public Executor defaultCallbackExecutor() {
return new MainThreadExecutor();
}
@Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
if (callbackExecutor == null) throw new AssertionError();
return new ExecutorCallAdapterFactory(callbackExecutor);
}
static class MainThreadExecutor implements Executor {
//注意這裡建立了一個位於主執行緒的Handler
private final Handler handler = new Handler(Looper.getMainLooper());
@Override public void execute(Runnable r) {
//通過handler.post(runnable)實現執行緒切換
handler.post(r);
}
}
}
複製程式碼
可以看到,在建立 ExecutorCallAdapterFactory 的同時傳入了一個 callbackExecutor ,這個 callbackExecutor 也是Retrofit中預設的 callbackExecutor ,在Android平臺中它是 MainThreadExecutor型別 ,可以看到,在它的內部建立了一個位於主執行緒的Handler。我們知道,使用Retrofit的時候不同於直接使用OKHttp,在使用Retrofit的非同步網路請求時,網路結果的回撥是位於主執行緒中的,那麼Retrofit是如何切換的執行緒,看過上面的程式碼應該會心裡有個數了,它是通過 handler.post(r) 來實現由子執行緒到主執行緒的切換。
通過動態代理建立網路介面代理物件
看過我的Retrofit原始碼分析第二篇:代理模式的小夥伴們應該都比較清楚了,在Retrofit的 create 方法中其實是使用了動態代理生成了一個代理物件,現在我們就來看一下 create 的原始碼:
public <T> T create(final Class<T> service) {
//忽略無關程式碼......
//下面就是JDK給我們提供的動態代理了
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 {
//忽略無關程式碼......
//獲取method的網路請求引數的封裝類ServiceMethod物件
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
//獲取對OKHttp中的RealCall的一個包裝類OkHttpCall物件
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
//通過網路請求介面卡將原始的Call物件轉換成需要的物件,比如RxJavaCallAdapter會將Call物件轉換成Observable。
return serviceMethod.adapt(okHttpCall);
}
});
}
複製程式碼
在 create 方法中做了3件事:
- 封裝網路請求方法
- 封裝原始Call物件
- 通過CallAdapter轉換Call物件 封裝網路請求方法很容易理解,關於ServiceMethod上面已經說過,不再贅述。封裝原始Call物件,對OKHttp原始碼熟悉的小夥伴應該知道,原始的Call其實就是OkHttp中的ReallCall。如果不熟悉OkHttp的同學可以看我之前的一篇 OkHttp原始碼分析 。我們可以簡單看一下這個包裝類 OkHttpCall :
final class OkHttpCall<T> implements Call<T> {
//忽略無關程式碼......
@Override public Response<T> execute() throws IOException {
okhttp3.Call call;
//熟悉OkHttp原始碼的同學應該很熟悉了,完全照抄OkHttp中的程式碼,確保每個Call只會被執行一次
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
//忽略無關程式碼......
call = rawCall;
if (call == null) {
try {
//建立OkHttp中的RealCall物件
call = rawCall = createRawCall();
} catch (IOException | RuntimeException | Error e) {
throwIfFatal(e); // Do not assign a fatal error to creationFailure.
creationFailure = e;
throw e;
}
}
}
if (canceled) {
call.cancel();
}
//解析OkHttp中的RealCall執行同步方法後返回的網路資料
return parseResponse(call.execute());
}
@Override public void enqueue(final Callback<T> callback) {
checkNotNull(callback, "callback == null");
okhttp3.Call call;
Throwable failure;
//確保一個Call物件只被執行一次
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
call = rawCall;
failure = creationFailure;
if (call == null && failure == null) {
try {
//建立OkHttp中的RealCall物件
call = rawCall = createRawCall();
} catch (Throwable t) {
throwIfFatal(t);
failure = creationFailure = t;
}
}
}
if (failure != null) {
callback.onFailure(this, failure);
return;
}
if (canceled) {
call.cancel();
}
//呼叫OkHttp中的RealCall的非同步請求網路方法
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
Response<T> response;
try {
response = parseResponse(rawResponse);
} catch (Throwable e) {
callFailure(e);
return;
}
try {
callback.onResponse(OkHttpCall.this, response);
} catch (Throwable t) {
t.printStackTrace();
}
}
@Override public void onFailure(okhttp3.Call call, IOException e) {
callFailure(e);
}
private void callFailure(Throwable e) {
try {
callback.onFailure(OkHttpCall.this, e);
} catch (Throwable t) {
t.printStackTrace();
}
}
});
}
}
複製程式碼
可以看到,Retrofit中的這個 OKHttpCall 完全是對OkHttp中的 RealCall 的一個包裝。在 OKHttpCall 中的同步網路方法 execute 和非同步網路方法 enQueue 中其實就做了三件事:獲取 RealCall 物件,呼叫 RealCall 中的 execute 或者 enQueue 方法,然後去解析OkHttp返回的原始資料並轉換成我們需要的型別,就這麼簡單。
接下來就 create 方法中就只剩下 通過CallAdapter轉換Call物件 了,我們上面說過,在沒有特別配置CallAdapter的時候,預設的CallAdapterFactory是 ExecutorCallAdapterFactory ,很明顯,CallAdapter是由Factory建立的,那我們看一下這個預設的CallAdapterFactory:
final class ExecutorCallAdapterFactory extends CallAdapter.Factory {
//本質是傳入的MainThreadExecutor
final Executor callbackExecutor;
ExecutorCallAdapterFactory(Executor callbackExecutor) {
this.callbackExecutor = callbackExecutor;
}
@Override
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
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;
}
@Override public Call<Object> adapt(Call<Object> call) {
//返回預設的CallAdapter轉換後的Call物件
return new ExecutorCallbackCall<>(callbackExecutor, call);
}
};
}
static final class ExecutorCallbackCall<T> implements Call<T> {
//本質是傳入的MainThreadExecutor
final Executor callbackExecutor;
//就是OKHttpCall,從名字也能理解,代理Call物件
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");
//呼叫OkHttp中RealCall的非同步網路請求
delegate.enqueue(new Callback<T>() {
@Override public void onResponse(Call<T> call, final Response<T> response) {
//注意,OkHttp的非同步回撥是在子執行緒的,Retrofit這裡實現了執行緒的切換,本質是呼叫了handler.post(runnable)
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) {
//注意,OkHttp的非同步回撥是在子執行緒的,Retrofit這裡實現了執行緒的切換,本質是呼叫了handler.post(runnable)
callbackExecutor.execute(new Runnable() {
@Override public void run() {
callback.onFailure(ExecutorCallbackCall.this, t);
}
});
}
});
}
@Override public boolean isExecuted() {
return delegate.isExecuted();
}
@Override public Response<T> execute() throws IOException {
//由於同步方法不需要切執行緒,所以直接執行並返回OKHttpCall的同步網路方法
return delegate.execute();
}
@Override public void cancel() {
delegate.cancel();
}
@Override public boolean isCanceled() {
return delegate.isCanceled();
}
@SuppressWarnings("CloneDoesntCallSuperClone") // Performing deep clone.
@Override public Call<T> clone() {
return new ExecutorCallbackCall<>(callbackExecutor, delegate.clone());
}
@Override public Request request() {
return delegate.request();
}
}
}
複製程式碼
上面的程式碼其實也很清晰了 ExecutorCallAdapterFactory 這個CallAdapter工廠類直接建立並返回一個CallAdapter的匿名物件,這個其實就是我們Retrofit的預設CallAdapter,關鍵是這個CallAdapter的adapt方法,它返回了 ExecutorCallbackCall 這個對OkHttpCall的包裝類,我們關注這個包裝類的 enqueue 方法,在這個方法中,它通過呼叫 callbackExecutor.execute
來實現了子執行緒到主執行緒的執行緒切換。這個 callbackExecutor 就是 MainThreadExecutor ,這個類我們上面提到過, MainThreadExecutor 中的 execute 方法內部就是 handler.post(r);
,這個 handler 其實就是主執行緒的,因此實現了執行緒切換。
獲取Call物件並執行同步或非同步方法
其實經過上一步的分析,我們已經知道了,我們獲取的Call物件,其實是通過動態代理中 serviceMethod.adapt(okHttpCall)
返回的Call物件,這個其實就是 ExecutorCallbackCall ,這個我們都很清楚了,它是對OkHttp中RealCall的一個包裝類,在它的非同步方法中通過呼叫 callbackExecutor.execute
實現了執行緒的切換,當然本質還是通過 Handler 機制。
其實到此為止,基本的流程已經分析完畢了,我們到這裡也會很清楚Retrofit的定位:Retrofit並不是一個網路請求的框架,而是一封裝網路請求引數,解析網路返回結果的框架。下面我們來分析一下使用了RxJava2CallAdapter的情況。
RxJava2CallAdapter的原理
CallAdapter的重點是它的adapt方法,我們來看一下RxJava2CallAdapter中的adapt方法:
@Override public <R> Object adapt(Call<R> call) {
//建立一個發射網路返回結果的Observable
Observable<Response<R>> responseObservable = new CallObservable<>(call);
Observable<?> observable;
if (isResult) {
observable = new ResultObservable<>(responseObservable);
} else if (isBody) {
observable = new BodyObservable<>(responseObservable);
} else {
observable = responseObservable;
}
//預設scheduler為空
if (scheduler != null) {
observable = observable.subscribeOn(scheduler);
}
if (isFlowable) {
return observable.toFlowable(BackpressureStrategy.LATEST);
}
if (isSingle) {
return observable.singleOrError();
}
if (isMaybe) {
return observable.singleElement();
}
if (isCompletable) {
return observable.ignoreElements();
}
return observable;
}
複製程式碼
這段程式碼本質是建立一個發射網路返回結果的Observable,我們看一下 CallObservable 的內部實現,看過RxJava2原始碼的同學應該都知道,Observable的關鍵方法是 subscribeActual,我們就看一下這個方法內部都做了什麼:
@Override protected void subscribeActual(Observer<? super Response<T>> observer) {
// Since Call is a one-shot type, clone it for each new observer.
Call<T> call = originalCall.clone();
observer.onSubscribe(new CallDisposable(call));
boolean terminated = false;
try {
//呼叫了OKHttpCall的同步網路訪問方法,並獲取網路資料
Response<T> response = call.execute();
if (!call.isCanceled()) {
//將獲取到的網路資料傳送到觀察者observer
observer.onNext(response);
}
if (!call.isCanceled()) {
terminated = true;
//傳送結束事件
observer.onComplete();
}
} catch (Throwable t) {
Exceptions.throwIfFatal(t);
if (terminated) {
RxJavaPlugins.onError(t);
} else if (!call.isCanceled()) {
try {
//傳送錯誤事件
observer.onError(t);
} catch (Throwable inner) {
Exceptions.throwIfFatal(inner);
RxJavaPlugins.onError(new CompositeException(t, inner));
}
}
}
}
複製程式碼
熟悉RxJava2的同學看完就應該懂了:RxJava2CallAdapter通過adapt方法生成並返回一個 CallObservable 物件,在這個 CallObservable 內部通呼叫 OKHttpCall 的 execute() 方法進行網路訪問,並將獲取到的資料傳送到下一級的觀察者observer中。
到此為止,Retrofit的原始碼分析終於結束了,其實它並不難,但想要完全理解整個網路訪問流程,除了要明白Retrofit,還需要了解OKHttp甚至是RxJava,對後兩者不太熟悉的小夥伴們可以看我的另外兩篇原始碼分析:OkHttp原始碼分析 , RxJava2原始碼分析 ,在熟悉了OKHttp和RxJava2的基礎上再回過頭來看Retrofit,相信你會有更深的理解。