常用輪子之Retrofit基本使用及原理

jasonhww發表於2018-12-05

不忘初心 砥礪前行, Tomorrow Is Another Day !

相關文章

本文概要:

  1. 引言
    • okHttp、urlConnection、httpClient、volley、retrofit之間的關係
  2. Retrofit的使用-簡單介紹
  3. Retrofit的原理-重點分析

一. 引言

在分析Retrofit之前,我們需要對基本的網路以及網路框架有個綜合的對比認識.

okHttp、urlConnection、httpClient、volley、retrofit之間的關係

  • okHttp、urlConnection、httpClient 這三者是底層的網路庫.
  • volley與retrofit屬於更高層的網路庫
  • volley對urlConnection和httpClient的進行了封裝,由於volley良好的擴充套件性,它還能配置使用okHttp作為底層庫
  • retrofit則是在okhttp的基礎上封裝了執行緒的切換及網路資料解析

二. Retrofit的使用

Retrofit的使用比較簡單,基本是就是通過三類註解方法註解,引數註解. 這裡就不再講述了,找了一篇個人感覺比較全面的文章你真的會用Retrofit2嗎?Retrofit2完全教程進行參考學習.我們重點進行原理分析.

三. 基本原理

3.1 Retrofit的建立

原始碼版本2.5.0
Retrofit是通過動態代理的方式建立各種網路介面的代理.

關於動態代理,筆者另起了篇講述.設計模式之代理模式如有興趣的可以看一下.最後我們迴歸正題.

使用過程一般都是通過建造者的build方法來構建Retorfit物件.我們就從Retorfit物件的建立開始,

使用示例-1

mRetrofit = new Retrofit.Builder()
                .baseUrl(baseUrl)
                .addConverterFactory(GsonConverterFactory.create())
                .callFactory(okHttpClient)
                .build();
複製程式碼

直接看build方法的對應原始碼

public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }

      // 1.初始化callFactory,如果未設定則預設設定一個OkHttpClient

      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }
      // 2.初始化回撥執行器,用於回撥到UI執行緒
      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      // 3.初始化介面卡工廠集合,新增一個預設介面卡工廠(ExecutorCallAdapterFactory)
      //用於適配返回型別,如預設支援的Call<T>
      List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
      callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));

      // 4.初始化轉換器集合
      //用於網路響應的解析
      List<Converter.Factory> converterFactories = new ArrayList<>(
          1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());

      //...省略部分程式碼

      return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
    }
  
複製程式碼

在build方法裡,初始化時會通過platform物件新增一些預設的介面卡,轉換器,以及回撥執行器.我們看具體的新增操作.

對應原始碼

  static class Android extends Platform {
    @IgnoreJRERequirement // Guarded by API check.
    @Override boolean isDefaultMethod(Method method) {
      if (Build.VERSION.SDK_INT < 24) {
        return false;
      }
      return method.isDefault();
    }
    //新增預設回撥執行器
    @Override public Executor defaultCallbackExecutor() {
      return new MainThreadExecutor();
    }

    //新增預設的介面卡工廠(ExecutorCallAdapterFactory)
    @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 int defaultCallAdapterFactoriesSize() {
      return Build.VERSION.SDK_INT >= 24 ? 2 : 1;
    }

    //新增預設的轉換器工廠
    @Override List<? extends Converter.Factory> defaultConverterFactories() {
      return Build.VERSION.SDK_INT >= 24
          ? singletonList(OptionalConverterFactory.INSTANCE)
          : Collections.<Converter.Factory>emptyList();
    }

    @Override int defaultConverterFactoriesSize() {
      return Build.VERSION.SDK_INT >= 24 ? 1 : 0;
    }

    static class MainThreadExecutor implements Executor {
      //繫結主執行緒的looper
      private final Handler handler = new Handler(Looper.getMainLooper());

      @Override public void execute(Runnable r) {
        //回撥到主執行緒執行
        handler.post(r);
      }
    }
  }
複製程式碼

至此Retorfit物件建立過程就分析完了,做一個小結.

