Retrofit原始碼分析

Humble先生發表於2020-11-08

Retrofit原始碼解析

在學習了Retrofit分析-漂亮的解耦套路(視訊版)後,自己又仔細的鑽研了一下Retrofit的原始碼,也大致清楚了Retrofit進行網路請求的步驟。好記性不如爛筆頭,以文章的形式將對於Retrofit的思考記錄下來,也加深理解。(如有錯誤,請不吝賜教)

分析原始碼的一般姿勢

首先我們聊一下對於一個優秀的libary,應該怎樣一步一步地分析它。在上面的視訊中,介紹了一個很好的方法。分為三個階段:

  • what: 這個框架是用來幹什麼的,暴露給使用者的API都是用來幹嘛的。
  • how: 這個API內部是怎樣實現的,整個框架正常呼叫時內部是一個怎樣的流程。
  • why: 為什麼要這樣實現,有什麼好處,可不可以有其他的實現,與原來的實現相比怎麼樣。

通過了解、深入、思考,將整個框架一層一層地剖析出來,由淺入深,最後從全域性再看這個框架,或許我們會不自禁的讚歎程式碼的美妙。

Retrofit使用(what)

詳細的瞭解Retrofit使用及API可以到Retrofit的Github

簡單使用

首先,我們需要建立一個請求介面,內部是一個請求方法,通過註解的方式定義請求型別及url

public interface IHttpRequestTest {
    @GET("api/data/福利/{number}/{page}")
    Call<BaseModel<ArrayList<Benefit>>> getBenefits(@Path("number") int number, @Path("page") int page);
}
複製程式碼

然後我們就可以愉快地使用Retrofit。

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://gank.io/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
IHttpRequestTest iHttpRequestTest = retrofit.create(IHttpRequestTest.class);
Call<BaseModel<ArrayList<Benefit>>> call = iHttpRequestTest.getBenefits(40, 2);
call.enqueue(new Callback<BaseModel<ArrayList<Benefit>>>() {
    @Override
    public void onResponse(@NonNull Call<BaseModel<ArrayList<Benefit>>> call, @NonNull Response<BaseModel<ArrayList<Benefit>>> response) {
        if(response.body() != null && response.body().results != null){
            myAdapter.setData(response.body().results);
        }
    }

    @Override
    public void onFailure(@NonNull Call<BaseModel<ArrayList<Benefit>>> call, Throwable t) {
        Toast.makeText(MainActivity.this, "請求失敗:" + t.getMessage(),Toast.LENGTH_SHORT).show();
    }
});
複製程式碼

一般http的請求步驟

http請求步驟
如上圖,對於一般http請求:

  • 生成request請求,包括請求型別,url等等,放入Excutor佇列
  • 請求在Excutor中迴圈進行httpcall請求
  • 等待請求回撥

接下來,我們就可以根據這個一般的http請求步驟,通過debug的形式弄清楚Retrofit的內部邏輯。

Retrofit原始碼解析

探索Retrofit物件初始化時引數的真正含義

首先第一步,就是生產Retrofit物件,並傳入基本的引數。

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://gank.io/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
複製程式碼

進入Retrofit類的原始碼,可以看到build方法

public Retrofit build() {
    if (baseUrl == null) {
    throw new IllegalStateException("Base URL required.");
    }
    
    okhttp3.Call.Factory callFactory = this.callFactory;
    if (callFactory == null) {
        callFactory = new OkHttpClient();
    }
    //如果callbackExcutor為空,建立預設的callbackEcxutor
    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> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
    //新增預設的callAdapterFactories
    callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

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

    // 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());
    converterFactories.addAll(this.converterFactories);
    //如果沒有自定義converterFactories,將使用預設的converterFactories
    converterFactories.addAll(platform.defaultConverterFactories());
    //返回Retrofit物件,包括在Builder中新增的自定義引數。
    return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
        unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
}
複製程式碼

可以看到,生成的retrofit物件有五個引數必須要存在。

  • baseUrl就不用多說了,是必須的。
  • callFactory使用的是OkHttp3中的OkHttpClient
  • callbackExcutorcallAdapterFactory中的callAdapter都對應一般請求步驟中的Excutor,只不過callbackExcutor用於在請求內部回撥中切換執行緒,回撥的方法存在哪個執行緒中取決於callbackExcutor在哪個執行緒。比如請求要在子執行緒中,回撥的方法在主執行緒中。
  • callAdapter是真正的請求物件,callbackExcutor相當於在callAdapter中切換執行緒。
  • converterFactory是用來解析返回的json資料的。當然這些具體的解釋在下面的分析中都會看到。

