Retrofit 學(xué)習(xí)第三彈—源碼分析篇

Retrofit 是目前作為網(wǎng)絡(luò)請求的主流框架,使用起來很方便,僅需在接口中定義方法踱卵,打上注解槐瑞,而且和 Rxjava 配合起來,可以更好的完成網(wǎng)絡(luò)請求時的線程切換問題崎页。那么這樣一個框架鞠绰,我們有必要對它的源碼分析一下,希望能夠從中吸取到一定的東西飒焦,Retrofit 框架里面用到了很設(shè)多計模式蜈膨,像外觀模式,代理模式牺荠,工廠模式翁巍,適配器模式,裝飾者模式等等休雌,這無疑是學(xué)習(xí)設(shè)計模式很好的一個實例灶壶。

如果對 Retrofit 的使用還不是很熟練的話,可以看看我之前寫的兩篇文章:

Retrofit2.0 學(xué)習(xí)第一彈——準備篇

Retrofit2.0 學(xué)習(xí)第二彈——使用篇

在看源碼之前杈曲,先來看幾個問題驰凛,帶著問題去看源碼,這樣更有目的性鱼蝉,不至于陷于源碼的細節(jié)而不能自拔洒嗤。

(1) 定義網(wǎng)絡(luò)請求是一個接口,里面的方法都是抽象方法魁亦,這些抽象方法是怎么被執(zhí)行的渔隶?

(2) 方法上的注解是如何被解析,轉(zhuǎn)化成網(wǎng)絡(luò)請求的洁奈?

(3) 網(wǎng)絡(luò)請求我們一般采用異步請求间唉,那么請求結(jié)果回來時,是如何進行線程切換的利术,即切換到主線程呈野?

Retrofit 實例構(gòu)建

構(gòu)建 Retrofit 實例是通過 Retrofit.Builder 來構(gòu)建的,采用構(gòu)建者模式印叁,配置參數(shù)更清晰被冒,而且能夠鏈式調(diào)用

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(baseUrl)
                .addConverterFactory(GsonConverterFactory.create(gson))
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();

接下來就來看下 Builder 中有哪些參數(shù)

public static final class Builder {

    // 平臺類型對象:Android
    private final Platform platform;
    
    // 網(wǎng)絡(luò)請求的工廠,默認為 okhttp3 中的 OkHttpClient,OkHttpClient 實現(xiàn)了 okhttp3.Call.Factory
    private @Nullable okhttp3.Call.Factory callFactory;
    
    // 網(wǎng)絡(luò)請求的url地址,如 www.baidu.com
    private HttpUrl baseUrl;
    
    // 數(shù)據(jù)轉(zhuǎn)換器工廠的集合
    private final List<Converter.Factory> converterFactories = new ArrayList<>();
    
    // 網(wǎng)絡(luò)請求適配器工廠的集合
    private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
    
    // 回調(diào)方法執(zhí)行器
    private @Nullable Executor callbackExecutor;
    
    // 是否校驗接口(針對 java8轮蜕,java8 中接口方法有默認實現(xiàn))
    private boolean validateEagerly;

    Builder(Platform platform) {
      this.platform = platform;
    }

    public Builder() {
      this(Platform.get());
    }

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

      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        // 默認是 OkHttpClient
        callFactory = new OkHttpClient();
      }

      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        // 默認是 Android平臺 中的主線程調(diào)度器
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      // 網(wǎng)絡(luò)請求適配器工廠的集合
      List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
      callAdapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

      // 數(shù)據(jù)轉(zhuǎn)換器工廠的集合
      List<Converter.Factory> converterFactories =
          new ArrayList<>(1 + this.converterFactories.size());
      converterFactories.add(new BuiltInConverters());
      converterFactories.addAll(this.converterFactories);
      // 創(chuàng)建 Retrofit 實例
      return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
    }
  }

使用 Builder 構(gòu)建 Retrofit 可以設(shè)置這幾個參數(shù):

