<源碼系列> Retrofit之二:源碼分析

說明:
本文的源碼分析較為粗淺,和其他源碼“解析”的文章相比并未特別詳細,個人覺得看別人的源碼,將整體的思路和大框架了解了,理解了其思想原理足矣

很重要的一點:
一定要帶著質疑別人所謂的的“解析”去分析,一定要結合源碼有自己的理解,不能完全相信他人的觀點,即便是所謂的“大神”,人,總有犯錯的時候。

如果文中有哪里不對的地方,請多指教。

從調用的流程開始分析

流程

一、Retrofit的初始化

    // 初始化配置Retrofit
    retrofit = new Retrofit.Builder()
            .baseUrl(AppConfig.BASE_URL)
            //可設置自定義的client
            .client(getOkHttpClient())
            //可設置自定義的執行類CallAdapterFactory,可多個
            .addCallAdapterFactory(new CustCallAdapterFactory())
            //可設置自定義的解析類ConverterFactory,可多個
            .addConverterFactory(new CustConvertFactory())
            .build();

   private OkHttpClient getOkHttpClient() {
      if (okHttpClient == null) {
         okHttpClient = new OkHttpClient().newBuilder()
               .connectTimeout(15 * 1000, TimeUnit.MILLISECONDS)
               .readTimeout(15 * 1000, TimeUnit.MILLISECONDS)
               .retryOnConnectionFailure(true)
               .addInterceptor(xxxInterceptor)
               .build();
      }
      return okHttpClient;
   }
  • 這里對Retrofit進行初始化,是通過構造者模式進行構建相關的參數數據
  • addConverterFactory:為對象的序列化和反序列化添加轉換器工廠
  • addCallAdapterFactory:添加調用適配器工廠以支持除Call之外的服務方法返回類型

1、baseUrl:

  • baseUrl的方法有多個重載,最終通過構造HttpUrl傳入,一直傳遞給okhttp3.Request

    public Builder baseUrl(HttpUrl baseUrl)  //最終的調用
    public Builder baseUrl(String baseUrl)
    
    Request build() {
      HttpUrl url;
      HttpUrl.Builder urlBuilder = this.urlBuilder;
      if (urlBuilder != null) {
        url = urlBuilder.build();
      } else {
        // No query parameters triggered builder creation, just combine the relative URL and base URL.
        url = baseUrl.resolve(relativeUrl);
        if (url == null) {
          throw new IllegalArgumentException(
              "Malformed URL. Base: " + baseUrl + ", Relative: " + relativeUrl);
        }
      }
      //省略好多...
      return requestBuilder
          .url(url)
          .method(method, body)
          .build();
    }
    
  • 如何動態切換Retrofit的BaseURL?
    Retrofit中接收的baseUrl(String),最終是作為HttpUrl(okhttp3包里的)的類型參數傳入的,在HttpUrl中有個host的字段,可以通過反射進行修改

    public Builder baseUrl(String baseUrl) {
        checkNotNull(baseUrl, "baseUrl == null");
        HttpUrl httpUrl = HttpUrl.parse(baseUrl);
        if (httpUrl == null) {
          throw new IllegalArgumentException("Illegal URL: " + baseUrl);
        }
        return baseUrl(httpUrl);
      }
    
    public Builder baseUrl(HttpUrl baseUrl) {
        checkNotNull(baseUrl, "baseUrl == null");
        List<String> pathSegments = baseUrl.pathSegments();
        if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
          throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
        }
        this.baseUrl = baseUrl;
        return this;
      }
    

二、Retrofit的請求和響應

    // ① 獲取接口實現
    GitHubService service = retrofit.create(GitHubService.class);
    
    // ② 調用(下面同步或異步請求選其一)
    Call<List<Repo>> repos = service.listRepos("octocat");
    
    // ③ 同步請求
    Response<ResponseBody> res = repos.execute();
    
    // ③ 異步請求
    repos.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
            try {
                System.out.println(response.body().string());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {
            t.printStackTrace();
        }
    });

    // 或者通過使用Observable,需要在初始化時進行配置:
    // addCallAdapterFactory(RxJava3CallAdapterFactory.createSynchronous())
   Observable<UserBean> call = mRetrofit.create(GitHubService.class).getUserData();
   call.subsribe(Xxx)