有原始碼可看出,預設的callbackExcutorcallAdapterFactoryconverterFactory新增都與platform物件有關。這是一個Platform類,進入研究一下。

class Platform {
  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) {
    }
    return new Platform();
  }
  
  @Nullable Executor defaultCallbackExecutor() {return null;}

  List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
      @Nullable Executor callbackExecutor) {...}

  List<? extends Converter.Factory> defaultConverterFactories() {...}

  @IgnoreJRERequirement // Only classloaded and used on Java 8.
  static class Java8 extends Platform {...}

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

    @Override List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
        @Nullable Executor callbackExecutor) {
      if (callbackExecutor == null) throw new AssertionError();
      ExecutorCallAdapterFactory executorFactory = new ExecutorCallAdapterFactory(callbackExecutor);
      return Build.VERSION.SDK_INT >= 24
        ? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
        : singletonList(executorFactory);
    }

    @Override List<? extends Converter.Factory> defaultConverterFactories() {
      return Build.VERSION.SDK_INT >= 24
          ? singletonList(OptionalConverterFactory.INSTANCE)
          : Collections.<Converter.Factory>emptyList();
    }

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

      @Override public void execute(Runnable r) {
        handler.post(r);
      }
    }
  }
}
複製程式碼

內容有點多,這裡我只提取了對現在有用的。整體來看,Platform內部有兩個子類繼承了該類Android與Java8。還有一個get方法,獲取Platform靜態例項,靜態例項呼叫findPlatform()方法。這個方法是根據程式所執行的環境決定是返回一個Android例項還是Java8例項或者Platform例項。這裡我們的環境是Android,所以具體看Android類中的方法。

  • defaultCallbackExecutor()方法,返回了MainThreadExecutor類例項。此類在Android類內部,可以清晰的看出其中的handler靜態變數是用的主執行緒的looper。也就是說,預設的callbackExcutor是執行在主執行緒的。
  • defaultCallAdapterFactories()方法,引數是上面得到的callbackExcutor。方法內部建立了ExecutorCallAdapterFactory類例項,並返回該例項的List。
  • defaultConverterFactories()方法,返回了OptionalConverterFactory例項,此類中的轉換converter()方法只檢查了引數是否為空就將其返回了,說明預設的回撥資料是不進行任何轉換的。

至此,三個預設引數已確定。

獲得自定義介面物件

IHttpRequestTest iHttpRequestTest = retrofit.create(IHttpRequestTest.class); 這是接下來的執行步驟,自定義介面物件由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();
          private final Object[] emptyArgs = new Object[0];

          @Override public 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);
            }
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
          }
        });
  }
複製程式碼

該方法返回的是通過反射獲得自定義介面的代理類,並在類中重寫了invoke方法。這表明當該類中的方法被執行時,就會攔截此方法。

攔截並解析方法。

Call<BaseModel<ArrayList<Benefit>>> call = iHttpRequestTest.getBenefits(40, 2); 由上一步驟可知,當執行代理類中的方法時,會被攔截。並最終執行loadServiceMethod(method).invoke(args != null ? args : emptyArgs);方法並返回。先看一下loadServiceMethod(method)方法。

ServiceMethod<?> loadServiceMethod(Method method) {
    //serviceMethodCache,如果執行過此方法,就會有快取存在這裡,直接取出即可
    ServiceMethod<?> result = serviceMethodCache.get(method);
    if (result != null) return result;

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        result = ServiceMethod.parseAnnotations(this, method);
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }
複製程式碼

嗯...,除了前面與快取有關的東西,又跑到了ServiceMethod類中的parseAnnotations(this, method)方法裡。

abstract class ServiceMethod<T> {
  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);

    Type returnType = method.getGenericReturnType();
    if (Utils.hasUnresolvableType(returnType)) {
      throw methodError(method,
          "Method return type must not include a type variable or wildcard: %s", returnType);
    }
    if (returnType == void.class) {
      throw methodError(method, "Service methods cannot return void.");
    }
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }

  abstract T invoke(Object[] args);
}
複製程式碼

這是一個抽象類,第一句RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);,是解析方法構建request引數的。RequestFactory類中內容比較多,簡單講一下就是通過獲得方法註解、返回型別、方法引數,分別進行解析。

最後它又跑到HttpServiceMethod類中了。此類繼承了ServiceMethod類。