參數(shù) 描述 說明
Platform 平臺類型 支持 Android昨悼,IOS,Java8
okhttp3.Call 用來網(wǎng)絡(luò)請求 Call 對象跃洛,默認為 OkHttpClient 最終生成的是 okhttp3 中的 RealCall 對象率触,Retrofit 中的 OkHttpCall 是對 okhttp3.Call 的一個包裝
HttpUrl 網(wǎng)絡(luò)請求的url地址 設(shè)置的基地址,如 www.baidu.com汇竭,接口中注解的是 path
Converter 數(shù)據(jù)轉(zhuǎn)換器 用于轉(zhuǎn)化數(shù)據(jù)葱蝗,將 responseBody 轉(zhuǎn)化為對象穴张,同也可以對請求完成轉(zhuǎn)化
CallAdapter 網(wǎng)絡(luò)請求適配器 可以對 okhttp3.Call 進行包裝適配,如適配使用 Rxjava 請求
Executor 請求結(jié)果調(diào)度執(zhí)行器 對網(wǎng)絡(luò)請求結(jié)果進行請求两曼,默認是 Android 平臺的 MainThreadExecutor皂甘,用來切換到主線程,可進行 UI 更新操作

上面表格中是對各個參數(shù)的一個說明合愈,有些參數(shù)可以不用設(shè)置叮贩,會有默認參數(shù):

(1) HttpUrl 必須指定,這個不用多說

(2) okhttp3.Call.Factory 這個參數(shù)可以不用設(shè)置佛析,默認是 OkHttpClient,OkHttpClient 是 okhttp3 中的彪蓬,它實現(xiàn)了 okhttp3.Call.Factory 這個接口寸莫,當然一般情況下,我們在使用時會自己設(shè)置一個 OkHttpClient档冬,對它設(shè)置一些 log 打印膘茎,證書驗證,添加 header 等

(3) Converter 使用來進行數(shù)據(jù)轉(zhuǎn)化的酷誓,一般使用 GsonConverterFactory披坏,默認是 BuiltInConverters

(4) CallAdapter.Factory 是用來生產(chǎn) CallAdapter 對象的,CallAdapter 是對 okhttp3.Call 進行包裝盐数,包裝后主要用于控制線程的切換操作棒拂,默認由平臺提供 platform.defaultCallAdapterFactory(callbackExecutor)

(5) Executor 是請求結(jié)果調(diào)度執(zhí)行器,用來切換到主線程玫氢,可進行 UI 更新操作帚屉,對于 Android 平臺,默認提供的是 MainThreadExecutor漾峡,默認通過 platform.defaultCallbackExecutor() 獲取

Builder 中完成這些參數(shù)的設(shè)置攻旦,就可以創(chuàng)建 Retrofit 的實例了,對于 Retrofit 的參數(shù)也就是 Builder 中的參數(shù)生逸,這就是構(gòu)建者模式完成的任務(wù)牢屋。有些參數(shù)可能不是很好理解,后面在流程中會慢慢引入槽袄。

構(gòu)建 Call

動態(tài)代理


public interface MyService {

    @GET("v1/user")
    Call<User> getUsers();

假設(shè)有這樣一個 Service 接口烙无,想實現(xiàn)構(gòu)建 Call實例,通過反射能夠做到么掰伸?

(1) 反射是用于獲取已創(chuàng)建實例的方法或者屬性皱炉,并對其進行調(diào)用或者賦值;

(2) 反射能夠加載一個類狮鸭,通過 newInstance 方法創(chuàng)建實例合搅,但是接口不行

基于這兩條多搀,可以得出對于接口,使用反射不能構(gòu)建出 Call 實例。在 java 中還有另外一個工具灾部,動態(tài)代理康铭。這里對動態(tài)代理的機制做一個簡單的介紹。

動態(tài)代理主要通過 Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法創(chuàng)建接口代理實例赌髓,loader 為類加載器从藤,interfaces 是需要生成代理對象的接口數(shù)組,handler 是用于自定義處理邏輯的對象锁蠕。

Class<User> service = MyService.class;
MyInvocationHandler handler = new MyInvocationHandler();
MyService proxy = Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service }, handler);
Call<User> call = proxy.getUsers();
public class MyInvocationHandler implements InvocationHandler {

    public MyInvocationHandler() {
        
    }

    // proxy 是代理實例對象
    // method 這個參數(shù)表示傳入接口中的所有 Method 對象
    // args => 這個參數(shù)對應(yīng)當前 method 方法中的參數(shù)
    @Override
    public void invoke(Object proxy, Method method, Object[] args) {
        ...
    }
}

這么看有些童鞋可能還是不是很理解動態(tài)代理夷野,那么看下面的代理類:

public class MyServiceProxy implements MyService {

    private InvocationHandler handler;

    public MyServiceProxy(InvocationHandler handler) {
        
    }

