那些年我們一起追過的框架之Retrofit源碼解析

目錄

  1. 前言
  2. 怎樣用Retrofit請求網絡
  3. Retorfit是如何創建的
  4. 請求接口的實例是如何創建出來的
  5. Call到底是什么

5.1 ServiceMethod的創建
5.2 Call的創建
5.3 總結

  1. Call的enqueue方法到底干了什么
  2. 遺留

1.前言

近些天回想起工作這幾年,感覺一事無成。俗話說,就算是咸魚,也要做一條有理想的咸魚。因此,訂了一個長期的計劃:博客。

為何選擇Retrofit作為人生中的第一篇博客?

  • Retrofit太火了,在最新的2017Android框架中Retrofit排名第一
  • Retrofit是對OkHttp的封裝。作為一個封裝庫,它用了大量的設計模式,有很多值得借鑒的地方
  • 代碼量少,相對簡單

在讀這篇文章前,假設你已經掌握了

  • Retrofit如何使用

分析源碼一般都有一根主線,本文打算從Retrofit最基本的用法開始,一步一步深入了解Retrofit全貌。


2.怎樣用Retrofit請求網絡

一般我們使用Retrofit需要以下五步:

  • 定義請求接口
  • 創建Retrofit實例
  • 獲取請求接口的實例
  • 獲取Call對象
  • 同步或異步請求
//1.定義請求接口
public interface GitHubService {
    @GET("users/{user}/repos")
    Call<List<Repo>> listRepos(@Path("user") String user);
}
        //2.創建Retrofit實例
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://api.github.com/")
                .addConverterFactory(GsonConverterFactory.create(gson))
                .build();
        //3.通過Retrofit實例獲取請求接口實例(動態代理)
        GitHubService service = retrofit.create(GitHubService.class);
        //4.獲取Call對象
        Call<List<Repo>> call = service.listRepos("octocat");
        //5.異步請求
        call.enqueue(new Callback<List<Repo>>() {
            @Override
            public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
                //doSometionSuccess
                ...
            }

            @Override
            public void onFailure(Call<List<Repo>> call, Throwable t) {
                //doSometionError
                ...
            }
        });

我們將會以上述代碼為導火索來分析源碼。針對上述代碼我們先提出以下幾個問題,分別對應代碼注釋中的2 3 4 5對應的代碼:

  • Retrofit是如何創建的
  • 請求接口的實例是如何創建出來的
  • Call到底是什么
  • Call的enqueue方法到底干了什么

我會在每節的開始處附上每個問題對應的代碼


3.Retorfit是如何創建的

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://api.github.com/")
                .addConverterFactory(GsonConverterFactory.create(gson))
                .build();

只要稍微了解Build模式的童鞋都能一眼看出來,Retrofit是通過Build模式構建的。關于Retrofit中的那些設計模式,我打算另起篇幅介紹,這里就不敘述了。


    private okhttp3.Call.Factory callFactory;
    private HttpUrl baseUrl;
    private final List<Converter.Factory> converterFactories = new ArrayList<>();
    private final List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
    private Executor callbackExecutor;
    private boolean validateEagerly;
    private final Platform platform;
    
    public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }

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

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

      // Make a defensive copy of the adapters and add the default Call adapter.
      List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

      // Make a defensive copy of the converters.
      List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);

      return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
          callbackExecutor, validateEagerly);
    }

這里我先簡單介紹一下各個參數的作用:

  • callFactory,前面介紹了Retrofit是對OkHttp的封裝,callFactory是Okhttp3提供的類,用來自定義OkHttpClient()。
  • callbackExecutor,請求完成后,用來對回調過程進行線程控制。默認是在主線程中回調。
  • adapterFactories,它是CallAdapter.Factory的集合。CallAdapter.Factory是一個工廠,用來生產callAdapter,callAdapter是對OkHttpCall的適配。至于OkHttpCall是什么,我們會在下面的篇幅中會介紹。隨便提一句,Retrofit集成RxJava就是通過CallAdapter.Factory實現的。
  • converterFactories,它與adapterFactories類似。不同的是,它生產的Converter可以控制請求結果的解析。例如,進行JSON還是XML解析。
  • validateEagerly默認為false,validateEagerly參數如果我們在配置的時候設置為true的時候,程序是在做一個預處理的操作,會提前解析請求接口中定義的方法,生產MethodHandler并緩存。
  • Platform,Retrofit希望是一個跨平臺的網絡請求庫,因此它定義了Platform接口。每個平臺都提供了默認的callbackExecutor與CallAdapter.Factory實現。

platform源碼

static class Android extends Platform {
    @Override public Executor defaultCallbackExecutor() {
      return new MainThreadExecutor();
    }

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

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

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

4.請求接口的實例是如何創建出來的

    GitHubService service = retrofit.create(GitHubService.class);

我們看一下Retrofit的create方法。

