Retrofit2實現原始碼分析
最近研究Retrofit2+RxJava實現網路請求及資料集處理,一部分的知識點就在Retrofit2,為了更好的理解程式碼,本人決定分析下Retrofit2原始碼,既然是分析原始碼就得帶著目的去分析,那麼說說本文要解決的問題吧,先看程式碼來說明
Retrofit retrofit = new Retrofit.Builder()
.client(new OkHttpClient())
.baseUrl(API_URL)
.addCallAdapterFactory(RxJavaCallAdapterFactory.createWithScheduler (Schedulers.io()))
.addConverterFactory(GsonConverterFactory.create())
.build();
ZhuanLanApi api = retrofit.create(ZhuanLanApi.class);
Call<ZhuanLanAuthor> call = api.getAuthor("qinchao");
call.enqueue(new Callback<ZhuanLanAuthor>() {
@Override
public void onResponse(Call <ZhuanLanAuthor> call, Response<ZhuanLanAuthor> response) {
System.out.println(response.body().getName());
}
@Override
public void onFailure(Call<ZhuanLanAuthor> call, Throwable t) {
}
});
以上程式碼就是單獨使用Retrofit2的基本方式,在寫該程式碼時,我有幾個疑惑,先列舉吧。
疑惑1: call.enqueue 是怎麼發起網路請求的,和Okhttp3發起的網路請求為什麼這麼相似
疑惑2: 建立retrofit物件時client函式傳的OkHttpClient物件有何作用
疑惑3: addCallAdapterFactory函式傳參有起到什麼作用
疑惑4: addConverterFactory函式傳參起到什麼作用
下面就來解決這些疑惑
1、Retrofit2和OkHttp3的關係
實際上Retrofit2是用來解決Okhttp3用法複雜的問題的,先看看用OkHttp3實現一個非同步網路請求的程式碼
String url = "https://www.baidu.com/";
OkHttpClient okHttpClient = new OkHttpClient();
MediaType JSON = MediaType.parse("application/json;charset=utf-8");
RequestBody body = RequestBody.create(JSON, "{'name':'yjing','age':'12'}");
Request request = new Request.Builder()
.post(body)
.url(url)
.build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
System.out.println("我是非同步執行緒,執行緒Id為:" + Thread.currentThread().getId());
}
});
注意,這裡只是舉個例子,傳的引數是沒有意義的,從程式碼可以看出OkHttp3使用太複雜,每次呼叫都得配置手動配置引數,而Retrofit2就是對OkHttp3進行了封裝,使得能夠通過呼叫一個Java方法來發起網路請求,網路請求實際上還是由OkHttp3完成的,Retrofit2只是將Java方法反射成為對應的網路請求。
由OkHttp3的使用程式碼可知,使用OkHttp3發起網路請求使通過Call物件來完成的,而建立Call例項物件,需要OkHttpClient物件和Request物件(這裡需要注意)。
2、Retrofit2框架結構說明
在對Retrofit2原始碼進行解析之前,先說說其框架結構
這裡的介面和類並不多
Converter:
負責轉換網路請求返回資料為對應格式資料的介面,其中GsonConverterFactory共廠類生產的GsonRequestBodyConverter類就是該介面的實現
CallAdapter:
負者決定retrofit.create()返回引數的介面,DefaultCallAdapterFactory以及RxJavaCallAdapterFactory連個工廠類生產的類都是該介面的實現
ServiceMethod:
這個類比較核心,幾乎儲存了一個API請求所有需要的資料,OkHttpCall需要從ServiceMethod中獲得一個Request物件,然後得到Response後,還需要傳入ServiceMethod用對應的Converter物件轉將Response資料換成資料格式
Retrofit:
生成Retrofit物件,並初始化上面的幾個介面實現類或ServiceMethod類物件
3、Retrofit類介紹
上面有程式碼說明Retrofit物件的初始化操作,這裡為了方便說明再貼一次
Retrofit retrofit = new Retrofit.Builder()
.client(new OkHttpClient())
.baseUrl(API_URL)
.addCallAdapterFactory(RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io()))
.addConverterFactory(GsonConverterFactory.create())
.build();
ZhuanLanApi api = retrofit.create(ZhuanLanApi.class);
通過build的一系列方法,完成了在Retrofit物件中例項化Convertor對應工廠類、CallAdapter對應工廠類以及OkHttpClient物件的初始化。
然後呼叫create方法
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, 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 serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
這裡的create方法中使用了動態代理,作用是當呼叫對應service方法時,會執行到動態代理程式碼中來,並且傳入service對應方法反射物件以及方法引數。而動態代理方法核心是最後三行程式碼,其中loadServiceMethod方法會例項化一個ServiceMethod物件,並例項化ServiceMethod物件中的一下幾個關鍵變數
下面先在三小節說說ServiceMethod方法的關鍵點
3、ServiceMethod類的關鍵點
ServiceMethod中有callAdapter變數、responseConverter變數、toRequest方法、toResponse方法這幾個關鍵點。
callAdapter變數
根據是否有呼叫addCallAdapterFactory方法來例項化,例如如果呼叫了addCallAdapterFactory(RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io()))則根據該工廠類建立SimpleCallAdapter(這裡只是列舉了該工廠類能生產的其中一種)物件
static final class SimpleCallAdapter implements CallAdapter<Observable<?>> {
private final Type responseType;
private final Scheduler scheduler;
SimpleCallAdapter(Type responseType, Scheduler scheduler) {
this.responseType = responseType;
this.scheduler = scheduler;
}
@Override public Type responseType() {
return responseType;
}
@Override public <R> Observable<R> adapt(Call<R> call) {
Observable<R> observable = Observable.create(new CallOnSubscribe<>(call)) //
.lift(OperatorMapResponseToBodyOrError.<R>instance());
if (scheduler != null) {
return observable.subscribeOn(scheduler);
}
return observable;
}
}
如果沒有呼叫addCallAdapterFactory方法則使用一下預設工廠類生成的CallAdapter物件。
final class DefaultCallAdapterFactory extends CallAdapter.Factory {
static final CallAdapter.Factory INSTANCE = new DefaultCallAdapterFactory();
@Override
public CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
if (getRawType(returnType) != Call.class) {
return null;
}
final Type responseType = Utils.getCallResponseType(returnType);
return new CallAdapter<Call<?>>() {
@Override public Type responseType() {
return responseType;
}
@Override public <R> Call<R> adapt(Call<R> call) {
return call;
}
};
}
}
這裡一定熬注意兩種CallAdapter物件的adapt方法的差異,不同的CallAdapterFactory生產的CallAdapter類,其adapt方法的返回引數不一樣,DefaultCallAdapterFactory 對應的返回引數是Call,而SimpleCallAdapter 對應的返回引數是Observable。
responseConverter變數
該變數負責根據對應規則對Response資料進行格式轉化,在Retrofit類的nextResponseBodyConverter方法中可以看到,當有呼叫addConverterFactory(GsonConverterFactory.create())使用GsonConverterFactory或其它的工廠類生產的轉換器,從程式碼中可以看出當新增多個工廠類時,使用第一個工廠類生產的轉換器。
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;
}
}
然後我們來看看GsonConverterFactory工廠類生產的物件
final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
private final Gson gson;
private final TypeAdapter<T> adapter;
GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
this.gson = gson;
this.adapter = adapter;
}
@Override public T convert(ResponseBody value) throws IOException {
JsonReader jsonReader = gson.newJsonReader(value.charStream());
try {
return adapter.read(jsonReader);
} finally {
value.close();
}
}
}
GsonResponseBodyConverter的convert方法會將ResponseBody 值轉化為對應Java方法的返回引數T型別,如下面的方法則轉化為ZhuanLanAuthor型別(解決疑惑4)。
Call<ZhuanLanAuthor> getAuthor(@Path("user") String user);
toResponse
T toResponse(ResponseBody body) throws IOException {
return responseConverter.convert(body);
}
這個方法將body資料轉化為對應引數資料。
toRequest
/** Builds an HTTP request from method arguments. */
Request toRequest(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();
}
這個方法,提供OkHttp3發起的網路請求需要的Request物件。
4、再談Retrofit類的create方法
大致介紹完Retrofit類和ServiceMethod後,接著看Retrofit的create方法,前面說了其最後三行是關鍵程式碼
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
其中第一行程式碼講了,是例項化ServiceMehod物件,第二行是例項化OkHttpCall物件,該OkHttpCall是Call介面的實現,最後呼叫了callAdapter.adapt方法。前面備註了要注意的就是這個adapt方法,不同的CallAdapter其對應的adapter方法不一樣,如前面所說DefaultCallAdapterFactory 工廠類生產的CallAdapter adapt方法對應的返回引數是Call,而SimpleCallAdapter 對應adapt方法的返回引數是Observable,這就為之後RxJava結合Retrofit2使用留下了鋪墊(解決疑惑3)。
我們看到當不新增.addCallAdapterFactory(RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io()))方法時,預設的CallAdater adapt方法直接來什麼引數返回什麼,也就是返回OKHttpCall物件。
到這裡應該知道在本文最開始在addCallAdapterFactory之後還使用了
Call<ZhuanLanAuthor> call = api.getAuthor("qinchao");
這種方式來返回Call在執行時,肯定是會報錯的,應為實際上api.getAuthor方法返回的是Observable變數,這地方只是為了方便後面講解,才那麼寫的。
5、call.enqueue發起網路請求
這裡的call.enqueue放棄網路請求的時候,實際上是OKHttpCall物件發起的網路請求,看起enqueue的部分程式碼
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
call = rawCall;
failure = creationFailure;
if (call == null && failure == null) {
try {
call = rawCall = createRawCall();
} catch (Throwable t) {
failure = creationFailure = t;
}
}
}
if (failure != null) {
callback.onFailure(this, failure);
return;
}
if (canceled) {
call.cancel();
}
call.enqueue(new okhttp3.Callback() {
@Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
throws IOException {
Response<T> response;
try {
response = parseResponse(rawResponse);
} catch (Throwable e) {
callFailure(e);
return;
}
callSuccess(response);
}
}
其中createRawCall中通過獲取ServiceMethod物件toRequest方法生成call物件,兒toRequest方法實際上就是獲取的我們傳入的.client(new OkHttpClient()) OkHttpClient物件(解決疑惑1、疑惑2)。
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;
}
然後通過call物件呼叫call.enqueue方法發起網路請求,並執行回撥。
到這裡對Retrofit2的原始碼分析就算完成了,在分析的過程中也算是解決了文章開始提出的四個疑問。
6、參考文獻
1、Retrofit2 原始碼解析
http://www.jianshu.com/p/c1a3a881a144
2、OkHttp3的基本用法
http://www.jianshu.com/p/1873287eed87
7、Demo地址
Android Demo:https://github.com/Yoryky/AndroidDemo.git