static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {
    CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method);
    Type responseType = callAdapter.responseType();

    Converter<ResponseBody, ResponseT> responseConverter =
        createResponseConverter(retrofit, method, responseType);

    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    return new HttpServiceMethod<>(requestFactory, callFactory, callAdapter, responseConverter);
  }
  
  private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(
      Retrofit retrofit, Method method) {
    Type returnType = method.getGenericReturnType();
    Annotation[] annotations = method.getAnnotations();
    
    return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);
  }

  private static <ResponseT> Converter<ResponseBody, ResponseT> createResponseConverter(
      Retrofit retrofit, Method method, Type responseType) {
    Annotation[] annotations = method.getAnnotations();
    
    return retrofit.responseBodyConverter(responseType, annotations);
  }
複製程式碼

簡化了異常檢測類程式碼,可以看到最後返回的是HttpServiceMethod類的例項物件,並將retrofit中的一些預設引數傳了進去。

解析完了loadServiceMethod(method),接下來就是執行invoke方法了。由於loadServiceMethod(method)返回的是HttpServiceMethod類的例項物件,所以執行的就是HttpServiceMethod類中的invoke方法。

@Override ReturnT invoke(Object[] args) {
    return callAdapter.adapt(
        new OkHttpCall<>(requestFactory, args, callFactory, responseConverter));
  }
複製程式碼

此方法很簡單,呼叫了callAdapter中的adapt方法,引數是一個OkHttpCall,字面意思就是將OkHttpCall適配為其他類。那麼這個類是什麼什麼呢。應該是我們需要返回的Call<BaseModel<ArrayList<Benefit>>>類。那麼怎麼適配的呢。上面我們已經說到,retrofi中的預設引數callAdapterFactoriesExecutorCallAdapterFactory類,而callAdapterFactories中的callAdapter是通過callAdapterFactories.get()獲得的,那麼就要去這裡面找了。

@Override public @Nullable 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);
      }
    };
  }
複製程式碼

返回的CallAdapter物件就是我們需要的。其中重寫了adapt方法,也是我們要找的。它又返回了ExecutorCallbackCall物件,第一個引數就是預設執行在主執行緒中的callbackExecutor,第二個引數則是呼叫時傳進來的OkHttpCall物件。所以說我們需要返回的Call<BaseModel<ArrayList<Benefit>>>物件就是ExecutorCallbackCall物件。

依靠OkHttp3進行網路請求,轉換回撥函式執行緒到主執行緒。

call.enqueue(new Callback<BaseModel<ArrayList<Benefit>>>() {...});,終於到最後一步了,拿著上一步返回的Call<BaseModel<ArrayList<Benefit>>>物件進行非同步請求。呼叫了enqueue方法,也就是ExecutorCallbackCall中的enqueue方法。

ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
      this.callbackExecutor = callbackExecutor;
      this.delegate = delegate;
    }
    @Override public void enqueue(final Callback<T> callback) {
      checkNotNull(callback, "callback == null");
      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(ExecutorCallbackCall.this, new IOException("Canceled"));
              } else {
                callback.onResponse(ExecutorCallbackCall.this, response);
              }
            }
          });
        }
        @Override public void onFailure(Call<T> call, final Throwable t) {
          callbackExecutor.execute(new Runnable() {
            @Override public void run() {
              callback.onFailure(ExecutorCallbackCall.this, t);
            }
          });
        }
      });
    }
複製程式碼

構造方法給了出來,變數名有些轉換。callbackExecutor依然是在主執行緒中的那個callbackExecutordelegate是上一步傳進來的OkHttpCall物件。而enqueue的引數callback則是我們自己傳入的callback。

方法中首先是delegate也就是OkHttp3進行的非同步請求,在回撥的函式中,通過callbackExecutor使執行緒切換為主執行緒,再呼叫我們自己的callback中對應的函式,使我們可以再回撥中進行操作,並且是在主執行緒中。

OkHttpCall這個類就不在多講了,主要作用是retrofit通過OkHttp3進行請求的一個轉換。

結語

第一次分析原始碼,雖然有視訊的幫助,但許多類之間跳來跳去的讓人眼花繚亂,很艱難地順了下來。有了這樣一個大概的瞭解,接下來就要分析其設計模式,欣賞其漂亮的解耦套路。當然,如果你已經有了上面的瞭解,接下來看Retrofit分析-經典設計模式案例就會簡單明瞭。

相關文章