    @Override
    @GET("v1/user")
    public Call<User> getUsers(){
        try {
            Method method = com.xx.proxy.MyService.class.getMethod("getUsers");
            this.handler.invoke(this, method, null);
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
}

這樣看來,代理類是這樣的一個類荣倾,是不是就能理解了動態(tài)代理的機制了悯搔。動態(tài)代理就是創(chuàng)建這樣一個類,然后通過類加載器加載到內(nèi)存舌仍,然后再通過反射創(chuàng)建出代理實例妒貌,obj 就是 MyServiceProxy 的一個實例。

Class clazz = classLoader.loadClass("com.xx.proxy.MyServiceProxy");
        Constructor constructor = clazz.getConstructor(InvocationHandler.class);
        Object obj = constructor.newInstance(handler);

Retrofit 中的動態(tài)代理

好铸豁,現(xiàn)在回到 Retrofit 中灌曙,看看 Retrofit 是如何使用動態(tài)代理創(chuàng)建 Call 對象的。

在 Retrofit 實例構(gòu)建中节芥,我們能夠拿到 Retrofit 對象 retrofit在刺,通過這個對象調(diào)用 create 方法創(chuàng)建 MyServiceProxy 實例,即 retrofit.create(MyService.class)藏古。

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, @Nullable Object[] args)
              throws Throwable {
            // 方法是 Object 的一個方法增炭,則直接執(zhí)行
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            // 如果是 java8 中接口的默認實現(xiàn),則直接執(zhí)行
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            // 重點是這三行拧晕,也是網(wǎng)絡(luò)請求執(zhí)行的代碼
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.adapt(okHttpCall);
          }
        });
  }

我們先不看 InvocationHandler 中的執(zhí)行邏輯隙姿,很簡單,就是利用 Proxy.newProxyInstance() 方法創(chuàng)建一個 MyServiceProxy 實例厂捞,有了 service 對象输玷,就可以調(diào)用接口中的方法,來得到 Call 實例:

service.getUsers() ---> handler.invoke()

最終 handler.invoke() 返回的結(jié)果就是 Call 實例靡馁。

小結(jié)

(1)到這里欲鹏,已經(jīng)有了 MyServiceProxy 接口實例,就可以調(diào)用接口中的方法臭墨,我們第一個問題就有了答案赔嚎,是如何完成接口中方法調(diào)用的,實際上是通過動態(tài)代理,幫助我們生成一個代理類尤误,代理類實現(xiàn)了我們定義的網(wǎng)絡(luò)請求接口侠畔,動態(tài)代理生成一個實例,通過這個實例來調(diào)用接口中的方法损晤。

(2)Retrofit 的 create 方法這里用到了软棺,代理模式(動態(tài)代理)和外觀模式,外觀模式是給出一個接口尤勋,外界通過這個接口來得到想要的結(jié)果喘落,不需要關(guān)心其內(nèi)部各個子系統(tǒng)實現(xiàn)的細節(jié),當然最冰,外觀模式不是必要的瘦棋。但是有了外觀模式,對于 Retrofit 的使用者使用起來更加方便和簡潔锌奴。

invoke 方法具體邏輯

//(1)創(chuàng)建 ServiceMethod 
ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
//(2)創(chuàng)建 OkHttpCall      
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
//(3)包裝或者適配 okHttpCall
serviceMethod.adapt(okHttpCall);
1 創(chuàng)建 ServiceMethod

對于我們定義的網(wǎng)絡(luò)接口中的每個方法兽狭,都對應(yīng)一個 ServiceMethod 對象,源碼中采用了緩存機制鹿蜀,以 method 為 key,ServiceMethod 對象為 value,緩存起來服球,這樣能夠提高效率茴恰,一個接口第二次調(diào)用時,不必重新創(chuàng)建 ServiceMethod 對象斩熊。

