Retrofit2原始碼解析

ChuckChenLw發表於2016-06-20

  最近專案將網路框架換成Retrofit2.0.2,文中說的Retrofit都是指的Retrofit2這裡要說明一下,畢竟和Retrofit1差別還是蠻大的,結合Okhttp,RxJava還是比較好用的,網上有很多前輩介紹過使用方法,本文是想研究一下Retrofit的原始碼。關於Retrofit的介紹可以查閱Retrofit的官方網站
  直接進入主題:(注本文是結合RxJava介紹的,最好可以瞭解一下RxJava不瞭解也沒有關係,大部分的思想是一樣的)
  Retrofit的基本使用
  Retrofit使用是非常簡單的,在上邊的官網上介紹的也非常詳細。但是為了後邊的分析,還是把使用的程式碼貼在這兒:
  一般在專案中會將需要請求網路方法寫在一個介面中,如下:

 public interface GitHubService {
    @GET("users/{user}/repos")
    Observable<List<Repo>> listRepos(@Path("user") String user);
 }

  如果要使用Retrofit還需要構建Retrofit的物件:

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

  主要就是配置baseUrl,ConverterFactory,CallAdapterFactory。這裡用的是Gson,和RxJava所以相應的就是GsonConverterFactoryRxJavaCallAdaperFactory這個在後邊的原始碼分析中會用到。
  最後直接呼叫就可以了:
  

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

service.listRepos("octocat")
              .observeOn(AndroidSchedulers.mainThread())
              .subscribeOn(Schedulers.io())
              .subscribe(list->{
              if(list!=null){
              //TODO 取得資料後邏輯處理
              }
             });

  可以說,程式碼還是非常簡潔的。用起來也很容易上手。
  Retrofit工作流程
  如上面的使用中我們可以看到:
1.通過Retrofit.Builder().build()構建Retrofit例項。
2.呼叫Retrofit的create()方法將生成介面GitHubService的例項。
3.呼叫GitHubService的listRepos()方法返回Observable<List<Repo>>,這裡的GitHubService例項實際上是個代理物件,這個下文再說。
  下圖是我整理的Retrofit執行時主要節點的時序圖,當然不是所有的過程都反映出來了。
  這裡寫圖片描述
  看不懂沒關係,一步一步來,先看看Retrofit原始碼的構成:
    

Retrofit2原始碼解析
 
  簡單的介紹一下
  1.Call(介面)–向伺服器傳送請求並返回響應的呼叫
  2.CallAdapter(介面)–Call的介面卡,用來包裝轉換Call
  3.CallAdapter.Factory(介面)–CallAdapter的工廠,通過get方法獲取對應的CallAdapter
  4.CallBack(介面)–Call的回撥
  5.Converter(介面)–資料轉換器,將一個物件轉化另外一個物件
  6.Converter.Factory(抽象類) – 資料轉換器Converter的工廠
  responseBodyConverter – 將伺服器返回的資料轉化ResponseBody。
  requestBodyConverter – 將GitHubService.listRepos()中的Body,Part等註解轉換為RequestBody(),以便Okhttp請求的時候使用。
  stringConverter – 將Field,FieldMap 值,Header,Path,Query,和QueryMap值轉化為String,以便Okhttp請求的時候使用。
  7.ServiceMethod 通過解析註解,傳參,會將你的介面方法呼叫轉化為一個 Call 物件。也就說對於每一個介面方法,他都會建立一個與之對應的 ServiceMethod
  另外retrofit還有一個http包,都是用來定義 HTTP 請求的自定義註解。如果對註解不太熟悉可以看看我的ButterKnife原始碼剖析 關於註解的介紹。
  
 
