Retrofit2源碼解讀

綜述

  Retrofit2的用法在Retrofit2.0使用詳解這篇文章中已經詳細介紹過了。那麼在這就來看一下Retrofit2它是如何實現的。Retrofit2中它的內部網絡請求是依賴於OKHttp,所以Retrofit2可以看做是對OKHttp的一次封裝,那麼下面就開看下Retrofit2是如何對OKHttp進行封裝的。

回顧Retrofit2的使用

  在這裏首先來回顧一下Retrofit2的使用。對於Retrofit2的使用可以分爲三步。
  首先,我們創建一個Java接口GitHubService來作爲Http請求接口。

public interface GitHubService {

    @GET("repos/{owner}/{repo}/contributors")
    Call<ResponseBody> contributorsBySimpleGetCall(@Path("owner") String owner,
@Path("repo") String repo);
}

  然後我們需要創建一個Retrofit實例,並通過Retrofit對象創建一個GitHubService接口的實現。

Retrofit retrofit = new Retrofit.Builder()
        //添加對RxJava的支持
        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
        //添加對Json轉換器的支持
        .addConverterFactory(GsonConverterFactory.create())
        .baseUrl("https://api.github.com/")
        .build();
GitHubService service = retrofit.create(GitHubService.class);

  最後通過調用我們創建的接口便能夠向後臺發起請求。

Call<ResponseBody> call = service.contributorsBySimpleGetCall("square", "retrofit");
call.enqueue(new Callback<ResponseBody>() {
    @Override
    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
        ......
    }

    @Override
    public void onFailure(Call<ResponseBody> call, Throwable t) {
        ......
    }
});

Retrofit2源碼分析

  首先通過@GET來標識這個接口是一個GET請求。那麼看一下這個GET註解的定義。

package retrofit2.http;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import okhttp3.HttpUrl;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface GET {
  String value() default "";
}

  從註解中可以看出這個註解是對方法的聲明,並且在運行時VM依然保留註解。Java自定義註解在這裏就不在過多說明,可以參考 Java註解在Android中使用這篇文章。
  下面就再來看一下是如何創建Retrofit對象的。對於Retrofit對象的創建採用的是Builder模式。那麼在這裏我們就來看一下這個Builder類。

public static final class Builder {

  //對平臺的支持
  private Platform platform;
  //發起請求OKHttp3的Client工廠
  private okhttp3.Call.Factory callFactory;
  //OKHttp2的HttpUrl對象,也就是將我們傳入的baseUrl字符串包裝成HttpUrl
  private HttpUrl baseUrl;
  //轉換器工廠集合,retrofit可以插入多個轉化器,例如:Gson,Jackson等等
  private List<Converter.Factory> converterFactories = new ArrayList<>();
  //用於發起請求和接收響應的Call適配器工廠集合,
  //Retrofit對RxJava的支持就是通過在該集合中添加RxJavaCallAdapterFactory,
  //而RxJavaCallAdapterFactory正是繼承自CallAdapter.Factory
  private List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
  //Executor併發框架,用於對請求後響應結果的回調執行
  private Executor callbackExecutor;
  //是否需要立即生效
  private boolean validateEagerly;

  Builder(Platform platform) {
    this.platform = platform;
    // Add the built-in converter factory first. This prevents overriding its behavior but also
    // ensures correct behavior when using converters that consume all types.
    converterFactories.add(new BuiltInConverters());
  }

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

  對於Retrofit中的屬性配置已經在註釋中進行說明。在這裏在對Platform進行一下介紹。我們可以看出通過Platform.get()來獲取到當前運行的平臺。下面就進出Platform.get()方法中來查看一下Retrofit到底支持哪些平臺。

  private static final Platform PLATFORM = findPlatform();

  static Platform get() {
    return PLATFORM;
  }

  private static Platform findPlatform() {
    try {
      Class.forName("android.os.Build");
      if (Build.VERSION.SDK_INT != 0) {
        return new Android();
      }
    } catch (ClassNotFoundException ignored) {
    }
    try {
      Class.forName("java.util.Optional");
      return new Java8();
    } catch (ClassNotFoundException ignored) {
    }
    try {
      Class.forName("org.robovm.apple.foundation.NSObject");
      return new IOS();
    } catch (ClassNotFoundException ignored) {
    }
    return new Platform();
  }

  在Platform的get方法中實際上還是調用了findPlatform方法,通過findPlatform方法我們可以看出Retrofit支持三個平臺,它們分別是Android,Java8,IOS。這裏的IOS指的是RoboVM。RoboVM它是一種可以在iOS設備上運行Java應用程序的技術,這種技術主要還是用於在遊戲開發中。在這裏我們只討論在Android平臺中的使用。在獲取到當前平臺爲Android平臺之後返回一個Android對象,這個Android類是Platform中的一個靜態內部類,並且它繼承自Platform。下面就在來看一下這個Android類。

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

    @Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {
      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);
      }
    }
  }

  在這個Android類中重寫了父類的defaultCallbackExecutor和defaultCallAdapterFactory方法。