ServiceMethod<?, ?> loadServiceMethod(Method method) {
    ServiceMethod<?, ?> result = serviceMethodCache.get(method);
    if (result != null) return result;

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        result = new ServiceMethod.Builder<>(this, method).build();
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

接著看 ServiceMethod 對象首次通過 Builder 模式創(chuàng)建過程往枣。

Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit;
      this.method = method;
      // 方法注解
      this.methodAnnotations = method.getAnnotations();
      // 方法參數(shù)類型數(shù)組
      this.parameterTypes = method.getGenericParameterTypes();
      // 方法參數(shù)注解數(shù)組(2維數(shù)組)
      this.parameterAnnotationsArray = method.getParameterAnnotations();
    }

    public ServiceMethod build() {
      // (1) 創(chuàng)建 callAdapter
      callAdapter = createCallAdapter();
      responseType = callAdapter.responseType();
      if (responseType == Response.class || responseType == okhttp3.Response.class) {
        throw methodError("'"
            + Utils.getRawType(responseType).getName()
            + "' is not a valid response body type. Did you mean ResponseBody?");
      }
      // (2) 創(chuàng)建 Converter
      responseConverter = createResponseConverter();

      // (3) 解析方法注解
      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }
      // 注意事項一
      if (httpMethod == null) {
        throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
      }

      if (!hasBody) {
        if (isMultipart) {
          throw methodError(
              "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
        }
        if (isFormEncoded) {
          throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
              + "request body (e.g., @POST).");
        }
      }
      // (4) 生成每個參數(shù)注解的解析輔助類 ParameterHandler,一個 ParameterHandler 可能對應(yīng)一個或者多個注解
      int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = new ParameterHandler<?>[parameterCount];
      for (int p = 0; p < parameterCount; p++) {
        Type parameterType = parameterTypes[p];
        if (Utils.hasUnresolvableType(parameterType)) {
          throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",
              parameterType);
        }

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

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

      // 注意事項二
      // 方法注解和參數(shù)注解解析后的錯誤項粉渠,這里便于我們更好的使用 Retrofit
      if (relativeUrl == null && !gotUrl) {
        throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
      }
      if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
        throw methodError("Non-body HTTP method cannot contain @Body.");
      }
      if (isFormEncoded && !gotField) {
        throw methodError("Form-encoded method must contain at least one @Field.");
      }
      if (isMultipart && !gotPart) {
        throw methodError("Multipart method must contain at least one @Part.");
      }

      return new ServiceMethod<>(this);
    }

(1) 創(chuàng)建 callAdapter分冈,實際上是通過 retrofit 實例,最終調(diào)用 nextCallAdapter() 霸株,查找在創(chuàng)建 Retrofit 對象時雕沉,callAdapterFactories 集合中的 CallAdapter,默認是 platform.defaultCallAdapterFactory() 得到的 new ExecutorCallbackCall<>(callbackExecutor, call)去件,負責(zé)將原本 call 回調(diào)轉(zhuǎn)發(fā)至UI線程坡椒。

(2) 創(chuàng)建 Converter,同樣類似尤溜,最終調(diào)用 nextResponseBodyConverter 方法倔叼,查找創(chuàng)建 Retrofit 對象時,converterFactories 集合中的 Converter.Factory宫莱,來拿到responseConverter對象丈攒,尋找的依據(jù)主要看該 converter 能否處理你編寫方法的返回值類型,默認為 BuiltInConverters,僅僅支持返回值的實際類型為 ResponseBody 和 Void巡验,也就說明了默認情況下际插,是不支持 Call < User > 這類類型的,所以一般我們使用 GsonResponseBodyConverter深碱。

(3) 方法注解解析腹鹉,通過 parseMethodAnnotation() 方法來完成,得到網(wǎng)絡(luò)請求的類型以及請求的參數(shù)敷硅,例如 @GET("v1/user")功咒,中相對路徑 v1/user,

private void parseMethodAnnotation(Annotation annotation) {
    if (annotation instanceof DELETE) {
            parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
      } else if (annotation instanceof GET) {
        parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
      } else if (annotation instanceof HEAD) {
        parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
        if (!Void.class.equals(responseType)) {
          throw methodError("HEAD method must use Void as response type.");
        }
      } 
      
      ...
}

在解析方法注解后有一些注意事項绞蹦,這些是我們在使用 Retrofit 時可能出現(xiàn)的一些問題

  // 注意事項一
  // 沒有在方法上注解網(wǎng)絡(luò)請求類型
  if (httpMethod == null) {
    throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
  }
  // 沒有請求體 body,說明不是 POST 請求力奋,那么不應(yīng)該出現(xiàn) @Multipart 注解和 @FormUrlEncoded 表單類型注解,
  // 因為這兩個注解用于 POST 請求
  if (!hasBody) {
    if (isMultipart) {
      throw methodError(
          "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
    }
    if (isFormEncoded) {
      throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
          + "request body (e.g., @POST).");
    }
  }