對於Retorfit的建立,進行一系列的初始化操作.

  • 初始化callFactory
  • 初始化"三器"
    • 回撥執行器,用於回撥到UI執行緒
    • 介面卡工廠,用於適配返回的型別
    • 轉換器工廠,用於解析網路響應

3.2 Call的建立

Call的建立過程實質是網路介面的實現過程

Rerofit物件建立完成後,呼叫create方法時來實現我們定義的網路介面ApiService,採用的就是動態代理.

使用示例-2

定義的網路介面類
public interface ApiService{
    @GET
    Observable<IDocViewBean> oaFileUpload(@Url String url,
                                          @Query("token") String token,
                                          @Query("mode") String mode,
                                          @Query("url") String urlFile);
}

ApiService apiService = retrofit.create(ApiService.class);
複製程式碼

對應原始碼

public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    /*
     * 返回了網路介面的代理物件
     */
    //當我們呼叫代理物件的介面方法時,會觸發InvocationHandler的invoke方法.
    //如呼叫apiService.oaFileUpload(...)時,會完成oaFileUpload的實現,並返回一個Call<T>
    //如呼叫apiService.oaFileUpload(...)時,會完成oaFileUpload的實現,並返回一個Call<T>
    //如呼叫apiService.oaFileUpload(...)時,會完成oaFileUpload的實現,並返回一個Call<T>
    //重要話說三遍,這一句理解了後面就容易了.
    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);
            }
            //載入網路介面的方法,如載入oaFileUpload方法並呼叫.
             /**
             * 第一大步:先分析loadServiceMethod(method)獲取到網路介面方法物件(ServiceMethod物件)
             * 第二大步:呼叫ServiceMethod的invoke.
             */
            return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
          }
        });
  }

    
複製程式碼

3.2.1 第一大步

接著我們看loadServiceMethod的具體實現

對應原始碼

ServiceMethod<?> loadServiceMethod(Method method) {
    //先從快取中獲取.
    ServiceMethod<?> result = serviceMethodCache.get(method);
    //有快取,直接返回.
    if (result != null) return result;

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        //無快取,對網路介面的方法進行解析,封裝成ServiceMethod並新增進快取.
        result = ServiceMethod.parseAnnotations(this, method);
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }
複製程式碼

對網路介面的方法進行解析呼叫的了ServiceMethod.parseAnnotations,實際呼叫的是它的子類HttpServiceMethod.parseAnnotations方法.這裡我們直接看子類的方法.

對應原始碼

//HttpServiceMethod.java
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {
    //建立CallAdapter
    CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method);
    Type responseType = callAdapter.responseType();
    if (responseType == Response.class || responseType == okhttp3.Response.class) {
      throw methodError(method, "'"
          + Utils.getRawType(responseType).getName()
          + "' is not a valid response body type. Did you mean ResponseBody?");
    }
    if (requestFactory.httpMethod.equals("HEAD") && !Void.class.equals(responseType)) {
      throw methodError(method, "HEAD method must use Void as response type.");
    }
    //建立轉換器
    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();
    try {
      //noinspection unchecked
      //裡面通過retrofit物件的callAdapter方法獲取CallAdapter物件
      return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);
    } catch (RuntimeException e) { // Wide exception range because factories are user code.
      throw methodError(method, e, "Unable to create call adapter for %s", returnType);
    }
  }
複製程式碼

createCallAdapter方法呼叫了retrofit物件的callAdapter方法獲取CallAdapter物件.接著看retrofit的CallAdapter方法.

對應原始碼

//retrofit.java
public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
    //再接著往下看
    return nextCallAdapter(null, returnType, annotations);
  }
  
  public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
      Annotation[] annotations) {
    checkNotNull(returnType, "returnType == null");
    checkNotNull(annotations, "annotations == null");

    int start = callAdapterFactories.indexOf(skipPast) + 1;
    //先遍歷callAdapterFactories集合
    for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
      //再通過網路介面的返回型別(returnType)來獲取介面卡工廠(CallAdapter.Factory),最終獲取到CallAdapter物件.
      //再通過網路介面的返回型別(returnType)來獲取介面卡工廠(CallAdapter.Factory),最終獲取到CallAdapter物件.
      //再通過網路介面的返回型別(returnType)來獲取介面卡工廠(CallAdapter.Factory),最終獲取到CallAdapter物件.
      //重要的話說三遍.
      //由於之前在Retrofit物件建立階段時,有預設設定CallAdapter.Factory為ExecutorCallAdapterFactory,那麼遍歷時就會獲取到它.
      //最後一起看下ExecutorCallAdapterFactory的get方法.
      CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
      if (adapter != null) {
        return adapter;
      }
    }
    
