深入retrofit 2.0 源碼分析

安卓開發領域中,很多重要的問題都有很好的開源解決方案,例如Square公司提供網絡請求 OkHttp , Retrofit 和現在非常流行的異步處理框架Rxjava。Square 公司開源的 Retrofit 更是以其簡易的接口配置、強大的擴展支持、優雅的代碼結構受到大家的喜愛。

1.初識Retrofit

認識Retrofit首先要從怎么使用開發,其次我們在使用過程深入了解其內部原理。最好下載源碼去閱讀。本文的例子來自 Retrofit 官方網站

1.1 Retrofit 概覽

Retrofit是一個 RESTful 的 HTTP 網絡請求框架的封裝 。Retrofit 2.0 開始內置 OkHttp,Retrofit專注于接口的封裝,OkHttp專注于網絡請求的高效。
我們App通過 Retrofit 請求網絡,實際上是使用 Retrofit 接口進行封裝請求參數、Header、Url 等信息(Header信息可以用OkHttp的Interceptor實現),之后由 OkHttp 完成后續的請求操作,在服務端返回數據之后,OkHttp 將原始的結果交給 Retrofit, Retrofit根據用戶的需求對結果進行解析的過程。

1.2 Retrofit使用

你先需要在你的 build.gradle 中添加依賴:

compile 'com.squareup.retrofit2:retrofit:2.2.0'

首先我們要構造Retrofit:

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

builder 模式,外觀模式(門面模式),這兩個設計模式,可以看看 stay 的 Retrofit分析-經典設計模式案例這篇文章。

其次在定義 API 接口:

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

看代碼非常簡潔,接口當中的 listRepos方法,除了兩個注解:@GET 和 @Path,就是我們想要訪問的 api 了:

https://api.github.com/users/{user}/repos

其中,在發起請求時, {user}會被替換為方法的第一個參數 user。
再次 創建Api實例:

GitHubService service = retrofit.create(GitHubService.class);
Call<List<Repo>> repos = service.listRepos("octocat");

retrofit.create(GitHubService.class)這樣就創建了 API 實例了,接著service.listRepos("octocat") 設置參數。
發起請求有同步和異步:

//同步
List<Repo> data= repos.execute();
//異步
repos.enqueue(new Callback<List<Repo>>() {
    @Override
    public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
      List<Repo>  data= response.body();
    }

    @Override
    public void onFailure(Call<List<Repo>> call,Throwable t) {

    }
});

看著代碼是不是很簡單。

1.3 Url 配置

@GET("users/{user}/repos")
 Call<List<Repo>> listRepos(@Path("user") String user);

上面的代碼使用兩個注解@GET和 @Path。這些注解都有一個參數 value,用來配置其路徑,比如示例中的 users/{user}/repos,我們還注意到在構造 Retrofit 之時我們還傳入了一個 baseUrl("https://api.github.com/"),
請求的完整 Url 就是通過 baseUrl 與注解的 value(下面稱 “path“ ) 整合起來的,具體整合的規則如下:

建議采用這種方式來配置,并盡量使用同一種路徑形式。

1.4 參數類型

Retrofit支持的協議包括 GET/POST/PUT/DELETE/HEAD/PATCH。發請求時,需要傳入參數,Retrofit 通過注解的形式令 Http 請求的參數變得更加直接,而且類型安全。

1.4.1 Path

@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId);

Path 其實就是 Url 中 {id}替換我們傳進去的groupId的值,上面的請求的如下:

https://api.github.com/group/1/users

1.4.2 Query & QueryMap

@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort);

Query 其實就是 Url 中 ‘?’ 后面的 key-value,上面的請求如下

https://api.github.com/group/1/users?sort=des

如果我有很多個 Query,每個都這么寫豈不是很累?而且根據不同的情況,有些字段可能不傳,這與方法的參數要求顯然也不相符。于是, QueryMap 橫空出世了,使用方法很簡單,如下:

@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId, @QueryMap Map<String, String> options);

1.4.3 Body

post請求的body需要發送一個對象,這是可以用Body,Body的作用是把對象轉換成需要的字符串發送到服務器,比如服務端需要的是關于某一個自定義對象的JSON數據格式。

@POST("users/new")
Call<User> createUser(@Body User user);

怎樣把對象轉換為json 數據是通過converter 實現的。

1.4.4 Field & FieldMap

POST使用場景很多,如何用 POST 提交表單的場景:

@FormUrlEncoded
@POST("user/edit")
Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);

其實也很簡單,我們只需要@FormUrlEncoded,然后用 Field 聲明了表單的項,這樣提交表單就跟普通的函數調用一樣簡單直接了。
如果你也多個Field ,不想一個一個寫,同樣有可以用——FieldMap。