(4) 生成每個參數(shù)注解的解析輔助類 ParameterHandler幽七,一個 ParameterHandler 可能對應(yīng)一個或者多個注解景殷,在生成 Request 時會使用到。方法參數(shù)注解有多個澡屡,在上一篇文章 Retrofit2.0 學(xué)習(xí)第二彈——使用篇 中有提到猿挚。

Retrofit注解類型.png

同樣,在參數(shù)注解解析后驶鹉,也有注意事項绩蜻,這些注意事項就是使用時的規(guī)則,規(guī)則是按照 http 請求的規(guī)則來的室埋。

      // 注意事項二
      // 方法注解和參數(shù)注解解析后的錯誤項办绝,這里便于我們更好的使用 Retrofit
      
      // relativeUrl 是在 方法注解中獲取 @GET("v1/user"),
      // 如果方法注解中沒有標注姚淆,那么在參數(shù)注解中應(yīng)該打上 @Url 注解孕蝉,指明地址的相對路徑
      if (relativeUrl == null && !gotUrl) {
        throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
      }
      // 不是 POST 請求,去打上了 @Body 注解腌逢,會報錯
      if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
        throw methodError("Non-body HTTP method cannot contain @Body.");
      }
      // 方法注解上有 @FormUrlEncoded 注解降淮,參數(shù)中需要有 @Field 注解或者 @FieldMap 注解
      if (isFormEncoded && !gotField) {
        throw methodError("Form-encoded method must contain at least one @Field.");
      }
      // 方法注解上有 @Multipart 注解,參數(shù)中需要有 @Part 注解或者 @PartMap 注解
      if (isMultipart && !gotPart) {
        throw methodError("Multipart method must contain at least one @Part.");
      }

經(jīng)過一系列的校驗之后上忍,沒有拋出錯誤骤肛,就可以創(chuàng)建一個 ServiceMethod 對象。

2 創(chuàng)建 OkHttpCall
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
  OkHttpCall(ServiceMethod<T, ?> serviceMethod, @Nullable Object[] args) {
    this.serviceMethod = serviceMethod;
    this.args = args;
  }

這一步?jīng)]什么說的窍蓝,就是創(chuàng)建 OkHttpCall 對象而已腋颠。

3 適配 okHttpCall

serviceMethod.adapt(okHttpCall) 調(diào)用 ServiceMethod 中的 adapt() 方法,還記得在創(chuàng)建 ServiceMethod 時獲取的 callAdapter 么吓笙,在平臺類型 Android 中提供的 ExecutorCallAdapterFactory.get() 方法生成的 CallAdapter 對象淑玫,再通過 CallAdapter 對象 adapt() 方法得到 ExecutorCallbackCall 對象,這樣 Call 請求對象的構(gòu)建過程就完成了。


  final class ExecutorCallAdapterFactory extends CallAdapter.Factory {
  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);
    return new CallAdapter<Object, Call<?>>() {
      @Override public Type responseType() {
        return responseType;
      }

      @Override public Call<Object> adapt(Call<Object> call) {
        return new ExecutorCallbackCall<>(callbackExecutor, call);
      }
    };
  }

  static final class ExecutorCallbackCall<T> implements Call<T> {
    final Executor callbackExecutor;
    final Call<T> delegate;

    ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
      this.callbackExecutor = callbackExecutor;
      this.delegate = delegate;
    }
    
    ...
    
  }
}

ExecutorCallbackCall 是對 Executor 的一個裝飾絮蒿,采用的是裝飾者模式尊搬,在請求回調(diào)時通過 MainThreadExecutor 切換到 UI 線程,切換過程中先通過 Call 等待回調(diào)土涝, run 方法執(zhí)行時佛寿,也有判斷。

這個 Call 實際上是步驟 2 中創(chuàng)建的 OkHttpCall 對象但壮,在 ExecutorCallbackCall 中是一個靜態(tài)代理對象冀泻,通過 OkHttpCall 執(zhí)行同步請求方法 execute() 或者 異步方法 enqueue(final Callback< T > callback)。