Retrofit2原始碼解析
 

  回到上邊步驟2,呼叫Retrofit的create()方法將生成介面GitHubService的例項:
  

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

  將GithubService.class作為引數,傳入create(),然後又返回一個GithubService例項,看起來是不是很神奇。原始碼如下:
  

 @SuppressWarnings("unchecked") // Single-interface proxy creation guarded by parameter safety.
  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, 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);
          }
        });
  }

  這裡使用了動態代理,返回了一個 Proxy 代理類,呼叫GithubService(在自己的專案中是XXXService介面)介面中的任何方法都會呼叫 proxy 裡的 invoke 方法。這個方法裡邊最重要的就是第23,24,25行程式碼。
  1.構建ServiceMethod例項
  先看第23行

 ServiceMethod serviceMethod = loadServiceMethod(method);

  通過loadServiceMethod方法獲取ServiceMethod例項:
  

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

  第4行首先會先嚐試從serviceMethodCache(型別Map<Method, ServiceMethod>)中get,如果快取中沒有則構建一個ServiceMethod例項。接著看怎麼構建ServiceMethod例項,ServiceMethod.java中原始碼:

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

 public ServiceMethod build() {
      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?");
      }
      responseConverter = createResponseConverter();

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

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

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

  使用Builder模式來構建serviceMethod例項,將retrofit例項,和invoke的方法作為引數來初始化。第10行通過呼叫createCallAdapter()建立CallAdapter例項callAdapter。

 private CallAdapter<?> createCallAdapter() {
      Type returnType = method.getGenericReturnType();
      if (Utils.hasUnresolvableType(returnType)) {
        throw methodError(
            "Method return type must not include a type variable or wildcard: %s", returnType);
      }
      if (returnType == void.class) {
        throw methodError("Service methods cannot return void.");
      }
      Annotation[] annotations = method.getAnnotations();
      try {
        return retrofit.callAdapter(returnType, annotations);
      } catch (RuntimeException e) { // Wide exception range because factories are user code.
        throw methodError(e, "Unable to create call adapter for %s", returnType);
      }
    }

  看第11行,獲取到了,介面方法中使用的註解,在我們本文中的GithubService的listRepos的註解就是@Get();第13行會通過呼叫Retrofit的callAdapter()方法來返回CallAdapter例項:
  Retrofit.java檔案中:
  

