Retrofit 2.x 系列之二 - Retrofit的源碼解析

文章使用的Retrofit版本爲:2.3.0

在前面的文章已經介紹過了Retrofit的使用,這篇文章主要是分析Retrofit的源碼。

源碼解析

首先我們從Retrofit的初始化配置開始說起:

Retrofit retrofit = new Retrofit.Builder()
        .client(new OkHttpClient.Builder().build())
        .baseUrl("http://www.baidu.com/")
        .addConverterFactory(GsonConverterFactory.create())
        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
        .build();

這是我們常見的Retrofit初始化的代碼,從 Builder().build() 就能明白Retrofit和okhttp一樣是使用建造者模式來構建對象的,看源碼我們先看這個類的成員變量,好的代碼規範從命名我們就能初步理解這個類大概是做哪些事:

public static final class Builder {
    private final Platform platform; // 運行的平臺,Retrofit可以運行在ios、Android、Java3個平臺環境下,會根據當前環境去適配不同的平臺
    @Nullable
    private Factory callFactory;// Okhttp的工廠類
    private HttpUrl baseUrl;// baseUrl,HttpUrl類型
    private final List<retrofit2.Converter.Factory> converterFactories; // 數據轉換器的集合,主要作用是將網絡請求返回的數據轉換成用戶想要的格式,用戶可自定義解析方式,比如Gson、FastJson
    private final List<retrofit2.CallAdapter.Factory> adapterFactories; // 數據適配器的集合,主要作用是將接口返回的對象適配成用戶想要的對象,用戶可自定義,比如RxJava
    @Nullable
    private Executor callbackExecutor;// 回調執行器,網絡請求返回數據通過這個進行回調
    private boolean validateEagerly;// 是否提前解析註解等信息
    
    //省略其餘代碼...
}

代碼中的註釋是我自己加上去的,註釋很詳細,這裏就不多解釋了。

我們跟着 .build() 方法進去看源碼:

public Retrofit build() {
    if (this.baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
    } else {
        Factory callFactory = this.callFactory;
        if (callFactory == null) {
            callFactory = new OkHttpClient();
        }

        Executor callbackExecutor = this.callbackExecutor;
        if (callbackExecutor == null) {
            callbackExecutor = this.platform.defaultCallbackExecutor();
        }

        List<retrofit2.CallAdapter.Factory> adapterFactories = new ArrayList(this.adapterFactories);
        adapterFactories.add(this.platform.defaultCallAdapterFactory(callbackExecutor));
        List<retrofit2.Converter.Factory> converterFactories = new ArrayList(this.converterFactories);
        return new Retrofit((Factory)callFactory, this.baseUrl, converterFactories, adapterFactories, callbackExecutor, this.validateEagerly);
    }
}

build() 方法中首先就判斷了baseUrl是否爲空,如果爲空會拋出一個異常提示 Base URL required. 這也是爲什麼在 Retrofit 2.x 系列之一 - Retrofit的使用姿勢(基礎篇) 這篇文章中說 baseUrl()必需項了。
當baseUrl不爲空時,走到else的代碼分支,判斷 this.callFactory 是否爲空,如果爲空則初始化一個默認的 OkhttpClient 對象,同樣也驗證了基礎篇文章中說的 client()可選項,這一步我們先停下來看 this.callFactory 在哪裏被賦值的:

 public Retrofit.Builder client(OkHttpClient client) {
     return this.callFactory((Factory)Utils.checkNotNull(client, "client == null"));
 }

 public Retrofit.Builder callFactory(Factory factory) {
     this.callFactory = (Factory)Utils.checkNotNull(factory, "factory == null");
     return this;
 }

通過 Builder().client() 方法傳入 OkhttpClient 實例,緊接着調用自己的 callFactory() 方法,在該方法內部對 callFactory 這個成員變量賦值。我們回到 build() 方法繼續看。
判斷完 callFactory 之後,判斷 callbackExecutor ,這個變量我們一般都不會去配置它,所以這裏默認爲null,Retrofit就會使用各自平臺的 defaultCallbackExecutor 。因爲我們是 Android 中使用,我們去看Android平臺默認的 callbackExecutor 是什麼:

static class Android extends Platform {
    Android() {
    }

    public Executor defaultCallbackExecutor() {
        return new Platform.Android.MainThreadExecutor();
    }

    Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
        if (callbackExecutor == null) {
            throw new AssertionError();
        } else {
            return new ExecutorCallAdapterFactory(callbackExecutor);
        }
    }

    static class MainThreadExecutor implements Executor {
        private final Handler handler = new Handler(Looper.getMainLooper());

        MainThreadExecutor() {
        }

        public void execute(Runnable r) {
            this.handler.post(r);
        }
    }
}

代碼中可以看到無參的 defaultCallbackExecutor() 方法中返回的是 MainThreadExecutor,也就是將返回接口切換到主線程中執行。

最後 build() 方法創建了 adapterFactoriesconverterFactories 的集合,並將我們添加進去的數據轉換器(本文中爲 GsonConverterFactory)、數據適配器(本文中爲 RxJavaCallAdapterFactory)傳入集合,然後使用配置好的所有參數構建了Retrofit對象。至此Retrofit就創建好了。

初始化配置完成後,第二步就是與網絡請求的接口類配合,創建能夠發起網絡請求的對象。但是在Retrofit的使用步驟中,我們是創建了一個接口來配置網絡請求的方式、參數、Header等等,接口是不能實例化的,所以,Retrofit使用了動態代理的方式(非常重要),動態代理的相關知識可以看:你真的完全瞭解Java動態代理嗎?看這篇就夠了 這篇文章,裏面描述了動態代理的使用場景、設計思想和原理。我們看一下Retrofit是如何使用動態代理的:

Api api = retrofit.create(Api.class);

我們跟着 create() 方法進入源碼:

public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (this.validateEagerly) {
        this.eagerlyValidateMethods(service);
    }

    return Proxy.newProxyInstance(service.getClassLoader(), new Class[]{service}, new InvocationHandler() {
        private final Platform platform = Platform.get();

        public Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {
            if (method.getDeclaringClass() == Object.class) {
                return method.invoke(this, args);
            } else if (this.platform.isDefaultMethod(method)) {
                return this.platform.invokeDefaultMethod(method, service, proxy, args);
            } else {
                ServiceMethod<Object, Object> serviceMethod = Retrofit.this.loadServiceMethod(method);
                OkHttpCall<Object> okHttpCall = new OkHttpCall(serviceMethod, args);
                return serviceMethod.callAdapter.adapt(okHttpCall);
            }
        }
    });
}

create() 方法中第一步先驗證一下傳遞進來的接口類的合法性,第二步判斷是否要提前解析接口類中的方法,第三步就是創建動態代理了。這裏的動態代理會將我們編寫的請求接口類中所有方法,都委託到這個動態代理去處理,每當我們調用一個方法,動態代理中 InvocationHandler 對象的 invoke 方法就會執行一次。 invoke 方法中前兩個 if 代碼塊中都是別的平臺(ios、java8),我們只要關注最後的 else 代碼塊,裏面的三行代碼就是Retrofit最重要的地方了,我們跟着第一個方法 loadServiceMethod 進去看:

 ServiceMethod<?, ?> loadServiceMethod(Method method) {
     ServiceMethod<?, ?> result = (ServiceMethod)this.serviceMethodCache.get(method);
     if (result != null) {
         return result;
     } else {
         Map var3 = this.serviceMethodCache;
         synchronized(this.serviceMethodCache) {
             result = (ServiceMethod)this.serviceMethodCache.get(method);
             if (result == null) {
                 result = (new retrofit2.ServiceMethod.Builder(this, method)).build();
                 this.serviceMethodCache.put(method, result);
             }

             return result;
         }
     }
 }