1.4.5 Part & PartMap

我們經常需要上傳文件,Part主要用于文件上傳,有Retrofit 會方便很多。

    @Multipart
    @POST("upload")
    Call<ResponseBody> upload(@Part("description") RequestBody description,@Part MultipartBody.Part file);

定義文件上傳的接口使用了@Multipart,后面只需要在參數中增加 Part 就可以了。這里的 Part 和 Field 究竟有什么區別,其實從功能上講,無非就是客戶端向服務端發起請求攜帶參數的方式不同,并且前者可以攜帶的參數類型更加豐富,包括數據流。也正是因為這一點,我們可以通過這種方式來上傳文件,下面我們就給出這個接口的使用方法:

File file = new File(filename);
RequestBody requestFile =RequestBody.create(MediaType.parse("application/otcet-stream"), file);
MultipartBody.Part body =MultipartBody.Part.createFormData("aFile", file.getName(), requestFile);
 
String descriptionString = "This is a description";
RequestBody description =RequestBody.create(MediaType.parse("multipart/form-data"), descriptionString);
 
Call<ResponseBody> call = service.upload(description, body);
call.enqueue(new Callback<ResponseBody>() {
  @Override
  public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
    System.out.println("success");
  }
 
  @Override
  public void onFailure(Call<ResponseBody> call, Throwable t) {
    t.printStackTrace();
  }
});

如有用多個文件上傳可以建多個Part 或使用PartMap。

1.4.6 Headers

@Headers("Cache-Control: max-age=640000")
@GET("widget/list")
Call<List<Widget>> widgetList();

Headers 主要用于添加頭控制,Cache-Control 控制是否保存數據,下次再規定時間內是否去服務器請求。

2 Retrofit源碼解析

Retrofit 的基本用法和概念介紹了一下,如果你的目標是學會如何使用它,那么下面的內容你可以不用看了

2.1 Api創建

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

Retrofit是如何創建Api的,是通過Retrofit類的create的方法。源碼如下

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);
            }
           //DefaultMethod 是 Java 8 的概念,是定義在 interface 當中的有實現的方法
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
             //每一個接口最終實例化成一個 ServiceMethod,并且會緩存
            ServiceMethod serviceMethod = loadServiceMethod(method);
            //Retrofit 與 OkHttp 完全耦合
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
          //發起請求,并解析服務端返回的結果
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

創建 API 實例使用的是動態代理技術,關于動態代理的詳細介紹,可以查看 codeKK 公共技術點之 Java 動態代理這篇文章。
簡而言之,就是動態生成接口的實現類(當然生成實現類有緩存機制),并創建其實例(稱之為代理),代理把對接口的調用轉發給 InvocationHandler實例,而在 InvocationHandler的實現中,除了執行真正的邏輯(例如再次轉發給真正的實現類對象),我們還可以進行一些有用的操作,例如統計執行時間、進行初始化和清理、對接口調用進行檢查等。

為什么要用動態代理?因為對接口的所有方法的調用都會集中轉發到 InvocationHandler#invoke函數中,我們可以集中進行處理,更方便了。你可能會想,我也可以手寫這樣的代理類,把所有接口的調用都轉發到 InvocationHandler#invoke,當然可以,但是可靠地自動生成豈不更方便,可以方便為什么不方便。

2.2 Api使用

create方法真正我們要看是下面三行代碼:

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

在分析具體三行代碼可以看下Stay 在 Retrofit分析-漂亮的解耦套路 這篇文章中分享的流程圖,完整的流程概覽建議仔細看看這篇文章:

retrofit

2.3 ServiceMethod<T>

Adapts an invocation of an interface method into an HTTP call.
把對接口方法的調用轉為一次 HTTP 調用。

一個 ServiceMethod 對象對應于一個 API interface 的一個方法,loadServiceMethod(method) 方法負責加載 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;
  }

這里實現了緩存邏輯,同一個 API 的同一個方法,只會創建一次。這里由于我們每次獲取 API 實例都是傳入的 class 對象,而 class 對象是進程內單例的,所以獲取到它的同一個方法 Method 實例也是單例的,所以這里的緩存是有效的。

我們再看 ServiceMethod 的構造函數:

ServiceMethod(Builder<T> builder) {
    this.callFactory = builder.retrofit.callFactory();
    this.callAdapter = builder.callAdapter;
    this.baseUrl = builder.retrofit.baseUrl();
    this.responseConverter = builder.responseConverter;
    this.httpMethod = builder.httpMethod;
    this.relativeUrl = builder.relativeUrl;
    this.headers = builder.headers;
    this.contentType = builder.contentType;
    this.hasBody = builder.hasBody;
    this.isFormEncoded = builder.isFormEncoded;
    this.isMultipart = builder.isMultipart;
    this.parameterHandlers = builder.parameterHandlers;
  }