    public <T> T create(final Class<T> service) {

        Utils.validateServiceInterface(service);

        if (validateEagerly) {
            eagerlyValidateMethods(service);
        }
        //動態代理,返回的是T類型的對象。
        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, 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);
                        }
                        if (platform.isDefaultMethod(method)) {
                            return platform.invokeDefaultMethod(method, service, proxy, args);
                        }
                        ServiceMethod serviceMethod = loadServiceMethod(method);
                        OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
                        return serviceMethod.callAdapter.adapt(okHttpCall);
                    }
                });
    }


我們先暫時不去分析eagerlyValidateMethods(service)方法,因為它看起來即使validateEagerly為false,我們的程序仍能正常運行。
Proxy.newProxyInstance()生成了一個動態代理對象,而InvocationHandler可以攔截這個對象的所有方法。這么說可能有些抽象,你可以簡單的認為以下這兩段代碼等價。

    GitHubService service = retrofit.create(GitHubService.class)

    GitHubService service = new GitHubService() {
    //創建InvocationHandler對象
        InvocationHandler invocationHandler = new InvocationHandler() {
            private final Platform platform = Platform.get();

            @Override
            public Object invoke(Object proxy, Method method, 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);
                }
                if (platform.isDefaultMethod(method)) {
                    return platform.invokeDefaultMethod(method, service, proxy, args);
                }
                ServiceMethod serviceMethod = loadServiceMethod(method);
                OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
                return serviceMethod.callAdapter.adapt(okHttpCall);
            }
        };
        
    //調用listRepos方法其實是調用InvocationHandler的invoke方法,參數分別為:當前GitHubService對象,listRepos()方法對象,listRepos()傳入的參數
        @Override
        public Call<List<String>> listRepos(@Path("user") String user) {
            return invocationHandler.invoke(this, GitHubService.class.getMethod("listRepos"), user);
        }
    }

5.Call到底是什么

    Call<List<Repo>> call = service.listRepos("octocat");

service.listRepos()方法其實調用了invocationHandler的invoke()方法。其核心代碼僅有三行。

  //ServiceMethod的創建
  ServiceMethod serviceMethod = loadServiceMethod(method);
  //Call的創建 
  OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
  return serviceMethod.callAdapter.adapt(okHttpCall);

這部分將分為兩部分介紹,ServiceMethod的創建和Call的創建。

5.1 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;
  }

loadServiceMethod是為了創建ServiceMethod,然后將其放入的緩存中,緩存的Key為Method對象。ServiceMethod同樣是通過Builder模式創建的。上述方法中的this指的是當前Retrofit對象。因此可以推測ServiceMethod.Builder持有了Retrofit對象和Method對象的引用。事實也的確如此,如下:

    Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit;
      this.method = method;
      this.methodAnnotations = method.getAnnotations();
      this.parameterTypes = method.getGenericParameterTypes();
      this.parameterAnnotationsArray = method.getParameterAnnotations();
    }

這段代碼主要是保存了Retrofit的實例對象,method對象,method上的注解,method參數注解,method參數類型。

    public ServiceMethod build () {
       
        callAdapter = createCallAdapter();
        responseType = callAdapter.responseType();
        responseConverter = createResponseConverter();

        for (Annotation annotation : methodAnnotations) {
            parseMethodAnnotation(annotation);
        }

        // ......

        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);
        }
        //...

        return new ServiceMethod<>(this);
    }

createCallAdapter主要是通過Builder中保存的retrofit對象,找到CallAdapterFactory集合,遍歷集合,根據Method的返回值類型和Method上的注解,找到合適的callAdapter。callAdapter是一個接口,定義了兩個方法:Type responseType(),返回值類型;<R> T adapt(Call<R> call),用來返回代理對象。我們看一下Platform中默認提供的CallAdapterFactory。

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);
              }
    };
  }
}

這樣ServiceMethod內部就創建了一個匿名的CallAdapter對象。

createResponseConverter與createCallAdapter過程類似,不再贅述。build()方法中其余的代碼主要是通過解析注解和拼接參數來生成Http請求數據,如:get、post、delete等方法、請求Url、參數。至此,ServiceMethod中擁有了callAdapter、responseConverter以及和網絡請求有關的url、parameter、method等。loadServiceMethod介紹完畢。

5.2 Call的創建

我們看一下這兩段代碼的意思

    OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
    return serviceMethod.callAdapter.adapt(okHttpCall);

OkHttpCall是Call的實現類,它有一個ServiceMethod對象和一個請求參數的數組。如下:

  final class OkHttpCall<T> implements Call<T> {
      //構造方法
      OkHttpCall(ServiceMethod<T, ?> serviceMethod, @Nullable Object[] args) {
      this.serviceMethod = serviceMethod;
      this.args = args;
    }
  }