/**
   * Returns the {@link CallAdapter} for {@code returnType} from the available {@linkplain
   * #callAdapterFactories() factories}.
   *
   * @throws IllegalArgumentException if no call adapter available for {@code type}.
   */
  public CallAdapter<?> callAdapter(Type returnType, Annotation[] annotations) {
    return nextCallAdapter(null, returnType, annotations);
  }

  /**
   * Returns the {@link CallAdapter} for {@code returnType} from the available {@linkplain
   * #callAdapterFactories() factories} except {@code skipPast}.
   *
   * @throws IllegalArgumentException if no call adapter available for {@code type}.
   */
  public CallAdapter<?> nextCallAdapter(CallAdapter.Factory skipPast, Type returnType,
      Annotation[] annotations) {
    checkNotNull(returnType, "returnType == null");
    checkNotNull(annotations, "annotations == null");

    int start = adapterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = adapterFactories.size(); i < count; i++) {
      CallAdapter<?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
      if (adapter != null) {
        return adapter;
      }
    }

    StringBuilder builder = new StringBuilder("Could not locate call adapter for ")
        .append(returnType)
        .append(".\n");
    if (skipPast != null) {
      builder.append("  Skipped:");
      for (int i = 0; i < start; i++) {
        builder.append("\n   * ").append(adapterFactories.get(i).getClass().getName());
      }
      builder.append('\n');
    }
    builder.append("  Tried:");
    for (int i = start, count = adapterFactories.size(); i < count; i++) {
      builder.append("\n   * ").append(adapterFactories.get(i).getClass().getName());
    }
    throw new IllegalArgumentException(builder.toString());
  }

  真的實現是在Retrofit.java中的nextCallAdapter()方法中實現的。上邊程式碼第25行,adapterFactories是一個快取CallAdapter.Factory的list,我們在構建Retrofit例項時,曾經設定過addCallAdapterFactory(RxJavaCallAdapterFactory.create()),也就意味著adapterFactories.get(i)將獲得到RxJavaCallAdapterFactory,然後會執行其get()方法。
  RxJavaCallAdapterFactory.java檔案中:
  

 @Override
  public CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    Class<?> rawType = getRawType(returnType);
    String canonicalName = rawType.getCanonicalName();
    boolean isSingle = "rx.Single".equals(canonicalName);
    boolean isCompletable = "rx.Completable".equals(canonicalName);
    if (rawType != Observable.class && !isSingle && !isCompletable) {
      return null;
    }
    if (!isCompletable && !(returnType instanceof ParameterizedType)) {
      String name = isSingle ? "Single" : "Observable";
      throw new IllegalStateException(name + " return type must be parameterized"
          + " as " + name + "<Foo> or " + name + "<? extends Foo>");
    }

    if (isCompletable) {
      // Add Completable-converter wrapper from a separate class. This defers classloading such that
      // regular Observable operation can be leveraged without relying on this unstable RxJava API.
      // Note that this has to be done separately since Completable doesn't have a parametrized
      // type.
      return CompletableHelper.createCallAdapter(scheduler);
    }

    CallAdapter<Observable<?>> callAdapter = getCallAdapter(returnType, scheduler);
    if (isSingle) {
      // Add Single-converter wrapper from a separate class. This defers classloading such that
      // regular Observable operation can be leveraged without relying on this unstable RxJava API.
      return SingleHelper.makeSingle(callAdapter);
    }
    return callAdapter;
  }

  正常情況下,會執行到第27行,通過getCallAdapter方法,獲得Observable泛型的CallAdapter,繼續跟進去看看:
  

 private CallAdapter<Observable<?>> getCallAdapter(Type returnType, Scheduler scheduler) {
    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>");
      }
      Type responseType = getParameterUpperBound(0, (ParameterizedType) observableType);
      return new ResponseCallAdapter(responseType, scheduler);
    }

    if (rawObservableType == Result.class) {
      if (!(observableType instanceof ParameterizedType)) {
        throw new IllegalStateException("Result must be parameterized"
            + " as Result<Foo> or Result<? extends Foo>");
      }
      Type responseType = getParameterUpperBound(0, (ParameterizedType) observableType);
      return new ResultCallAdapter(responseType, scheduler);
    }

    return new SimpleCallAdapter(observableType, scheduler);
  }

  第3行會getRawType(observableType)會返回Observable具體型別,例如本例中,

 public interface GitHubService {
    @GET("users/{user}/repos")
    Observable<List<Repo>> listRepos(@Path("user") String user);
 }

  返回值是Observable<List<Repo>> 那麼getRawType(observableType)將得到List.class,這樣程式將執行第22行,將返回一個SimpleCallAdapter例項,那麼它就是CallAdapter的真正實現。
  回到ServiceMethod的build()方法中,繼續執行第18行responseConverter = createResponseConverter();過程也和createCallAdapter差不多,也是使用工廠模式,通過get()方法獲得Converter的真正實現,篇幅所限就不一步一步的跟了,本例使用的是Gson,所以Converter得實現是GsonResponseBodyConverter。之後就是解析註解,引數等,這樣ServiceMethod已經構建好了。
  
  生成OkHttpCall
  回到Retrofit動態代理invoke方法中,第二步就是生成Call的例項了,目前Retrofit的預設使用的是OkhttpCall。其是OkHttp的包裝類,所有OkHttp需要的引數都在該類中找到。

  adapt Call
  第三步執行serviceMethod.callAdapter.adapt(okHttpCall),通過之前的分析,serviceMethod.callAdapter真正的實現是SimpleCallAdapter,那麼自然也是執行的它的adapt()方法。為了證實這個,可以debug一下就知道了。
  這裡寫圖片描述

  RxJavaCallAdapterFactory.SimpleCallAdapter.adapt():
  

@Override public <R> Observable<R> adapt(Call<R> call) {
      Observable<R> observable = Observable.create(new CallOnSubscribe<>(call)) 
          .lift(OperatorMapResponseToBodyOrError.<R>instance());
      if (scheduler != null) {
        return observable.subscribeOn(scheduler);
      }
      return observable;
    }
  }

  其返回型別Observable,也就是我們需要的結果,還記得我們是怎麼使用的RxJava的嗎?
  