這里用了buider 模式,這里很多參數,有的參數一看就明白 像baseUrl,headers ,contyentType等,講解主要關注四個成員:callFactory,callAdapter,responseConverter 和 parameterHandlers。

  • callFactory 負責創建 HTTP 請求,HTTP 請求被抽象為了 okhttp3.Call 類,它表示一個已經準備好,可以隨時執行的 HTTP 請求;
  • callAdapter 把 retrofit2.Call<T> 轉為 T(注意和 okhttp3.Call 區分開來,retrofit2.Call<T> 表示的是對一個 Retrofit 方法的調用),這個過程會發送一個 HTTP 請求,拿到服務器返回的數據(通過 okhttp3.Call 實現),并把數據轉換為聲明的 T 類型對象(通過 Converter<F, T> 實現);
  • responseConverter 是 Converter<ResponseBody, T> 類型,負責把服務器返回的數據(JSON、XML、二進制或者其他格式,由 ResponseBody 封裝)轉化為 T 類型的對象;
  • parameterHandlers 則負責解析 API 定義時每個方法的參數,并在構造 HTTP 請求時設置參數;

2.3.1 callFactory

this.callFactory = builder.retrofit.callFactory(),所以 callFactory實際上由 Retrofit類提供,而我們在造 Retrofit
對象時,可以指定 callFactory,如果不指定,將默認設置為一個 okhttp3.OkHttpClient。

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

可以看到,callAdapter 還是由 Retrofit 類提供。在 Retrofit 類內部,將遍歷一個 CallAdapter.Factory 列表,讓工廠們提供,如果最終沒有工廠能(根據 returnType 和 annotations)提供需要的 CallAdapter,那將拋出異常。而這個工廠列表我們可以在構造 Retrofit 對象時進行添加。

2.3.2 responseConverter

 private Converter<ResponseBody, T> createResponseConverter() {
      Annotation[] annotations = method.getAnnotations();
      try {
        return retrofit.responseBodyConverter(responseType, annotations);
      } catch (RuntimeException e) { // Wide exception range because factories are user code.
        throw methodError(e, "Unable to create converter for %s", responseType);
      }
    }

同樣,responseConverter還是由 Retrofit類提供,而在其內部,邏輯和創建 callAdapter基本一致,通過遍歷 Converter.Factory 列表,看看有沒有工廠能夠提供需要的 responseBodyConverter。工廠列表同樣可以在構造 Retrofit對象時進行添加。

2.3.3,parameterHandlers

每個參數都會有一個 ParameterHandler,由 ServiceMethod#parseParameter方法負責創建,其主要內容就是解析每個參數使用的注解類型(諸如 Path,Query,Field等),對每種類型進行單獨的處理。構造 HTTP 請求時,我們傳遞的參數都是字符串,那 Retrofit 是如何把我們傳遞的各種參數都轉化為 String 的呢?還是由 Retrofit 類提供 converter!

Converter.Factory除了提供上一小節提到的 responseBodyConverter,還提供 requestBodyConverter 和 stringConverter,API 方法中除了 @Body 和 @Part類型的參數,都利用 stringConverter 進行轉換,而 @Body和 @Part類型的參數則利用 requestBodyConverter 進行轉換。

這三種 converter 都是通過“詢問”工廠列表進行提供,而工廠列表我們可以在構造 Retrofit對象時進行添加。

2.3.4,工廠讓各個模塊得以高度解耦

上面提到了三種工廠:okhttp3.Call.Factory,CallAdapter.Factory和 Converter.Factory,分別負責提供不同的模塊,至于怎么提供、提供何種模塊,統統交給工廠,Retrofit 完全不摻和,它只負責提供用于決策的信息,例如參數/返回值類型、注解等。

這不正是我們苦苦追求的高內聚低耦合效果嗎?解耦的第一步就是面向接口編程,模塊之間、類之間通過接口進行依賴,創建怎樣的實例,則交給工廠負責,工廠同樣也是接口,添加(Retrofit doc 中使用 install 安裝一詞,非常貼切)怎樣的工廠,則在最初構造 Retrofit 對象時決定,各個模塊之間完全解耦,每個模塊只專注于自己的職責,全都是套路,值得反復玩味、學習與模仿。

除了上面重點分析的這四個成員,ServiceMethod中還包含了 API 方法的 url 解析等邏輯,包含了眾多關于泛型和反射相關的代碼,有類似需求的時候,也非常值得學習模仿。