我們在 ServiceMethod的創建 中介紹了serviceMethod.callAdapter是一個匿名類。它的adapt方法返回了一個ExecutorCallbackCall對象。

  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是對OkHttpCall對象的裝飾。它內部的delegate對象就是上面分析的OkHttpCall對象。callbackExecutor是我們在** Retorfit是如何創建的 **中介紹的callbackExecutor對象。Platform提供了默認實現MainThreadExecutor。MainThreadExecutor很簡單,它將接收到的Runnable對象交給主線程的Handler去處理。

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

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

5.3 總結

我們返回的call對象其實是一個ExecutorCallbackCall對象,它實現了Call接口,是對OkHttpCall的裝飾,并且內部持有了callbackExecutor對象。OkHttpCall也實現了Call接口,它有持有了一個ServiceMethod對象和一個請求參數的數組的引用。ServiceMethod封裝了請求的細節,如get、post、delete等方法、請求Url、參數等,同時callAdapter和responseConverter也是在ServiceMethod中創建的。


6.Call的enqueue方法到底干了什么

 call.enqueue(new Callback<List<Repo>>() {
            @Override
            public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
                //doSometionSuccess
                ...
            }

            @Override
            public void onFailure(Call<List<Repo>> call, Throwable t) {
                //doSometionError
                ...
            }
        });

在上節中,我們知道了返回的Call實例是ExecutorCallbackCall類型的對象。那我們看一下ExecutorCallbackCall的enqueue()方法。

   public void enqueue(final Callback<T> callback) {

        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(ExecutorCallAdapterFactory.ExecutorCallbackCall.this, new IOException("Canceled"));
                        } else {
                            callback.onResponse(ExecutorCallAdapterFactory.ExecutorCallbackCall.this, response);
                        }
                    }
                });
            }
            @Override public void onFailure(Call<T> call, final Throwable t) {
                callbackExecutor.execute(new Runnable() {
                    @Override public void run() {
                        callback.onFailure(ExecutorCallAdapterFactory.ExecutorCallbackCall.this, t);
                    }
                });
            }
        });
    }

我們先來介紹一下這段代碼中出現的變量,然后再來分析整段代碼的含義。

  • delegate是上一節介紹的OkHttpCall對象。
  • callbackExecutor是上一節介紹的MainThreadExecutor對象。

這段代碼更能說明ExecutorCallbackCall采用了裝飾模式,它生成了一個新的Callback對象。這個新Callback有兩個作用。一,將回調切換到callbackExecutor中執行。二,增加了取消操作。最終,這個新的Callback對象交給OkHttpCall對象的enqueue方法去處理。

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 {
          call = rawCall = createRawCall();
        } catch (Throwable 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)
          throws IOException {
        Response<T> response;
        try {
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          callFailure(e);
          return;
        }
        callSuccess(response);
      }

      @Override public void onFailure(okhttp3.Call call, IOException e) {
        try {
          callback.onFailure(OkHttpCall.this, e);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }
    });
  }

OkHttpCall是對Okhttp3.Call的包裝,OkHttpCall內部有一個類型為Okhttp3.Call的rawCall對象。rawCall的創建,是通過Retrofit初始化時,傳入的OkHttpClient創建的(若為空,系統提供了默認的)。在enqueue方法中利用rawCall對象發起請求。請求成功后,會通過parseResponse(rawResponse)方法對原始數據進行解析。
而parseResponse()的核心代碼只有一句話。

 T body = serviceMethod.toResponse(catchingBody);

不錯,它調用了serviceMethod的toResponse方法。而serviceMethod的toResponse方法也只有一句話。

  R toResponse(ResponseBody body) throws IOException {
    return responseConverter.convert(body);
  }

還記得我們在 ServiceMethod的創建 提到的,和callAdapter創建過程類似的responseConverter嗎?沒錯,就是它。


7.遺留

我們在** 請求接口的實例是如何創建出來的 ** 這節中遺留下一段代碼

  if (validateEagerly) {
        eagerlyValidateMethods(service);
  }

我們現在回過頭來看一下這段代碼

  private void eagerlyValidateMethods(Class<?> service) {
    Platform platform = Platform.get();
    for (Method method : service.getDeclaredMethods()) {
      if (!platform.isDefaultMethod(method)) {
        loadServiceMethod(method);
      }
    }
  }

loadServiceMethod是不是很熟悉。不要告訴我你已經忘了!!! 好吧,那我來告訴你,ServiceMethod的創建 中我們最先介紹的就是這個方法。eagerlyValidateMethods是一個對service中的方法提前解析,將其緩存的過程。所以我們可以通過設置validateEagerly=true來提高請求效率。


哇,終于完了,寫博客是真的累啊
真的累啊
真累啊
累啊
累?。。?/p>

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

推薦閱讀更多精彩內容