天天看點

【Android】Retrofit源碼學習

【Android】Retrofit源碼學習

使用Retrofit的流程#

通過Builder建立Retrofit對象:

Copy

Retrofit retrofit = new Retrofit.Builder().baseUrl("").addConverterFactory().build();

用Java注解描述API

public interface MyApi {

@GET("/api")
Call<Data> getData();           

}

通過retrofit建立api對象,并建立Call對象

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

Call call = api.getData();

通過Call對象擷取資料,enqueue()方法發送異步請求,同步方式則使用execute()方法

call.enqueue(new Callback() {

@Override
public void onResponse(Response<ZhuanLanAuthor> author) {
    System.out.println("name: " + author.getName());
}
@Override
public void onFailure(Throwable t) {
}           

});

原了解析#

Retrofit所做的事情:将Java接口翻譯成HTTP請求,然後用OkHttp去發送請求。

Retrofit使用動态代理實作了這件事

動态代理#

動态代理可以在不實作相同接口的proxy的情況下,對相關方法進行代理。

Java可以通過Proxy類實作代理模式,而其中的newProxyInstance()方法可以實作動态代理。通過實作InvocationHandler接口來定義代理動作。

Proxy.newProxyInstance(ClassLoader, Class<?>[] interfaces,InvocationHandler)

InvocationHandler的接口定義如下:

public interface InvocationHandler {

public Object invoke(Object proxy, Method method, Object[] args)
    throws Throwable;           

參數含義:

proxy:代理對象

method: 代理方法

args: 方法的參數

實作invoke()方法來進行代理:

public Object invoke(Object proxy, Method method, Object[] args)

throws Throwable {
// do something
method.invoke(target, args);
// do something           

這樣便能夠成功對target方法進行代理,動态代理生成的代理類的名字為包名+$Proxy+id序号

請求流程分析#

回到使用方法,一開始要使用create()生成API的對象

這裡看下create()的源碼:

public T create(final Class service) {

validateServiceInterface(service); // 判斷是否為接口
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 @Nullable Object invoke(Object proxy, Method method,
          @Nullable Object[] args) throws Throwable {
        // 如果是Object的方法則直接調用
        if (method.getDeclaringClass() == Object.class) {
          return method.invoke(this, args);
        }
        // 相容Java8,Android平台不會調用, 确認平台的方法是通過反射機制判斷類的加載資訊
        if (platform.isDefaultMethod(method)) {
          return platform.invokeDefaultMethod(method, service, proxy, args);
        }
        // 主要方法,傳回ServiceMethod對象
        return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
      }
    });           

create使用了動态代理的方法,傳回了Proxy.newProxyInstance()動态代理對象

是以api對象為動态代理對象,不是真正的實作接口産生的對象。當api對象調用getData()方法時會被動态代理攔截,然後調用InvocationHandler對象中的invoke()方法。

然後Retrofit通過反射擷取到getData()方法的注解資訊,配合invoke()的args參數,建立一個ServiceMethod對象

ServiceMethod傳入Retrofit對象和Method對象,調用各個接口和解析器,最終生成一個Request,包含api的域名、path、http請求方法、請求頭、body等等。最後傳回一個Call對象。

ServiceMethod<?> loadServiceMethod(Method method) {

ServiceMethod<?> result = serviceMethodCache.get(method); // 如果有緩存則直接用
if (result != null) return result;

synchronized (serviceMethodCache) { 
    result = serviceMethodCache.get(method); // 線程安全,鎖住後檢視其他線程是否有加載
    if (result == null) {
        result = ServiceMethod.parseAnnotations(this, method); // 解析注解
        serviceMethodCache.put(method, result); // 放入Cache中
    }
}
return result;           

跟進ServiceMethod.parseAnnotation():

static ServiceMethod parseAnnotations(Retrofit retrofit, Method method) {

// 解析注解為HTTP請求的相關資訊
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) { //API接口方法傳回值不能為void
    throw methodError(method, "Service methods cannot return void.");
}

return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);           

這裡建構了一個RequestFactory對象,解析了接口中關于Http協定的相關資訊,具體解析方法就是拿到Method的Annotation後instansof比較确定

RequestFactory(Builder builder) {

method = builder.method;
baseUrl = builder.retrofit.baseUrl;
httpMethod = builder.httpMethod;
relativeUrl = builder.relativeUrl;
headers = builder.headers;
contentType = builder.contentType;
hasBody = builder.hasBody;
isFormEncoded = builder.isFormEncoded;
isMultipart = builder.isMultipart;
parameterHandlers = builder.parameterHandlers;
isKotlinSuspendFunction = builder.isKotlinSuspendFunction;           

解析完後使用HttpServiceMethod.parseAnnotations()最後生成HttpServiceMethod對象

static HttpServiceMethod parseAnnotations(

Retrofit retrofit, Method method, RequestFactory requestFactory) {
boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction; // kotlin支援,先忽略
boolean continuationWantsResponse = false;
boolean continuationBodyNullable = false;

Annotation[] annotations = method.getAnnotations(); // 方法的注解 @GET @POST @DELETE等
Type adapterType;
// ...這裡有一段關于Kotlin支援的代碼,adapterType
adapterType = method.getGenericReturnType(); // 接口方法的傳回類型,一般為Call<T>

CallAdapter<ResponseT, ReturnT> callAdapter =
    createCallAdapter(retrofit, method, adapterType, annotations);
Type responseType = callAdapter.responseType();
if (responseType == okhttp3.Response.class) {
    throw methodError(method, "'"
                      + getRawType(responseType).getName()
                      + "' is not a valid response body type. Did you mean ResponseBody?");
}
if (responseType == Response.class) {
    throw methodError(method, "Response must include generic type (e.g., Response<String>)");
}
// HEAD請求沒有Response Body
if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
    throw methodError(method, "HEAD method must use Void as response type.");
}
// 設定Response的解析,可以是json解析
Converter<ResponseBody, ResponseT> responseConverter =
    createResponseConverter(retrofit, method, responseType);

okhttp3.Call.Factory callFactory = retrofit.callFactory; // 這裡若是沒有自定則預設為OkHttpClient
if (!isKotlinSuspendFunction) {
    return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter); // 不使用Kotlin就關注這裡就行了
}
// ...關于Kotlin的return           

最後傳回了HttpServiceMethod的繼承類CallAdapted,其中存放着RequestFactory、Converter、CallFactory

然後我們傳回來看這段代碼

return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);