1、接口的實現類:

Retrofit 的示例來看,只需要一個接口及接口方法即可,調用者無需實現接口的具體實現類,就可以調用其內的方法,接口的實現類是從哪來的呢?
通過Retrofit#create 方法獲得了接口的實現類,其內部是通過 動態代理(模式) Proxy.newProxyInstance 來實現的:

  public <T> T create(final Class<T> 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 {
                // If the method is a method from Object then defer to normal invocation.
                if (method.getDeclaringClass() == Object.class) {
                  return method.invoke(this, args);
                }
                args = args != null ? args : emptyArgs;
                return platform.isDefaultMethod(method)  //對default方法進行判斷
                    ? platform.invokeDefaultMethod(method, service, proxy, args)
                    : loadServiceMethod(method).invoke(args);
              }
            });
  }
  • 動態代理Proxy.newProxyInstance 的參數和返回結果:

    • classLoader[參數]:類加載器會產生新類(實現的是傳入的接口)
    • class[][參數]:新類實現的接口,傳進來(放到數組中)
    • invocationHandler 接口[參數]:執行方法體
    • T[返回值]:一個封裝了請求參數的接口的類,來發起網絡請求
      通過字節碼(或打印其getClass())可得出類似于:class com.sun.proxy.$Proxy0
  • 獲?。▌討B代理)接口的實例對象
    Proxy中,通過newProxyInstance獲取對象時,其中維護了一個代理類的緩存集合proxyClassCache【是WeakCache,其內部通過ConcurrentMap進行實際的緩存】,從此緩存中獲取生成的接口實現類。
    而實現類的創建,是通過其固定的名稱組裝的類名:

     //Proxy中:
     //固定規則
     if (proxyPkg == null) {
        // if no non-public proxy interfaces, use com.sun.proxy package
        proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";  //com.sun.proxy
     }
     String proxyName = proxyPkg + proxyClassNamePrefix + num;
    
     byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
     try {
         //這里返回結果,加載byte[]來獲取對象
         return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
     } catch (ClassFormatError e) {
         throw new IllegalArgumentException(e.toString());
     }
    
  • InvocationHandler#invoke 內部解析:
    1】Retrofit#loadServiceMethod
    在每次調用請求接口方法的時候,每次都會通過調用invocationHandler 中的loadServiceMethod 方法,
    loadServiceMethod方法內部,會解析得到具體的接口方法的實現(如例子中的listRepos),進行調用invoke(args) 執行方法,發起網絡請求。
    2】platform.isDefaultMethod(method)
    invocationHandler 中對default 方法進行了判斷。如果是默認方法則對默認方法進行調用
    default 是Java8的特性,在接口中可定義一些默認的方法:

     interface GithubApi {
          //默認方法
         default void hello() {
           //do something...
         }
     }
    

注:其中loadServiceMethod返回的類型是ServiceMethod ,其具體實現類為HttpServiceMethod。(似乎在之前的版本,2.2.0吧,ServiceMethodfinal 的,沒有實現類。)

2、解析并裝配參數(Okhttp請求)