2.4 OkHttpCall

OkHttpCall實現了 retrofit2.Call,我們通常會使用它的 execute() 和 enqueue(Callback<T> callback)接口。前者用于同步執行 HTTP 請求,后者用于異步執行。

2.4.1 execute()

 public Response<T> execute() throws IOException {
   //這個 call 是真正的 OkHttp 的 call,本質上 OkHttpCall 只是對它做了一層封裝
    okhttp3.Call call;

    synchronized (this) {
   //處理重復執行的邏輯
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;

      if (creationFailure != null) {
        if (creationFailure instanceof IOException) {
          throw (IOException) creationFailure;
        } else {
          throw (RuntimeException) creationFailure;
        }
      }

      call = rawCall;
      if (call == null) {
        try {
          call = rawCall = createRawCall();
        } catch (IOException | RuntimeException e) {
          creationFailure = e;
          throw e;
        }
      }
    }

    if (canceled) {
      call.cancel();
    }

    return parseResponse(call.execute());
  }

  private okhttp3.Call createRawCall() throws IOException {
    Request request = serviceMethod.toRequest(args);
    okhttp3.Call call = serviceMethod.callFactory.newCall(request);
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }

  Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
    ResponseBody rawBody = rawResponse.body();

    // Remove the body's source (the only stateful object) so we can pass the response along.
    rawResponse = rawResponse.newBuilder()
        .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
        .build();

    int code = rawResponse.code();
    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();
      }
    }

    if (code == 204 || code == 205) {
      return Response.success(null, rawResponse);
    }

    ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
    try {
      T body = serviceMethod.toResponse(catchingBody);
      return Response.success(body, rawResponse);
    } catch (RuntimeException e) {
      // If the underlying source threw an exception, propagate that rather than indicating it was
      // a runtime exception.
      catchingBody.throwIfCaught();
      throw e;
    }
  }

主要包括三步:

  • 創建 okhttp3.Call,包括構造參數;
  • 執行網絡請求;
  • 解析網絡請求返回的數據;

createRawCall() 函數中,我們調用了 serviceMethod.toRequest(args) 來創建 okhttp3.Request,而在后者中,我們之前準備好的 parameterHandlers 就派上了用場。

然后我們再調用 serviceMethod.callFactory.newCall(request) 來創建 okhttp3.Call,這里之前準備好的 callFactory 同樣也派上了用場,由于工廠在構造 Retrofit 對象時可以指定,所以我們也可以指定其他的工廠(例如使用過時的 HttpURLConnection 的工廠),來使用其它的底層 HttpClient 實現。

我們調用 okhttp3.Call#execute() 來執行網絡請求,這個方法是阻塞的,執行完畢之后將返回收到的響應數據。收到響應數據之后,我們進行了狀態碼的檢查,通過檢查之后我們調用了 serviceMethod.toResponse(catchingBody) 來把響應數據轉化為了我們需要的數據類型對象。在 toResponse 函數中,我們之前準備好的 responseConverter 也派上了用場.

2.4.2 enqueue(Callback<T> callback)

這里的異步交給了 okhttp3.Call#enqueue(Callback responseCallback)來實現,并在它的 callback 中調用 parseResponse解析響應數據,并轉發給傳入的 callback。

2.5 CallAdapter

終于到了最后一步了,CallAdapter<T>#adapt(Call<R> call)函數負責把 retrofit2.Call<R> 轉為 T。這里 T當然可以就是 retrofit2.Call<R>,這時我們直接返回參數就可以了,實際上這正是 DefaultCallAdapterFactory創建的 CallAdapter 的行為。至于其他類型的工廠返回的 CallAdapter的行為,這里暫且不表,后面再單獨分析。

至此,一次對 API 方法的調用是如何構造并發起網絡請求、以及解析返回數據,這整個過程大致是分析完畢了。對整個流程的概覽非常重要,結合 stay 畫的流程圖,應該能夠比較輕松地看清整個流程了。

2.6 retrofit-adapters 模塊

retrofit 模塊內置了 DefaultCallAdapterFactory 和 ExecutorCallAdapterFactory,它們都適用于 API 方法得到的類型為 retrofit2.Call 的情形,前者生產的 adapter 啥也不做,直接把參數返回,后者生產的 adapter 則會在異步調用時在指定的 Executor 上執行回調。

retrofit-adapters 的各個子模塊則實現了更多的工廠:GuavaCallAdapterFactory,Java8CallAdapterFactory 和 RxJavaCallAdapterFactory。這里我主要分析 RxJavaCallAdapterFactory,下面的內容就需要一些 RxJava的知識了.