service.listRepos("octocat")
              .observeOn(AndroidSchedulers.mainThread())
              .subscribeOn(Schedulers.io())
              .subscribe(list->{
              if(list!=null){
              //TODO 取得資料後邏輯處理
              }
             });

  看程式碼service.listRepos(“octocat”)應該是一個Observable,而剛剛分析的,service.listRepos(“octocat”)確實會返回一個Observable,有了這個Observable之後,我們就可以通過subscribeOn給他指定執行執行緒,這樣像網路請求耗時操作就不會再UI執行緒中執行,從而達到非同步的目的,然後通過observeOn()將執行緒切回UI執行緒,當Okhttp請求完資料並進行相應的convert之後,就可以在UI處理相應的邏輯。
  回到adapt方法,第2行建立Observable,而new CallOnSubscribe<>(call)生成了一個OnSubscribe()的例項,而OnSubscribe繼承自Action1,其只包含一個call()方法,而這個call是在CallOnSubscribe中實現:
  

static final class CallOnSubscribe<T> implements Observable.OnSubscribe<Response<T>> {
    private final Call<T> originalCall;

    CallOnSubscribe(Call<T> originalCall) {
      this.originalCall = originalCall;
    }

    @Override public void call(final Subscriber<? super Response<T>> subscriber) {
      // Since Call is a one-shot type, clone it for each new subscriber.
      Call<T> call = originalCall.clone();

      // Wrap the call in a helper which handles both unsubscription and backpressure.
      RequestArbiter<T> requestArbiter = new RequestArbiter<>(call, subscriber);
      subscriber.add(requestArbiter);
      subscriber.setProducer(requestArbiter);
    }
  }

  首先clone了一份Call,然後生成了RequestArbiter,他繼承自AtomicBoolean,實現了Subscription, Producer介面,Producer只有一個request方法;一般實現該介面的類,都會包含一個Subscriber物件和一個待處理的資料:
  

static final class RequestArbiter<T> extends AtomicBoolean implements Subscription, Producer {
    private final Call<T> call;
    private final Subscriber<? super Response<T>> subscriber;

    RequestArbiter(Call<T> call, Subscriber<? super Response<T>> subscriber) {
      this.call = call;
      this.subscriber = subscriber;
    }

    @Override public void request(long n) {
      if (n < 0) throw new IllegalArgumentException("n < 0: " + n);
      if (n == 0) return; // Nothing to do when requesting 0.
      if (!compareAndSet(false, true)) return; // Request was already triggered.

      try {
        Response<T> response = call.execute();
        if (!subscriber.isUnsubscribed()) {
          subscriber.onNext(response);
        }
      } catch (Throwable t) {
        Exceptions.throwIfFatal(t);
        if (!subscriber.isUnsubscribed()) {
          subscriber.onError(t);
        }
        return;
      }

      if (!subscriber.isUnsubscribed()) {
        subscriber.onCompleted();
      }
    }

    @Override public void unsubscribe() {
      call.cancel();
    }

    @Override public boolean isUnsubscribed() {
      return call.isCanceled();
    }
  }

  那麼看看request()方法到底幹了什麼?第16行程式碼call.execute()最終將執行okhttp的execute(),為什麼這裡會同步請求資料呢,還記得之前我們把耗時操作切換到子執行緒中嗎?既然已經不是在UI執行緒了,這裡就可以使用同步獲取response資料了。在獲取到response後就通過執行subscriber.onNext(response);這樣subscribe過Observable的subscriber都可以收到資料了。而因為我們設定過observeOn(AndroidSchedulers.mainThread()),所以當我們接受到資料的時候,已經是在UI執行緒中了,所以就可以做後續的邏輯處理了。
  總結:Retrofit 的程式碼並不是很多,其底層網路通訊時交由 OkHttp 3來完成的,但是Retrofit運用了大量的設計模式,程式碼邏輯很清晰。沒事的時候,不妨研究研究,畢竟是Jake Wharton大神的傑作。
  

相關文章