在發送請求的時候,需要傳入一些參數,對于Retrofit ,其請求的參數是放到注解上的,那它是如何對其進行解析的呢?是通過loadServiceMethod 方法內部對接口方法的注解及參數的解析而得。

  • Retrofit#loadServiceMethod
    loadServiceMethod 方法中,通過傳入的Method類型的參數來封裝成ServiceMethod 對象。
    獲取具體的ServiceMethod對象流程(復用):
    1】先從serviceMethodCache 緩存集合(ConcurrentHashMap)中根據Method取出ServiceMethod對象,有則直接返回
    2】若無,加鎖,通過ServiceMethod#parseAnnotations解析方法的注解信息,創建對應的接口方法的實現;
    并緩存到集合中
  • ConcurrentHashMap是個線程安全的集合(數組 + 鏈表 + 紅黑樹),因為網絡請求都是在線程中去做的
  • ServiceMethod 對象(具體實現為HttpServiceMethod)中封裝了各種解析工廠類等請求和響應的數據類信息(CallAdapterConverter 等等)。
  • ServiceMethod#parseAnnotations
    1】通過RequestFactory獲取接口方法(如listRepos方法)上的注解和參數等信息
    如:baseUrlheaders等等
    2】獲取返回值的泛型參數類型:
    method.getGenericReturnType()
    3】最后返回具體的注解信息結果:
    HttpServiceMethod.parseAnnotations
    通過HttpServiceMethodServiceMethod的實現類)進行 返回值包裝類返回值類的處理。

  • HttpServiceMethod#parseAnnotations
    1】通過HttpServiceMethod#createCallAdapter 得到CallAdapter對象,方法內部根據參數進行匹配而得
    2】通過createResponseConverter得到Converter對象,方法內部根據參數匹配而得
    3】通過CallAdapterConverter等對象構造出HttpServiceMethod具體實現的子類對象并返回
    ① 若不是kotlin協程的方法,則返回CallAdapted對象
    ② 否則返回協程方法相關的對象實例:SuspendForResponseSuspendForBody

  • ServiceMethod#invoke(args)
    執行接口方法的調用,進而發起網絡請求
    invokeServiceMethod的抽象方法,由其直接子類HttpServiceMethod實現

  • HttpServiceMethod.invoke(args)
    1】實現了父類ServiceMethod的方法
    2】創建Call對象
    Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
    3】在實現的invoke方法中,又轉交給HttpServiceMethod.adapt的抽象方法去完成的

  • HttpServiceMethod.adapt
    適配數據類型,并執行網絡請求。
    1】具體如何將Call轉換為指定的返回值包裝類的,最終是交給實現了HttpServiceMethod的子類去完成的。
    2】如在CallAdapted中,其adapt方法是直接交由傳入的CallAdapter的實現類來處理的
    在使用的例子中是RxJava3CallAdapterFactory內的具體實現RxJava3CallAdapter

3、“處理器”工廠:

這里我將類型適配器CallAdapter和解析轉換器Converter稱之為處理器,兩者都用了 抽象工廠模式。
CallAdapter:是對響應的數據進行轉換,由T類型轉換為R類型,這里用了 適配器模式
Converter:是對請求數據和響應結果進行處理的數據類型轉換的處理類。

  • CallAdapter
    1】Retrofit中定義了接口CallAdapter<R, T>,并由具體的子類實現如下方法:
    Type responseType();:返回具體的內部類型,如UserBean
    T adapt(Call<R> call);:用于將retrofit2.Call轉換為 返回值包裝類,如Observable,并執行請求
    2】Retrofit默認只支持retrofit2.Call作為接口方法的 返回值包裝類,
    可通過實現CallAdapter接口進行擴展Call,適配器的添加是通過在初始化Retrofit時配置的:
    Retrofit.Builder.addCallAdapterFactory方法,可加多個適配器來轉換 返回值包裝類,
    如可設置RxJava2CallAdapterFactory的Adapter
    3】而真正使用的只有一個,在Retrofit#nextCallAdapter中處理了相關邏輯
    根據類型和注解的相關信息,循環List<CallAdapter.Factory> 進行匹配,
    根據returnTypeType)、annotationsAnnotation[])等信息,匹配到了適配器(即返回結果不為null),則終止循環,直接返回這個factory。
    public interface CallAdapter<R, T> {
    
      Type responseType();
    
      T adapt(Call<R> call);
    }
    
  • Converter
    1】Retrofit 中定義了接口Converter<F, T>,并由具體的子類實現convert方法:
    T convert(F value):即從FT(數據Bean)之間的轉換操作。(多是處理POST請求)
    F 一般是RequestBody(請求體)或是ResponseBody (響應體),而T 是通過convert 方法返回的轉換類型(如UserBean)。
    2】Retrofit提供了addConverterFactory方法,來配置添加轉換實體的工廠對象,可加多個
    ① 在請求的時候,通過Retrofit#requestBodyConverter 方法(在構建RequestFactory中調用的)獲取指定的 請求 數據解析器。
    ② 通過Retrofit#responseBodyConverter 方法(在HttpServiceMethod#createResponseConverter中獲得的)獲取指定的 響應 數據解析器。
    3】在HttpServiceMethod#parseAnnotations方法中調用了HttpServiceMethod#createResponseConverter,
    并在其內部獲取到匹配的轉換器,實際調用了Retrofit#responseBodyConverter方法
    4】在獲取響應結果的轉換器Retrofit#responseBodyConverter(實際調用了nextResponseBodyConverter方法)中,會循環遍歷converterFactories轉換器集合(addConverterFactory方法添加)
    直到根據typeType)、annotationsAnnotation[])等信息,匹配到轉換器(即返回結果不為null),則終止循環,直接返回這個factory。
    public interface Converter<F, T> {
      T convert(F value) throws IOException;
    }
    