對於defaultCallbackExecutor方法它所返回的一個Executor,從它的實現類MainThreadExecutor可以看出,實際上就是在主線中創建一個Handler對象,然後通過Handler的post方法將請求的結果回調到主線程中運行。而defaultCallAdapterFactory它是默認的Call適配器,它是一個ExecutorCallAdapterFactory對象,對於ExecutorCallAdapterFactory在後面會進行說明。
在這裏完成了對於Retrofit對象的創建以後,便通過Retrofit中的create方法創建一個我們請求接口的實現。下面就來看一下這個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);
            }
            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);
          }
        });
  }

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

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

  在create方法中首先會進行判斷所傳入進來的Service是否是一個接口,當我們將validateEagerly屬性設爲true的時候,在我們調用create方法創建一個Service,就直接調用eagerlyValidateMethods方法。而eagerlyValidateMethods的作用是通過反射獲取我們創建service接口中所有的接口方法,然後根據接口方法和當前的retrofit對象來獲得ServiceMethod並且以接口方法作爲Key,ServiceMethod作爲值添加到serviceMethodCache緩存中。下次便可以通過接口方法直接獲取ServiceMethod。
  現在在往下看我們就明白爲什麼通過接口來作爲一個Http請求,以及爲什麼調用create方法時需要驗證我們的service是否爲一個接口。因爲在這裏是通過Java的動態代理來實現Http請求,並返回一個代理類對象。對於Java的動態代理它是需要委託類與代理類實現同一個接口,在這裏也就是我們創建的service請求接口。對於Java的動態代理可參考Java設計模式之代理模式這篇文章。當我們通過代理類(也就是我們調用create方法後返回的service)調用我們所創建的接口方法時。InvocationHandler中的invoke方法將會被調用。在invoke方法中由於method.getDeclaringClass()獲取到的是一個接口,並不是Object類,所以第一個條件不成立。而在Android平臺下platform.isDefaultMethod(method)返回的爲false,所以這個條件也不成立。之後通過loadServiceMethod方法來獲取ServiceMethod。最後調用ServiceMethod中callAdapter的adapt方法。而ServiceMethod中的callAdapter屬性是通過ServiceMethod中createCallAdapter方法所創建。在createCallAdapter中實際上是調用了Retrofit中的callAdapter方法來對ServiceMethod中的callAdapter進行初始化。下面再看一下Retrofit中的callAdapter方法。

  public CallAdapter<?> callAdapter(Type returnType, Annotation[] annotations) {
    return nextCallAdapter(null, returnType, annotations);
  }

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

    ......
  }

  在這段代碼中,通過遍歷adapterFactories並根據我們的接口方法所中返回值類型來獲取響應的適配器callAdapter。例如上面的例子中返回值是一個Call對象,將會採用默認的適配器,如果我們返回的是RxJava中Observable對象,如果我們添加了RxJavaCallAdapterFactory,那麼返回的就是RxJavaCallAdapter。如果沒有添加那麼此處的adapter爲null,便會拋出異常。在正是通過這種適配器模式完成了對RxJava的完美結合。下面就以Retrofit默認的callAdapter爲例來看一下是如何調用OKHttp來完成對網絡的請求的。對於此時的callAdapter就是通過platform.defaultCallAdapterFactory(callbackExecutor)所創建的適配器。從剛纔觀察Platform內部類Android中可以看出它返回的是一個ExecutorCallAdapterFactory對象。下面就來看一下這個ExecutorCallAdapterFactory類。

final class ExecutorCallAdapterFactory extends CallAdapter.Factory {
  final Executor callbackExecutor;

  ExecutorCallAdapterFactory(Executor callbackExecutor) {
    this.callbackExecutor = callbackExecutor;
  }

  @Override
  public CallAdapter<Call<?>> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != Call.class) {
      return null;
    }
    final Type responseType = Utils.getCallResponseType(returnType);
    return new CallAdapter<Call<?>>() {
      @Override public Type responseType() {
        return responseType;
      }

      @Override public <R> Call<R> adapt(Call<R> 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;
    }

    @Override public void enqueue(final Callback<T> callback) {
      if (callback == null) throw new NullPointerException("callback == null");

      delegate.enqueue(new Callback<T>() {
        @Override public void onResponse(final 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(call, new IOException("Canceled"));
              } else {
                callback.onResponse(call, response);
              }
            }
          });
        }

        @Override public void onFailure(final Call<T> call, final Throwable t) {
          callbackExecutor.execute(new Runnable() {
            @Override public void run() {
              callback.onFailure(call, t);
            }
          });
        }
      });
    }
    ......
}

  從ExecutorCallAdapterFactory類中可看出通過get方法返回一個CallAdapter對象,從對CallAdapter的實現中可以看出在CallAdapter中的adapt方法返回的是ExecutorCallbackCall對象。它實現了Retrofit中的Call接口。到這裏我們也就明白了,當通過代理調用我們創建的接口方法中所返回的Call對象就是這個ExecutorCallbackCall。當我們通過call.enqueue來完成網絡請求操作實際上就是調用ExecutorCallbackCall中的enqueue方法。在ExecutorCallbackCall中enqueue又將網絡請求委託給OkHttpCall去執行。而這個OkHttpCall正是我們在Retrofit的create方法中所創建的OkHttpCall。由於OKHttp的CallBack接口中的onResponse和onFailure是在子線程中執行的,所以在這時候又通過callbackExecutor將CallBack的onResponse和onFailure切換到主線程中執行。

總結

  在這裏對於Retrofit2的源碼分析就結束了,在這裏我們只分析了通過GET方式進行異步請求這一種情況,對於同步,以及POST請求等原理類似,在這就不在進行詳細說明。在Retrofit2中我們可以發現它的代碼雖然不多,但是卻大量用到了Java中的設計模式。很是值得我們學習。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章