該方法會先從緩存中獲取 ServiceMethod 對象,如果獲取不到就利用 Builder 創建一個,再加入緩存。我們看看 ServiceMethod.Builder對象是什麼,因爲 Builder 是用來創建 ServiceMethod 的,知道了 Builder 就能知道 ServiceMethod,和查看 Retrofit 類一樣,先看下成員變量都有什麼:

static final class Builder<T, R> {
    final Retrofit retrofit;
    final Method method;
    final Annotation[] methodAnnotations; // 方法的註解:@Post、@Header 等等
    final Annotation[][] parameterAnnotationsArray; // 方法中參數的註解:@Field、@Query 等等
    final Type[] parameterTypes; //參數類型
    Type responseType; //返回類型
    boolean gotField;
    boolean gotPart;
    boolean gotBody;
    boolean gotPath;
    boolean gotQuery;
    boolean gotUrl;
    String httpMethod; // 請求方法
    boolean hasBody; // 是否有請求體
    boolean isFormEncoded; // 是否是表單
    boolean isMultipart; // 是否是文件
    String relativeUrl;
    Headers headers; // 請求頭
    MediaType contentType; // 媒體類型
    Set<String> relativeUrlParamNames;
    ParameterHandler<?>[] parameterHandlers; // 根據請求方法將參數中的註解處理成網絡請求的請求體內容
    Converter<ResponseBody, T> responseConverter; //返回數據的轉換器
    CallAdapter<T, R> callAdapter; // 方法返回值得對象適配器
    //省略其餘代碼...
}

代碼中添加了註釋,通過註釋我們能知道,這個 ServiceMethod 類就是對我們自定義的 ApiService接口 中的所有註解、方法、參數逐一去進行解析,解析的步驟在該類的 build() 方法中:

 public ServiceMethod build() {
     this.callAdapter = this.createCallAdapter();
     this.responseType = this.callAdapter.responseType();
     if (this.responseType != Response.class && this.responseType != okhttp3.Response.class) {
         this.responseConverter = this.createResponseConverter();
         Annotation[] var1 = this.methodAnnotations;
         int p = var1.length;
         
         for(int var3 = 0; var3 < p; ++var3) {
             Annotation annotation = var1[var3];
             this.parseMethodAnnotation(annotation);
         }

         if (this.httpMethod == null) {
             throw this.methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
         } else {
             if (!this.hasBody) {
                 if (this.isMultipart) {
                     throw this.methodError("Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
                 }

                 if (this.isFormEncoded) {
                     throw this.methodError("FormUrlEncoded can only be specified on HTTP methods with request body (e.g., @POST).");
                 }
             }

             int parameterCount = this.parameterAnnotationsArray.length;
             this.parameterHandlers = new ParameterHandler[parameterCount];

             for(p = 0; p < parameterCount; ++p) {
                 Type parameterType = this.parameterTypes[p];
                 if (Utils.hasUnresolvableType(parameterType)) {
                     throw this.parameterError(p, "Parameter type must not include a type variable or wildcard: %s", parameterType);
                 }

                 Annotation[] parameterAnnotations = this.parameterAnnotationsArray[p];
                 if (parameterAnnotations == null) {
                     throw this.parameterError(p, "No Retrofit annotation found.");
                 }

                 this.parameterHandlers[p] = this.parseParameter(p, parameterType, parameterAnnotations);
             }

             if (this.relativeUrl == null && !this.gotUrl) {
                 throw this.methodError("Missing either @%s URL or @Url parameter.", this.httpMethod);
             } else if (!this.isFormEncoded && !this.isMultipart && !this.hasBody && this.gotBody) {
                 throw this.methodError("Non-body HTTP method cannot contain @Body.");
             } else if (this.isFormEncoded && !this.gotField) {
                 throw this.methodError("Form-encoded method must contain at least one @Field.");
             } else if (this.isMultipart && !this.gotPart) {
                 throw this.methodError("Multipart method must contain at least one @Part.");
             } else {
                 return new ServiceMethod(this);
             }
         }
     } else {
         throw this.methodError("'" + Utils.getRawType(this.responseType).getName() + "' is not a valid response body type. Did you mean ResponseBody?");
     }
 }

第一個for循環中的 this.parseMethodAnnotation(annotation); 主要是對請求方法(@GET、@POST、@DELETE等等)進行解析。

然後我們回到創建動態代理的 invoke() 方法源碼中:

ServiceMethod<Object, Object> serviceMethod = Retrofit.this.loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);