RxJavaCallAdapterFactory#get 方法中對返回值的類型進行了檢查,只支持 rx.Single,rx.Completable 和 rx.Observable,這里我主要關注對 rx.Observable 的支持。

RxJavaCallAdapterFactory#getCallAdapter 方法中對返回值的泛型類型進行了進一步檢查,例如我們聲明的返回值類型為 Observable<List<Repo>>,泛型類型就是 List<Repo>,這里對 retrofit2.Response 和 retrofit2.adapter.rxjava.Result 進行了特殊處理,有單獨的 adapter 負責進行轉換,其他所有類型都由 SimpleCallAdapter 負責轉換。

那我們就來看看 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,它的邏輯由 CallOnSubscribe 類實現,同時使用了一個 OperatorMapResponseToBodyOrError 操作符,用來把 retrofit2.Response 轉為我們聲明的類型,或者錯誤異常類型。

這里創建了一個 Observable,它的邏輯由 CallOnSubscribe 類實現,同時使用了一個 OperatorMapResponseToBodyOrError 操作符,用來把 retrofit2.Response 轉為我們聲明的類型,或者錯誤異常類型。

我們接著看 CallOnSubscribe#call:

@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,因為 okhttp3.Call 是只能用一次的,所以每次都是新 clone 一個進行網絡請求;
  • 創建了一個叫做 RequestArbiter 的 producer,別被它的名字嚇懵了,它就是個 producer;
  • 把這個 producer 設置給 subscriber;

簡言之,大部分情況下 Subscriber 都是被動接受 Observable push 過來的數據,但要是 Observable 發得太快,Subscriber 處理不過來,那就有問題了,所以就有了一種 Subscriber 主動 pull 的機制,而這種機制就是通過 Producer 實現的。給 Subscriber 設置 Producer 之后(通過 Subscriber#setProducer 方法),Subscriber 就會通過 Producer 向上游根據自己的能力請求數據(通過 Producer#request 方法),而 Producer 收到請求之后(通常都是 Observable 管理 Producer,所以“相當于”就是 Observable 收到了請求),再根據請求的量給 Subscriber 發數據。

那我們就看看 RequestArbiter#request:

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

實際干活的邏輯就是執行 call.execute(),并把返回值發送給下游。

而 OperatorMapResponseToBodyOrError#call也相當簡短:

@Override
public Subscriber<? super Response<T>> call(final Subscriber<? super T> child) {
  return new Subscriber<Response<T>>(child) {
    @Override
    public void onNext(Response<T> response) {
      if (response.isSuccessful()) {
        child.onNext(response.body());
      } else {
        child.onError(new HttpException(response));
      }
    }

    @Override
    public void onCompleted() {
      child.onCompleted();
    }

    @Override
    public void onError(Throwable e) {
      child.onError(e);
    }
  };
}

關鍵就是調用了 response.body() 并發送給下游。這里,body() 返回的就是我們聲明的泛型類型了,至于 Retrofit 是怎么把服務器返回的數據轉為我們聲明的類型的,這就是 responseConverter 的事了。
總結下RxJavaCallAdapterFactory 執行路徑就是:

  • Observable.subscribe,觸發 API 調用的執行;
  • CallOnSubscribe#call,clone call,創建并設置 producer;
  • RequestArbiter#request,subscriber 被設置了 producer 之后最終調用 request,在 request 中發起請求,把結果發給下游;
  • OperatorMapResponseToBodyOrError$1#onNext,把 response 的 body 發給下游;
  • 最終就到了我們 subscribe 時傳入的回調里面了;

2.7,retrofit-converters 模塊

retrofit 模塊內置了 BuiltInConverters,只能處理 ResponseBody, RequestBody 和 String類型的轉化(實際上不需要轉)。而 retrofit-converters 中的子模塊則提供了 JSON,XML,ProtoBuf 等類型數據的轉換功能,而且還有多種轉換方式可以選擇。這里我主要關注 GsonConverterFactory。
代碼非常簡單:

 @Override
  public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
      Retrofit retrofit) {
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
    return new GsonResponseBodyConverter<>(gson, adapter);
  }

  @Override
  public Converter<?, RequestBody> requestBodyConverter(Type type,
      Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
    return new GsonRequestBodyConverter<>(gson, adapter);
  }

根據目標類型,利用 Gson#getAdapter獲取相應的 adapter,轉換時利用 Gson 的 API 即可。

3 總結

以上就是retrofit 2.0 使用和代碼部分功能分析,把retrofit 大致流程走了一邊。如有有不對的請指出。

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

推薦閱讀更多精彩內容