這裡調用的invoke方法來為HttpServiceMethod中的invoke方法:

@Override final @Nullable ReturnT invoke(Object[] args) {

Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);           

// CallAdapted中

@Override protected ReturnT adapt(Call call, Object[] args) {

return callAdapter.adapt(call);           

這裡的OKHttpCall為Okhttp3.Call的封裝類,并實作了Call的相關方法enqueue、execute。

這裡最後使用的adapt方法調用了Retrofit對象中的callAdapter.adapt()來對Call對象進行了适配。

若是開始初始化Retrofit對象時沒有設定CallAdapter,則回預設使用Call,api接口定義時方法的傳回類型隻能是Call

是以便能解釋如下代碼:

api對象為一個動态代理對象,當執行getData()時進入動态代理函數,在InvocationHandler的invoke函數最後調用了HttpServiceMethod.invoke(args),傳回了一個Call對象。

響應流程分析#

Retrofit使用中最後調用自定義的API接口方法傳回了Call對象,這個對象實際上是Retrofit自己封裝的OkHttpCall對象,随後我們使用enqueue方法發出異步請求

call.enqueue(new CallBack() {

@Override
public void onResponse(Call<MyData> call, Response<MyData> response) {
    //... on response    
}
@Override
public void onFailure(Call<MyData> call, Throwable t) {
    //... on response    
}           

})

跟進OkHttpCall.enqueue的源碼:

@Override public void enqueue(final Callback callback) {

Objects.requireNonNull(callback, "callback == null"); // callback不能為null

okhttp3.Call call; // okhttp3的Call對象
Throwable failure;

synchronized (this) { // 線程安全
    if (executed) throw new IllegalStateException("Already executed.");
    executed = true;

    call = rawCall; // rawCall為OkHttpCall儲存的Okttp3的Call對象
    failure = creationFailure;
    if (call == null && failure == null) {
        try {
            // createRawCall中使用callFactory.newCall(requestFactory.create(args))
            // 實際上就是OkHttpClient.newCall(OkHttp3.Request)
            // 傳回了OkHttp3.Call對象
            call = rawCall = createRawCall();
        } catch (Throwable t) {
            throwIfFatal(t);
            failure = creationFailure = t;
        }
    }
}

if (failure != null) {
    callback.onFailure(this, failure);
    return;
}

if (canceled) {
    call.cancel();
}
// 使用okhttp3.Call的enqueue方法
call.enqueue(new okhttp3.Callback() {
    @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)           {
        Response<T> response;
        try {
            // 這裡使用了Converter來解析Response
            // 将Okhttp3.Response對象解析成Retrofit封裝的Response對象
            response = parseResponse(rawResponse);
        } catch (Throwable e) {
            throwIfFatal(e);
            callFailure(e);
            return;
        }

        try {
            // 調用傳進來的回調
            callback.onResponse(OkHttpCall.this, response);
        } catch (Throwable t) {
            throwIfFatal(t);
            t.printStackTrace();
        }
    }

    @Override public void onFailure(okhttp3.Call call, IOException e) {
        callFailure(e);
    }
    // 請求失敗則進入callback的OnFailure方法
    private void callFailure(Throwable e) {
        try {
            callback.onFailure(OkHttpCall.this, e);
        } catch (Throwable t) {
            throwIfFatal(t);
            t.printStackTrace();
        }
    }
});           

其中parseResponse()方法:

Response parseResponse(okhttp3.Response rawResponse) throws IOException {

ResponseBody rawBody = rawResponse.body();

// 将Response Body 和 ResponseHeader 分開
// 之後再對Body進行處理
rawResponse = rawResponse.newBuilder()
    .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
    .build();

int code = rawResponse.code(); // HTTP 狀态碼
// 響應不成功
if (code < 200 || code >= 300) {
    try {
        // Buffer the entire body to avoid future I/O.
        ResponseBody bufferedBody = Utils.buffer(rawBody);
        return Response.error(bufferedBody, rawResponse);
    } finally {
        rawBody.close();
    }
}
// 響應無内容,填入null
if (code == 204 || code == 205) {
    rawBody.close();
    return Response.success(null, rawResponse);
}
// 儲存source的Response Body,在解析失敗時可以使用
ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
try {
    // 使用responseConverter來解析Body
    T body = responseConverter.convert(catchingBody);
    // 将解析好的Body裝入Retrofit的Response對象傳回
    return Response.success(body, rawResponse);
} catch (RuntimeException e) {
    catchingBody.throwIfCaught();
    throw e;
}           

主要的parse過程便是,将Okhttp.Response對象的Body和Header拆開,若請求成功且Body有内容則将Body交給responseConverter取解析成響應對象,裝入Retrofit的Response對象中傳回。

總結#

Retrofit的特色:通過使用注解定義API接口的方式聲明API,通過注解的解析,将解析得到的資訊封裝在RequestFactory中,在使用時調用create()方法生成Okhttp的Request對象。

通過動态代理的方式,代理使用者定義的API接口方法,使其生成封裝的OkHttpCall對象

封裝okhttp.Call為OkHttpCall,使其能夠使用CallAdapter(可以使傳回的Call對象适配為其他的對象,如RxJava(沒用過)中的對象)和ResponseConverter(支援Gson等解析)

目前隻讀到這裡,還有一些機制沒讀完

參考文章#

https://www.jianshu.com/p/c1a3a881a144 https://segmentfault.com/a/1190000006767113 https://yq.aliyun.com/articles/658544

作者: y4ngyy

出處:

https://www.cnblogs.com/y4ngyy/p/12530566.html