複製程式碼
//ExecutorCallAdapterFactory.java
@Override
  public @Nullable CallAdapter<?, ?> get(
      Type returnType, Annotation[] annotations, Retrofit retrofit) {
    //檢測返回型別是不是CALL
    if (getRawType(returnType) != Call.class) {
      return null;
    }
    final Type responseType = Utils.getCallResponseType(returnType);
    //返回一個CallAdapter物件,終於找到源頭了,不容易啊.至此第一大步到此為止
    return new CallAdapter<Object, Call<?>>() {
      @Override
      public Type responseType() {
        return responseType;
      }


     //...省略部分程式碼
    };
  }
複製程式碼

3.2.2 第二大步

呼叫ServiceMethod實際呼叫了HttpServiceMethod的invoke方法,我們直接看這個方法.

//HttpServiceMethod.java
@Override ReturnT invoke(Object[] args) {
    /*因為之前第一大步我們已經清楚,預設情況下callAdapter是ExecutorCallAdapterFactory介面卡工廠下返回的,
    那麼繼續往下看介面卡工廠找adapt
    */
    return callAdapter.adapt(
        new OkHttpCall<>(requestFactory, args, callFactory, responseConverter));
  }
  
  //ExecutorCallAdapterFactory.java
  public @Nullable CallAdapter<?, ?> get(
      Type returnType, Annotation[] annotations, Retrofit retrofit) {
     //...省略部分程式碼

      @Override
      public Call<Object> adapt(Call<Object> call) {
        //返回的Call實際上就是ExecutorCallbackCall
        return new ExecutorCallbackCall<>(callbackExecutor, call);
      }
    };
  }
複製程式碼

Call的建立過程相對來說比較複雜,需要反覆多瀏覽幾遍,關鍵在於冷靜下來根據線索去尋找.最後對Call的建立過程做一個總結.

  • 當呼叫代理物件的網路介面方法時,會觸發InvocationHandler的invoke方法,最終返回一個Call.
  • 在invoke方法裡面主要包含兩大步驟.
    1. 第一大步,獲取到網路介面方法物件(loadServiceMethod)
      • 檢查是否有快取,有直接返回,無,重新解析生成新的物件後,快取再返回
        • 解析時獲取介面卡(CallAdapter),遍歷介面卡工廠集合,通過網路介面的返回型別(returnType)來獲取對應介面卡工廠,最終獲得介面卡(CallAdapter).
    2. 第二大步,呼叫ServiceMethod的invoke傳送請求.
      • 同樣利用獲取到的CallAdapter呼叫adapt方法,返回一個Call物件.最後傳送請求.

3.3 網路請求的傳送過程

最後我們看ExecutorCallbackCall是如何發請求和回撥的.

對應原始碼

static final class ExecutorCallbackCall<T> implements Call<T> {
    //初始化時設定的執行緒回撥執行器
    final Executor callbackExecutor;
    //為OkHttpCall
    final Call<T> delegate;

    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>() {//使用okhttpClient進行非同步請求
        //網路回撥成功
        @Override public void onResponse(Call<T> call, final Response<T> response) {
          callbackExecutor.execute(new Runnable() {
            @Override public void run() {
              if (delegate.isCanceled()) {
                
                //主執行緒回撥失敗
                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);
            }
          });
        }
      });
    }

    //...省略部分程式碼
}
複製程式碼

最後老規矩,對網路請求的傳送過程做一個小結.

通過enqueue方法,利用Okhttp傳送網路請求,網路響應成功或失敗通過回撥執行器(MainThreadExecutor)分發到主執行緒執行.


由於本人技術有限,如有錯誤的地方,麻煩大家給我提出來,本人不勝感激,大家一起學習進步.

相關文章