4、請求的發起OkHttpCall

Retrofit 中真正發起網絡請求的,最終還是通過的OkHttp,所以要創建OkHttpCall

  • OkHttpCall對象的創建:
    HttpServiceMethod.invoke(args)的方法中,創建了OkHttpCall對象
    并將其實例傳給了抽象方法adapt,是由子類決定如何通過OkHttp的實例發起網絡請求

     @Override
     final @Nullable ReturnT invoke(Object[] args) {
       Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
       return adapt(call, args);
     }
    
  • OkHttpCall#getRawCall -> createRawCall
    在方法中,創建了真正的請求調用對象,并傳入了請求的相關信息
    返回的Call實例,是OkHttp中的請求對象,所以具體的請求還是由OkHttp完成的。

      private okhttp3.Call createRawCall() throws IOException {
        okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
        if (call == null) {
          throw new NullPointerException("Call.Factory returned null.");
        }
        return call;
      }
    
  • OkHttpCall#enqueue / execute方法:
    1】OkHttpCall類中是具體發起請求的地方,即重寫的enqueue / execute方法
    2】在接口(GitHubService)中定義的方法(listRepos)的返回類型Call<ResponseBody> ,這里的Call 實際上就是OkHttpCall類型
    3】當調用call.enqueue(Callback)時,enqueue就是OkHttpCall中的具體實現,而Callback(retrofit2.Callback)會在okhttp3.Callback回調時進行轉換。
    4】HttpServiceMethod的子類實現的adapt方法中,根據傳入的Call,又調用了其enqueue來執行請求的異步任務。

5、結果返回類型

