源碼分析Retrofit請求流程
Retrofit
是 square
公司的另一款廣泛流行的網絡請求框架。前面的一篇文章《源碼分析OKHttp執行過程》已經對 OkHttp
網絡請求框架有一個大概的了解。今天同樣地對 Retrofit
的源碼進行走讀,對其底層的實現邏輯做到心中有數。
0x00 基本用法
Retrofit
的項目地址為:https://github.com/square/retrofit
打開項目目錄下的 samples
文件夾,從這裏可以瀏覽 Retrofit
項目的使用範例。
在本文中打開SimpleService.java
這個類作為源碼走讀的入口。這個類很簡單,展示了 Retrofit
的基本用法
public final class SimpleService { //定義接口請求地址 public static final String API_URL = "https://api.github.com"; //定義接口返回數據的實體類 public static class Contributor { public final String login; public final int contributions; public Contributor(String login, int contributions) { this.login = login; this.contributions = contributions; } } //定義網絡請求接口 public interface GitHub { //這個是請求github項目代碼貢獻者列表的接口 //使用@GET註解指定GET請求,並指定接口請求路徑,使用大括號{}定義的參數,是形參,retrofit會把方法中的 //@Path 傳入到請求路徑中 @GET("/repos/{owner}/{repo}/contributors") Call<List<Contributor>> contributors( @Path("owner") String owner, @Path("repo") String repo); } public static void main(String... args) throws IOException { // 創建一個retrofit,並且指定了接口的baseUrl // 然後設置了一個gson轉換器,用於將接口請求下來的json字符串轉換為Contributor實體類。 Retrofit retrofit = new Retrofit.Builder() .baseUrl(API_URL) .addConverterFactory(GsonConverterFactory.create()) .build(); // 這裏是魔法所在,retrofit將程序猿定義的接口變成“實現類” GitHub github = retrofit.create(GitHub.class); //通過retrofit這個“實現類”執行contributors方法 Call<List<Contributor>> call = github.contributors("square", "retrofit"); // 執行Call類中的execute方法,這是一個同步方法 // 當然跟okhttp一樣,異步方法是enqueue,這個下文會提到 List<Contributor> contributors = call.execute().body(); for (Contributor contributor : contributors) { System.out.println(contributor.login + " (" + contributor.contributions + ")"); } } }
通過上面代碼的閱讀,知道 retrofit
使用流程
- 定義
API
- 構造接口數據實體類
- 構造
retrofit
對象,指定baseUrl
和數據轉換器(即接口數據解析器,如對json
、xml
、protobuf
等數據類型的解析) - 通過
retrofit
將程序猿定義的API
接口變成"實現類" - 執行“實現類”的方法
- 執行網絡請求,獲取接口請求數據
這個流程關鍵點是4、5、6,下文將詳細對這幾個步驟的源碼進行閱讀。
在繼續下文之前,我們先看看這個SimpleService
的執行結果,它打印了retrofit
這個項目的代碼貢獻者
JakeWharton (928) swankjesse (240) pforhan (48) eburke (36) dnkoutso (26) NightlyNexus (26) edenman (24) loganj (17) Noel-96 (16) rcdickerson (14) rjrjr (13) kryali (9) adriancole (9) holmes (7) swanson (7) JayNewstrom (6) crazybob (6) Jawnnypoo (6) danrice-square (5) vanniktech (5) Turbo87 (5) naturalwarren (5) guptasourabh04 (4) artem-zinnatullin (3) codebutler (3) icastell (3) jjNford (3) f2prateek (3) PromanSEW (3) koalahamlet (3)
0x01 構造過程
從上文的源碼閱讀中,可以看出程序猿只是定義了一個接口,但是現在實現接口的工作是由 retrofit
來實現的
GitHub github = retrofit.create(GitHub.class);
Call<List<Contributor>> call = github.contributors("square", "retrofit");
create
打開 retrofit.create
方法
public <T> T create(final Class<T> service) { //對接口進行校驗 Utils.validateServiceInterface(service); if (validateEagerly) { eagerlyValidateMethods(service); } //通過Proxy創建了一個代理 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); } //判斷是否為默認方法,Java8中接口也可以有默認方法,所以這裏有這個判斷 if (platform.isDefaultMethod(method)) { return platform.invokeDefaultMethod(method, service, proxy, args); } //關鍵點 return loadServiceMethod(method).invoke(args != null ? args : emptyArgs); } }); }
這個方法很短,關鍵是通過 Proxy
創建了一個 Github
接口的代理類並返回該代理。
newProxyInstance
方法需要3個參數:ClassLoader
、Class<?>
數組、InvocationHandler
回調。
這個 InvocationHandler
非常關鍵,當執行接口 Github
的contributors
方法時,會委托給InvocationHandler
的invoke
方法來執行。即Github
將接口代理給了Proxy
來執行了。
InvocationHandler
接著看InvocationHandler
接口的實現。
在 invoke
方法中有三個參數,其中proxy
就是代理對象,而 method
就是程序猿定義的那個網絡請求接口,顧名思義 args
就是方法的參數。
此方法最終是調用了
loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
loadServiceMethod
打開 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;
}
這個方法就是通過 method
來獲取一個 ServiceMethod
對象。
ServiceMethod
打開 ServiceMethod
發現它是一個抽象類,有一個靜態方法 parseAnnotations
和一個抽象方法 invoke
。
abstract class ServiceMethod<T> {
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
//對註解進行解析
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
//獲取方法的返回類型
Type returnType = method.getGenericReturnType();
//對返回類型進行校驗
if (Utils.hasUnresolvableType(returnType)) {
throw methodError(method,
"Method return type must not include a type variable or wildcard: %s", returnType);
}
if (returnType == void.class) {
throw methodError(method, "Service methods cannot return void.");
}
//最終使用到HttpServiceMethod類
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
abstract T invoke(Object[] args);
}
parseAnnotations
方法就是對程序猿定義的接口中使用的註解進行解析。
最後是使用了HttpServiceMethod.parseAnnotations
方法
HttpServiceMethod
/** Adapts an invocation of an interface method into an HTTP call. */
final class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> {
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method);
//...省略部分代碼
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
okhttp3.Call.Factory callFactory = retrofit.callFactory;
return new HttpServiceMethod<>(requestFactory, callFactory, callAdapter, responseConverter);
}
//...省略部分代碼
@Override ReturnT invoke(Object[] args) {
return callAdapter.adapt(
new OkHttpCall<>(requestFactory, args, callFactory, responseConverter));
}
}
HttpServiceMethod
是 ServiceMethod
的子類。而在parseAnnotations
方法中構造了HttpServiceMethod
實例並返回。
因此,loadServiceMethod
方法返回的是HttpServiceMehod
對象
這樣下面代碼的執行實際上是執行了 HttpServiceMehod
的 invoke
方法。
loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
再次翻看上文中HttpServiceMethod
類
@Override ReturnT invoke(Object[] args) {
return callAdapter.adapt(
new OkHttpCall<>(requestFactory, args, callFactory, responseConverter));
}
invoke
方法裏有執行了callAdapter.adapt
方法,參數為OkHttpCall
,這個類實際上就是對okhttp
網絡請求的封裝,這裏也可以看出retrofit
內部是使用了okhttp
來執行網絡請求的
CallAdapter
public interface CallAdapter<R, T> {
//..省略部分代碼
T adapt(Call<R> call);
//CallAdapter抽象工廠類
abstract class Factory {
//返回CallAdapter實例
public abstract @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations,
Retrofit retrofit);
//..省略部分代碼
}
}
這是一個接口,內部有一個Factory
抽象工廠類,用於獲取CallAdapter
對象。
CallAdapter
有很多子類,那 callAdapter.adapt
方法執行的是哪個具體類的方法呢?實際上,從調試代碼中可以發現是調用DefaultCallFactory
中的內部實現類
DefaultCallAapterFactory
final class DefaultCallAdapterFactory extends CallAdapter.Factory {
static final CallAdapter.Factory INSTANCE = new DefaultCallAdapterFactory();
@Override public @Nullable CallAdapter<?, ?> get(
Type returnType, Annotation[] annotations, Retrofit retrofit) {
if (getRawType(returnType) != Call.class) {
return null;
}
final Type responseType = Utils.getCallResponseType(returnType);
//返回一個CallAapter實例
return new CallAdapter<Object, Call<?>>() {
@Override public Type responseType() {
return responseType;
}
@Override public Call<Object> adapt(Call<Object> call) {
//將參數返回,而這個參數就是OKHttpCall的實例
return call;
}
};
}
}
可以發現,在adapt
方法中就是將參數call
返回。
所以下面代碼返回的是OkHttpCall
對象。
loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
綜上
//創建了Github接口的代理類
GitHub github = retrofit.create(GitHub.class);
//執行接口的方法,其實就是調用了代理類的方法,並最終返回了一個OKhttpCall對象
//而這個對象就是對Okhttp的封裝
Call<List<Contributor>> call = github.contributors("square", "retrofit");
0x02 執行結果
上文中獲取到OKhttpCall
對象,它只是把接口請求過程進行了封裝,並沒有真正的獲取到接口數據。要獲取到接口數據還需要調用OkHttpCall.execute
方法
List<Contributor> contributors = call.execute().body();
Call.execute 或 Call.enqueue
這裏的請求過程與前文中《源碼分析OKHttp執行過程》介紹的是類似的。接一下
打開OkHttpCall.execute
方法
@Override public Response<T> execute() throws IOException {
okhttp3.Call call;
synchronized (this) {
if (executed) throw new IllegalStateException("Already executed.");
executed = true;
if (creationFailure != null) {
if (creationFailure instanceof IOException) {
throw (IOException) creationFailure;
} else if (creationFailure instanceof RuntimeException) {
throw (RuntimeException) creationFailure;
} else {
throw (Error) creationFailure;
}
}
call = rawCall;
if (call == null) {
try {
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();
}
return parseResponse(call.execute());
}
這裏的執行邏輯也很簡單
- 使用
synchronized
進行同步操作 - 進行異常處理
- 調用
createRawCall
創建okhttp3.Call
對象 - 執行
okhttp
的Call.execute
方法,並解析response
後返回請求結果
同樣地,異步請求操作也是類似的
打開OkHttpCall.enqueue
方法
@Override public void enqueue(final Callback<T> callback) {
checkNotNull(callback, "callback == null");
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 {
//創建okhttp網絡請求
call = rawCall = createRawCall();
} catch (Throwable t) {
throwIfFatal(t);
failure = creationFailure = t;
}
}
}
if (failure != null) {
callback.onFailure(this, failure);
return;
}
if (canceled) {
call.cancel();
}
//最終是執行了OkHttp中的call.enqueue方法
//並回調相應的接口
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) {
throwIfFatal(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();
}
}
});
}
這個方法其實最終都是執行了okhttp
的相應方法。
0x03 總結
Retrofit
其實一種更加高級的網絡應用框架,通過代理模式簡化了接口的定義,無需提供接口的具體實現就可以完成網絡接口請求的執行。它的底層實際上是封裝了 okhttp
的執行過程,也把對網絡的操作進行了封裝,而對於程序猿來說只需要關註業務邏輯,對網絡請求的具體實現不必關心。
例如在本文開頭的實例中我們只需要定義接口,定義實體類,其他工作都交給了 Retrofit
,接下來就是Magic
。
源碼分析Retrofit請求流程