小結(jié):這一部分主要在拿到 service 代理對象后蜡饵,調(diào)用接口方法獲取 Call 對象弹渔。過程中最重要就是 ServiceMethod,具體的工作都交給它來完成,ServiceMethod 對象進行網(wǎng)絡(luò)請求參數(shù)配置:通過解析網(wǎng)絡(luò)請求接口方法的參數(shù)溯祸、返回值和注解類型肢专,從 Retrofit 中獲取對應(yīng)的網(wǎng)絡(luò)請求的 url 地址、網(wǎng)絡(luò)請求執(zhí)行器焦辅、網(wǎng)絡(luò)請求適配器博杖、數(shù)據(jù)轉(zhuǎn)換器,最后通過調(diào)用 callAdapter.adapt 方法得到一個 Call 對象筷登,適配的目的就是對 Call 請求回調(diào)進行操作欧募,如果是異步請求,將異步線程切換到 UI 線程仆抵。

調(diào)用 Call 執(zhí)行請求

在上一步 Call 對象構(gòu)建過程中,最終得到的是一個 ExecutorCallbackCall 對象种冬,那么就看看 ExecutorCallbackCall 是如何執(zhí)行網(wǎng)絡(luò)請求的镣丑。


  static final class ExecutorCallbackCall<T> implements Call<T> {
    final Executor callbackExecutor;
    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");

      delegate.enqueue(new Callback<T>() {
        @Override public void onResponse(Call<T> call, final Response<T> response) {
          callbackExecutor.execute(new Runnable() {
            @Override public void run() {
              if (delegate.isCanceled()) {
                // Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
                callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
              } else {
                callback.onResponse(ExecutorCallbackCall.this, response);
              }
            }
          });
        }

        @Override public void onFailure(Call<T> call, final Throwable t) {
          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 {
      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();
    }
  }

ExecutorCallbackCall 內(nèi)部有 execute 和 enqueue 方法,即同步和異步執(zhí)行方法娱两,在創(chuàng)建 ExecutorCallbackCall 對象過程中莺匠,我們知道 ExecutorCallbackCall 實際上是通過 OkHttpCall 這個代理來完成請求的,我們看下 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 {
          // 創(chuàng)建 call 對象
          call = rawCall = createRawCall();
        } catch (Throwable t) {
          throwIfFatal(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) {
        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();
        }
      }
    });
  }

首先是構(gòu)建 Call 對象趣竣,這個 Call 不是我們步驟 2 中的 Call 對象,而是 okhttp3.Call 對象旱物,真正完成網(wǎng)絡(luò)請求的對象遥缕,我們構(gòu)建的 Call 是對它的一個外層包裝,okhttp3.Call 的創(chuàng)建需要 一個 Request 參數(shù),這個應(yīng)該有 ServiceMethod 來完成宵呛,因為 ServiceMethod 對象中包含網(wǎng)絡(luò)請求的全部參數(shù)单匣,構(gòu)建最終調(diào)用 ServiceMethod 的 toCall 方法。


okhttp3.Call toCall(@Nullable 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 callFactory.newCall(requestBuilder.build());
  }

ServiceMethod 對象中的參數(shù)就是在上面 retrofit.loadServiceMethod(method) 創(chuàng)建過程中賦值的,包括解析方法注解户秤,方法參數(shù)注解码秉,最終構(gòu)建出 Request 對象,callFactory 實際上是 OkHttpClient鸡号。

到這里就可以回答第二個問題【方法上的注解是如何被解析转砖,轉(zhuǎn)化成網(wǎng)絡(luò)請求的?】鲸伴,就是在 serviceMethod 創(chuàng)建過程中完成的府蔗,解析方法注解,方法參數(shù)注解解析等挑围,然后在 toCall 方法中進行拼接完成礁竞,當然不是簡單地拼接,具體過程這里就不再分析了杉辙。

有了 okhttp3.Call 對象模捂,接著就可以調(diào)用 call.enqueue() 方法執(zhí)行異步請求。請求的具體過程這里不展開蜘矢,具體過程是調(diào)用了 okhttp3.Call 執(zhí)行的狂男,后面會單獨寫一篇 okhttp3 的文章。請求結(jié)果如果順利品腹,如何解析岖食?
通過調(diào)用 OkHttpCall # response = parseResponse(rawResponse),無需看代碼應(yīng)該也能夠腦補出來舞吭, 如果你還記得在構(gòu)建 OkHttpCall 對象時泡垃,將 ServiceMethod 對象作為參數(shù)傳遞進去,所以 parseResponse 最后調(diào)用的是 ServiceMethod 中的 Conver 來進行數(shù)據(jù)解析工作的羡鸥。