例子中的Call<List<Repo>> 是如何返回的呢?

  • 類型說明
    首先先明確一下,Retrofit中如何對這個返回類型中的各個部分定義的:

    • returnType:接口方法中的 返回值類型,如Call<List<Repo>>
    • rawType:接口方法中的 返回值包裝類,如Call
    • responseType:接口方法中返回的數據Bean類型,如List<Repo>
  • 創建對象后的類型返回
    1】ReturnT invoke
    上面1中說到,請求的方法通過invoke方法來調用的,那返回類型就從這里入手,
    invoke 是在ServiceMethod 中定義的,具體實現在HttpServiceMethodinvoke中(之前ServiceMethod無實現類的時候,代碼直接寫在Retrofit 中來實現的),
    HttpServiceMethod#invoke 中又包了一層了adapt 抽象方法,adapt定義的返回值的類型ReturnT 就是接口方法的返回類型(如Call<List<Repo>>

    @Override
    final @Nullable ReturnT invoke(Object[] args) {
      Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
      return adapt(call, args);
    }
    
    protected abstract @Nullable ReturnT adapt(Call<ResponseT> call, Object[] args);
    

    2】ReturnT adapt(Call<ResponseT> call, Object[] args)
    adapt 方法的具體實現類是CallAdapted<ResponseT, ReturnT>(非kotlin協程),就在HttpServiceMethod 中,是個內部類。此方法是通過CallAdapter 來實現的,它其中的adapt 方法就是一個類型轉換器,具體后面介紹。

     @Override
     protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
       return callAdapter.adapt(call);
     }
    

    3】CallAdapter 的具體實現有很多,其中有個最原始的支持:DefaultCallAdapterFactory ,
    此類中直接創建的匿名內部類CallAdapter的實現沒有具體轉換,只轉成了最基本的Call 類型,是因為要將線程中的請求結果,發送到主線程中,這其中就將Handler的消息發送包裝了起來:

      return new CallAdapter<Object, Call<?>>() {
        @Override
        public Type responseType() {
          return responseType;
        }
    
        @Override
        public Call<Object> adapt(Call<Object> call) {
          return executor == null ? call : new ExecutorCallbackCall<>(executor, call);
        }
      };
    
  • 泛型類型的處理
    DefaultCallAdapterFactory 中,對相關的泛型參數類型進行了處理:

    @Override
    public @Nullable CallAdapter<?, ?> get(
        Type returnType, Annotation[] annotations, Retrofit retrofit) {
      if (getRawType(returnType) != Call.class) {
        return null;
      }
      if (!(returnType instanceof ParameterizedType)) {
        throw new IllegalArgumentException(
            "Call return type must be parameterized as Call<Foo> or Call<? extends Foo>");
      }
      final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType);
    

    1】先判斷rawTypeCall,在DefaultCallAdapterFactory 的只對Call 類型處理
    2】然后判斷其中是否有泛型參數(即returnType[如Call<List<Repo>>] 是否是參數化類型ParameterizedType
    3】再從參數化類型中取出泛型實參(responseType [如List<Repo>]),這里取的上限(Call<? extends Foo>)的第一個(有可能有多個泛型實參)。

  • 如何處理非原始支持的類型,如kotlin的Deffered
    同樣的,也是需要實現CallAdapter 來具體的進行轉換返回相應的類型。

6、如何獲取返回結果的泛型實參

問題:java泛型有類型擦除,那是如何在運行時獲得XxxBean(如List<Repo>)的呢?

上面說到,在獲取returnType 之后,判斷是否為泛型參數類型(ParameterizedType),
然后再通過CallAdapter中的getParameterUpperBound (取類型的上限)

 //Factory中
 protected static Type getParameterUpperBound(int index, ParameterizedType type) {
   return Utils.getParameterUpperBound(index, type);
 }
 
 //Utils中
 static Type getParameterUpperBound(int index, ParameterizedType type) {
     //types即為<Xxx, Yyy, ...> 中的 Xxx, Yyy, ...這一數組
     //獲取泛型實參,主要在這里:getActualTypeArguments
     Type[] types = type.getActualTypeArguments();
     if (index < 0 || index >= types.length) {
       throw new IllegalArgumentException(
           "Index " + index + " not in range [0," + types.length + ") for " + type);
     }
     Type paramType = types[index];
     //對通配符的判斷
     if (paramType instanceof WildcardType) {
       return ((WildcardType) paramType).getUpperBounds()[0];
     }
     return paramType;
 }

如果需要獲取泛型類型,就要在混淆的時候,保留相關的方法處理,不能進行混淆。
若混淆了,生成的如下面的字節碼上的注釋都會被干掉,因此這些泛型類型都會被去掉,就無法獲取到了。

  • 原始代碼:
     public static Map<List<String>, Set<Map<Number, String>>> test(Map<String, Set<Map<Number, String>>> map) {
         return null;
     }
    
  • 編譯后的字節碼:
    // access flags 0x9
    // signature (Ljava/util/Map<Ljava/lang/String;Ljava/util/Set<Ljava/util/Map<Ljava/lang/Number;Ljava/lang/String;>;>;>;)Ljava/util/Map<Ljava/util/List<Ljava/lang/String;>;Ljava/util/Set<Ljava/util/Map<Ljava/lang/Number;Ljava/lang/String;>;>;>;
    // declaration: java.util.Map<java.util.List<java.lang.String>, java.util.Set<java.util.Map<java.lang.Number, java.lang.String>>> test(java.util.Map<java.lang.String, java.util.Set<java.util.Map<java.lang.Number, java.lang.String>>>)
    public static test(Ljava/util/Map;)Ljava/util/Map;
    

泛型擦除是一個無奈之舉,因為在java1.5出現泛型之前的1.4版本及之前的版本,已經使用的很廣泛了,為了兼容之前的代碼程序。
C#是將泛型作為一個真實的類型存在,沒有泛型擦除的問題。

7、Retrofit中的kotlin協程

  • 使用:可直接返回實體Bean,而不需要任何包裝類
    interface GitHubService {
    
        @GET("getUserData")
        suspend fun getUserData(): UserBean
    
    }

    data class UserBean(val userName: String, val userAge: Long)

    val retrofit = Retrofit.Builder()
        .baseUrl(BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .build()
    val service = retrofit.create(GitHubService::class.java)
    val job: Job = launch {
       try {
           val userBean: UserBean = service.getUserData()
           println("userBean: $userBean")
       } catch (e: Throwable) {
           println("onFailure: $e")
       }
    }

  • 內部解析
    1】Retrofit是以Java語言實現的,而suspend 掛起函數只能用于ktolin,不過都會編譯為JVM語言
    通過將接口ApiService 反編譯得到的Java類,結果如下:

     public interface ApiService {
        @GET("getUserData")
        @Nullable
        Object getUserData1(@NotNull Continuation var1);
     }
    

    2】在RequestFactory中包含一個isKotlinSuspendFunction的成員,用來標記當前解析的Method是否為suspend函數。
    isKotlinSuspendFunction的值,是在RequestFactory#build方法中跟進方法參數的最后一個是否為Continuation.class來判定的。

    3】在處理方法的解析中,HttpServiceMethod#parseAnnotations內,根據isKotlinSuspendFunction來處理kotlin協程的接口方法的解析結果
    如果是,則返回SuspendForResponse(得到Response對象)或SuspendForBody(得到數據Bean對象,如UserBean)對象,皆為HttpServiceMethod 的子類

    4】SuspendForBody實現的adapt方法中,
    將接口方法(getUserData)最后一個參數強轉為Continuation<ResponseT>
    最終會調用KotlinExtensions.await這個kotlin擴展方法

    5】KotlinExtensions.await方法中,
    suspendCancellableCoroutine 支持cancel的CoroutineScope 為作用域,依舊以Call.enqueue發起OkHttp 請求,得到responseBody 后將其回調出去。

8、Retrofit在Android中的支持

支持主要在兩方面:
① 是否支持 Java8,根據Android上是否啟用 Java 8來判斷,(Gradle中配置)
② 實現UI線程回調的Executor,將回調結果切到UI線程

  • 是否為Android平臺的判斷:
    根據虛擬機的名稱來判斷,其中定義的Android 類,是Platform的唯一子類
  private static Platform findPlatform() {
    //根據 JVM 名字來判斷使用方是否是 Android 平臺
    return "Dalvik".equals(System.getProperty("java.vm.name"))
        ? new Android()
        : new Platform(true);
  }
  • 是否支持Java 8
    通過Platform中的hasJava8Types成員來判斷,在Platform#Android類中,是通過構造方法傳入的條件判斷的:
    Android() {
      super(Build.VERSION.SDK_INT >= 24);
    }
  • UI線程的回調實現:
    1】在Platform#Android中,重寫了defaultCallbackExecutor方法,
    返回了創建的MainThreadExecutor對象,其中通過Handler實現了將執行任務Runnable轉發到UI線程的邏輯

     static final class MainThreadExecutor implements Executor {
       private final Handler handler = new Handler(Looper.getMainLooper());
    
       @Override
       public void execute(Runnable r) {
         handler.post(r);
       }
     }
    

    2】上面說到的CallAdapter.Factory ,是用于處理接口返回值包裝類(如Observable)的適配器,在Retrofit#build的時候,會將DefaultCallAdapterFactory添加進去。而DefaultCallAdapterFactory 中就是處理對應的線程切換的操作。

    3】DefaultCallAdapterFactory#get方法返回的CallAdapter對象,就是包裝了ExecutorCallbackCall(實現了Call接口)對象,即具體實現了執行請求的ExecutorCallbackCall#enqueue方法,在此方法中,進行了任務的執行:

     @Override
     public void enqueue(final Callback<T> callback) {
       Objects.requireNonNull(callback, "callback == null");
    
       delegate.enqueue(
           new Callback<T>() {
             @Override
             public void onResponse(Call<T> call, final Response<T> response) {
               callbackExecutor.execute(
                   () -> {
                     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(() -> callback.onFailure(ExecutorCallbackCall.this, t));
             }
           });
     }
    

9、如何支持RxJava

  • 在構建Retrofit時,通過添加CallAdapterFactory

      retrofit = new Retrofit.Builder()
                 .client(OKHttpFactory.INSTANCE.getOkHttpClient())
                 .baseUrl(AppConfig.BASE_URL)
                 .addConverterFactory(GsonConverterFactory.create())
                 //看這里
                 .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                 .build();
    
  • RxJava2CallAdapterFactory內部實現:
    RxJava2CallAdapterFactory中對返回值類型進行一系列操作,處理泛型參數等,并處理返回
    其中的returnType 就是Observable<XxxBean>XxxBean 就是自己定義的需要解析的類型),拿到returnType 之后,獲取rawType 并進行一系列判斷,判斷各種RxJava 中定義的類型,最終將Obervable的這種rawType 類型進行處理,然后在對泛型參數類型(即Observable 尖括號中的泛型類型)進行獲取并處理,最終進行相關賦值,并返回CallAdapter<?>

  • 還支持Observable<Response<XxxBean>>
    請求的時候,具體實現為BodyObservable 的類型返回,真正實現adapt 的相關工作

     Type observableType = getParameterUpperBound(0, (ParameterizedType) returnType);
     Class<?> rawObservableType = getRawType(observableType);
     if (rawObservableType == Response.class) {
       if (!(observableType instanceof ParameterizedType)) {
         throw new IllegalStateException(
             "Response must be parameterized" + " as Response<Foo> or Response<? extends Foo>");
       }
       responseType = getParameterUpperBound(0, (ParameterizedType) observableType);
     } else if (rawObservableType == Result.class) {
       if (!(observableType instanceof ParameterizedType)) {
         throw new IllegalStateException(
             "Result must be parameterized" + " as Result<Foo> or Result<? extends Foo>");
       }
       responseType = getParameterUpperBound(0, (ParameterizedType) observableType);
       isResult = true;
     } else {
       responseType = observableType;
       isBody = true;
     }
    
     return new RxJava2CallAdapter(
         responseType, scheduler, isAsync, isResult, isBody, isFlowable, isSingle, isMaybe, false);
    }
    

10、如何在反序列化時實例化對象

具體處理類是GsonConverterFactory
其中在處理request請求和response響應時,通過 gson.getAdapter 來返回TypeAdapter ,并傳入 GsonRequestBodyConverterGsonResponseBodyConverter 構造來處理解析的數據。在 TypeAdapterFactory 獲取的TypeAdapter 實現的,一般是ReflectiveTypeAdapterFactory ,而ObjectTypeAdapter 中的read 方法是對基本數據類型進行處理解析。
具體的實例化是,在 Gson 構造函數中,創建了一個ConstructorConstructor 構造器的構造器,接收一個instanceCreators(調用者傳進來的):

Gson(final Excluder excluder, final FieldNamingStrategy fieldNamingStrategy, final Map<Type,
      InstanceCreator<?>> instanceCreators,
      ..., ...//省略好多入參....
    ) {
    //省略好多....
    this.constructorConstructor = new ConstructorConstructor(instanceCreators);
    //省略好多....
}

ConstructorConstructor 中對對象的構造做了很多流程判斷(如圖),判斷了是否有無參構造(通過newDefaultConstructor)
終極方案就是使用newUnsafeAllocator 中的native 方法來開辟對象內存空間:

public native Object allocateInstance(Class<?> var1) throws InstantiationException;

在終極方案中,進行實例化對象:

private <T> ObjectConstructor<T> newUnsafeAllocator(
      final Type type, final Class<? super T> rawType) {
    return new ObjectConstructor<T>() {
      private final UnsafeAllocator unsafeAllocator = UnsafeAllocator.create();
      @SuppressWarnings("unchecked")
      @Override public T construct() {
        try {
           //看這里
          Object newInstance = unsafeAllocator.newInstance(rawType);
          return (T) newInstance;
        } catch (Exception e) {
          throw new RuntimeException(("Unable to invoke no-args constructor for " + type + ". "
              + "Registering an InstanceCreator with Gson for this type may fix this problem."), e);
        }
      }
    };
  }

最終實例化了對象,實例化的時機,是在read的時候:

//ReflectiveTypeAdapterFactory <-- 大多數都是這個factory
public static final class Adapter<T> extends TypeAdapter<T> {
    private final ObjectConstructor<T> constructor;
    private final Map<String, BoundField> boundFields;

    Adapter(ObjectConstructor<T> constructor, Map<String, BoundField> boundFields) {
      this.constructor = constructor;
      this.boundFields = boundFields;
    }

    @Override
    public T read(JsonReader in) throws IOException {
      if (in.peek() == JsonToken.NULL) {
        in.nextNull();
        return null;
      }
      //看這里啊,這里啥都木有,就是實例化一個對象,字段全是默認值
      T instance = constructor.construct();
      //省略好多..... 
      return instance;
    }

    @Override
    public void write(JsonWriter out, T value) throws IOException {
      //省略好多.....
    }
  }
Gson對象實例化流程

設計模式舉例:

1、Builder模式

image.png

2、工廠模式

  • 抽象工廠模式:生產一系列的產品,關注的是一個大的集合
  • 工廠方法模式:創建一類對象,一種產品
  • 二者關聯:抽象工廠通常是工廠方法來實現的
抽象工廠模式

Retrofit中的抽象工廠模式

工廠方法模式
  • 簡單工廠:根據傳遞的參數(String、class等等)來創建對應的類對象(產品)
    特點:接口較少,簡單方便
    缺點:增加產品的時候,需要增加/修改simpleFactory的代碼


    簡單工廠

3、適配器模式

適配器模式
  • adapt 接收一種類型(接口),讓實現者去轉換為具體的結果,接收的是R 類型,返回的是T 類型。
    除了Retrofit的CallAdapter,還有Gson中的TypeAdapter同樣也是適配器模式
Retrofit中的適配器模式

4、代理模式

代理模式

訪問子類(真正實現類)不可直接訪問,要通過Proxy 的訪問策略,來對內部私有的子類成員來進行控制訪問。都實現了同樣的接口(父類Subject)

需要注意的是,在Retrofit中創建實例的時候,雖然用了java中的動態代理,但并不是嚴格意義上的代理?難道不是代理了傳入的接口service 么?

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() {
          //省略好多...
}
  • 代理模式 vs 裝飾模式
    • 代理模式:控制訪問
    • 裝飾模式:增強功能

如何分析源碼總結:

  • 帶著問題入手
  • 從最熟悉的部分切入
  • 看看有哪些可擴展性設計
  • 分析下運用了哪些設計模式
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,797評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,179評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,628評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,642評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,444評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,948評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,040評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,185評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,717評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,794評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,418評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,414評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,750評論 2 370