通過 loadServiceMethod() 方法已經創建好 ServiceMethod ,而 ServiceMethod 類中也保存瞭解析後的請求所需的各種相關信息,緊接着創建了 OkHttpCall 對象,這個對象是 Retrofit 中對 OkHttpCall 接口的實現類,當我們調用 異步請求 enqueue 或者 同步請求 execute 時,就是通過 OkHttpCall 去調用 OkHttp 的請求,所以我們說 Retrofit 和其他 OkHttpUtils 的開源項目一樣,只是對 OkHttp 的一種封裝,底層的網絡請求還是通過 OkHttp 發起的。由於篇幅過長, OkHttpCall 源碼相對簡單,我們這裏就不展開對它的源碼解析了,感興趣的道友可自行查看。

回到主題,我們接着看最後一行代碼,調用了 serviceMethod.callAdapter.adapt(okHttpCall); ,這裏就是爲什麼我們通過添加 RxJavaCallAdapterFactory 之後,我們可以在請求結果返回 RxJava 的對象了。

到這裏我們還存在一個疑問,RxJavaCallAdapterFactory 的工作時機我們看到了,那 GsonConverterFactory 呢?
這就要說回 OkhttpCall 了,假設我們執行的是 異步請求 enqueue ,在其內部最終會調用 OkHttpcallenqueue 方法:

call.enqueue(new okhttp3.Callback() {
    public void onResponse(okhttp3.Call call, Response rawResponse) throws IOException {
        retrofit2.Response response;
        try {
            response = OkHttpCall.this.parseResponse(rawResponse);
        } catch (Throwable var5) {
            this.callFailure(var5);
            return;
        }

        this.callSuccess(response);
    }

	//省略其他代碼...
});

在結果的回調中,調用了自己的 parseResponse 方法:

retrofit2.Response<T> parseResponse(Response rawResponse) throws IOException {
    ResponseBody rawBody = rawResponse.body();
    rawResponse = rawResponse.newBuilder().body(new OkHttpCall.NoContentResponseBody(rawBody.contentType(), rawBody.contentLength())).build();
    int code = rawResponse.code();
    if (code >= 200 && code < 300) {
        if (code != 204 && code != 205) {
            OkHttpCall.ExceptionCatchingRequestBody catchingBody = new OkHttpCall.ExceptionCatchingRequestBody(rawBody);

            try {
                T body = this.serviceMethod.toResponse(catchingBody);
                return retrofit2.Response.success(body, rawResponse);
            } catch (RuntimeException var9) {
                catchingBody.throwIfCaught();
                throw var9;
            }
        } else {
            rawBody.close();
            return retrofit2.Response.success((Object)null, rawResponse);
        }
    } else {
        retrofit2.Response var5;
        try {
            ResponseBody bufferedBody = Utils.buffer(rawBody);
            var5 = retrofit2.Response.error(bufferedBody, rawResponse);
        } finally {
            rawBody.close();
        }

        return var5;
    }
}

在一個try catch代碼塊中,調用了 this.serviceMethod.toResponse(catchingBody); 這裏就是去找到我們創建 Retrofit 時傳遞進去的 ConverterFactory 把網絡請求返回的數據轉換成我們自己想要的格式。

至此,整個 Retrofit 的源碼就解析完了,在整個過程中我們會發現 Retrofit 將設計模式充分運用在了整個項目中,比如:Builder模式(建造者模式)、適配器模式(Adapter)、工廠模式(Factory)、代理模式(Procy)、裝飾模式(OkHttpCall)等等,這也讓我意識到設計模式能夠給我們的架構帶來質的飛躍,因此還需要多看源碼,才能不斷進步。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章