最后還剩下一個問題蔑穴,回調(diào)后如何切換線程到 UI 線程?回頭看下 ExecutorCallbackCall 中的 enqueue 方法惧浴,callbackExecutor 實際是 MainThreadExecutor存和,callbackExecutor 的 execute方法最后是通過主線程的 Handler 切換到 UI 線程中

callbackExecutor.execute(new Runnable() {
            @Override public void run() {
              if (delegate.isCanceled()) {
                // Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
                callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
              } else {
                callback.onResponse(ExecutorCallbackCall.this, response);
              }
    }
    
    
  static class MainThreadExecutor implements Executor {
      private final Handler handler = new Handler(Looper.getMainLooper());

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

到這里,Retrofit 的請求過程基本就分析完了衷旅,其請求的主要過程:

(1) 請求過程實際由代理 OkHttpCall 發(fā)起捐腿,需要構(gòu)建 okhttp3.Call 對象

(2) 構(gòu)建 okhttp3.Call 對象需要參數(shù) Request,Request 對象構(gòu)建參數(shù)在 ServiceMethod 對象中在創(chuàng)建時就已經(jīng)準備好柿顶,serviceMethod.toCall() 方法就是利用這些參數(shù)完成 Request 構(gòu)建茄袖,生成 okhttp3.Call 對象。

(3) okhttp3.Call 請求結(jié)果回調(diào)時九串,對數(shù)據(jù)的解析最終還是通過 serviceMethod.toResponse() 來完成解析的绞佩。

(4) 異步請求是在子線程寺鸥,Retrofit 默認是通過 MainThreadExecutor 的來切換到 UI 線程。

總結(jié)

Retrofit 整個源碼分析也就差不多了品山,整體流程基本覆蓋到胆建,那么最后再總結(jié)一下:

1、首先構(gòu)造 Retrofit肘交,主要參數(shù)笆载,baseurl、callFactory(默認okhttpclient)涯呻、converterFactories凉驻、adapterFactories,excallbackExecutor

2、通過 create() 方法拿到接口的實現(xiàn)代理類复罐,這里利用動態(tài)代理來實現(xiàn)

3涝登、在 invoke 方法內(nèi)部,拿到方法注解以及方法參數(shù)效诅,方法參數(shù)注解等胀滚,構(gòu)造 ServiceMethod,ServiceMethod 完成注解解析乱投,構(gòu)建 Request 和 數(shù)據(jù)轉(zhuǎn)換等大量工作咽笼。最后 calladapter 對 Call 進行裝飾返回。

4戚炫、拿到 Call 就可以執(zhí)行 enqueue 或者 execute 方法了剑刑。

以上分析并不是基于 Retrofit 與 Rxjava 結(jié)合的分析,關(guān)于 Rxjava 部分的分析双肤,有興趣的童鞋可以自行分析下施掏,重點也是集中在對 Call 的包裝和線程切換部分。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末茅糜,一起剝皮案震驚了整個濱河市其监,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌限匣,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件毁菱,死亡現(xiàn)場離奇詭異米死,居然都是意外死亡,警方通過查閱死者的電腦和手機贮庞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門峦筒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人窗慎,你說我怎么就攤上這事物喷÷辈模” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵峦失,是天一觀的道長扇丛。 經(jīng)常有香客問我,道長尉辑,這世上最難降的妖魔是什么帆精? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮隧魄,結(jié)果婚禮上卓练,老公的妹妹穿的比我還像新娘。我一直安慰自己购啄,他們只是感情好襟企,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著狮含,像睡著了一般顽悼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上辉川,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天表蝙,我揣著相機與錄音,去河邊找鬼乓旗。 笑死府蛇,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的屿愚。 我是一名探鬼主播汇跨,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼妆距!你這毒婦竟也來了穷遂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤娱据,失蹤者是張志新(化名)和其女友劉穎蚪黑,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體中剩,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡忌穿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了结啼。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片掠剑。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖郊愧,靈堂內(nèi)的尸體忽然破棺而出朴译,到底是詐尸還是另有隱情井佑,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布眠寿,位于F島的核電站躬翁,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏澜公。R本人自食惡果不足惜姆另,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望坟乾。 院中可真熱鬧迹辐,春花似錦、人聲如沸甚侣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽殷费。三九已至印荔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間详羡,已是汗流浹背仍律。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留实柠,地道東北人水泉。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像窒盐,于是被迫代替她去往敵國和親草则。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容