【原始碼SOLO】Retrofit2原始碼解析(二)

darkwh1230發表於2018-02-05

版權宣告:本文為博主原創文章,未經博主允許不得轉載
Github:github.com/Darkwh
若有錯誤或疑問歡迎小夥伴們留言評論

友情提示!!!

本人英文渣,文章中哪些單詞翻譯的不夠形象的話。。。。那你到是來打我呀O(∩_∩)O 另外本篇文章強烈建議開啟原始碼參考閱讀,否則造成強烈不適概不負責~

系列回顧

【原始碼SOLO】Retrofit2原始碼解析(一)

【原始碼SOLO】Retrofit2原始碼解析(二)

前言

上一篇為大家介紹了retrofit的使用和其中幾個比較重要的類,本篇將會順著retrofit的使用來詳細分析原始碼。本文為了節省篇幅,程式碼中都新增了中文註釋,這裡我就講一下我認為值得講解的地方,小夥伴們記得看註釋啊。那麼接下來讓我們正式開始吧!!

第一步:建立Retrofit物件

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

首先Retrofit採用建造者模式,這種模式大家應該都不陌生了,這裡就不多介紹了。

addConverterFactory和baseUrl方法均是為builder的引數成員賦值,賦值之前會有一些非空或URL格式的檢查,程式碼很簡單。讓我們直接聚焦到build方法中去:

        /**
         * Create the {@link Retrofit} instance using the configured values.
         * <p>
         * Note: If neither {@link #client} nor {@link #callFactory} is called a default {@link
         * OkHttpClient} will be created and used.
         */
        public Retrofit build() {
            if (baseUrl == null) {
                throw new IllegalStateException("Base URL required.");
            }
            okhttp3.Call.Factory callFactory = this.callFactory;
            //如果未指定callFactory,則預設建立一個OkHttpClint來使用
            if (callFactory == null) {
                callFactory = new OkHttpClient();
            }

            Executor callbackExecutor = this.callbackExecutor;
            //如果未指定callbackExecutor,則獲取預設的callbackExecutor
            if (callbackExecutor == null) {
                callbackExecutor = platform.defaultCallbackExecutor();
            }

            //做一次深度拷貝
            List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
            //此處將預設的CallAdapter放到集合末尾
            adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

            //做一次深度拷貝
            List<Converter.Factory> converterFactories =
                    new ArrayList<>(1 + this.converterFactories.size());

            //此處將預設的Converter.Factory實現新增到集合首位置
            converterFactories.add(new BuiltInConverters());
            converterFactories.addAll(this.converterFactories);
            //返回Retrofit例項
            return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
                    callbackExecutor, validateEagerly);
        }
複製程式碼

在build的時候,callFactory、callbackExecutor未做顯示指定的時候,都會採用預設的實現。而至於adapterFactories和converterFactories這兩個集合變數,則分別將內建的例項新增到末尾和首位置。

獲取預設實現的時候涉及到了Platform和BuiltInConverters這兩個類,我們來看一下這兩個類,首先來看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();
  }
  ......
}
複製程式碼

看下findPlatform方法,Android平臺自然會返回Android這樣一個Platform的子類

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

    @Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
      if (callbackExecutor == null) throw new AssertionError();
      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);
      }
    }
  }
複製程式碼

可以看到預設的CallbackExecutor為MainThreadExecutor,利用handler在回到主執行緒處理。預設的CallAdapterFactory為 ExecutorCallAdapterFactory

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

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

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

ExecutorCallAdapterFactory提供一個匿名的CallAdapter實現,該匿名物件的adapt方法會返回ExecutorCallbackCall物件

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) {
    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);
          }
        });
      }
    });
  }
  ......
  @Override public Request request() {
    return delegate.request();
  }
}
複製程式碼

靜態代理模式,呼叫目標物件,即第一篇為大家介紹過的Call介面的內建實現OkHttpCall物件,並將通過介面將成功失敗結果回撥回來。

BuiltInConverters這個類我們放到後面去講解。

第二步:建立自定義介面的代理物件

val service: TestService = retrofit.create(TestService::class.java)
複製程式碼

create方法是整個Retrofit最精髓的一段程式碼,它的作用是通過Java的動態代理方式來返回一個你定義好的介面的動態代理物件,不明白動態代理的小夥伴們可以自行百度一下,網上有很多的講解。看一下這段程式碼:

public <T> T create(final Class<T> service) {
        //檢查介面,如果目標類不是一個介面或繼承於其他的介面則丟擲異常
        Utils.validateServiceInterface(service);
        //是否提前將method物件轉換為ServiceMethod並快取
        if (validateEagerly) {
            eagerlyValidateMethods(service);
        }
        //通過Java的動態代理返回代理物件
        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, @Nullable Object[] args)
                            throws Throwable {
                        // If the method is a method from Object then defer to normal invocation.
                        // 如果該方法屬於Object的方法,則直接呼叫
                        if (method.getDeclaringClass() == Object.class) {
                            return method.invoke(this, args);
                        }
                        // Android平臺該if分支不會執行
                        if (platform.isDefaultMethod(method)) {
                            return platform.invokeDefaultMethod(method, service, proxy, args);
                        }
                        //將該方法對映為ServiceMethood
                        ServiceMethod<Object, Object> serviceMethod =
                                (ServiceMethod<Object, Object>) loadServiceMethod(method);
                        //建立OkHttpCall物件
                        OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
                        //利用CallAdapter將okHttpCall轉換為指定的型別(預設為Call<Object>)
                        return serviceMethod.callAdapter.adapt(okHttpCall);
                    }
                });
    }
複製程式碼

讓我們將這段程式碼分開講解

1.檢查自定義的介面

Utils.validateServiceInterface(service);

對傳入的自定義介面進行檢查,如果該類並非一個介面或繼承於另一個介面,則丟擲異常。

2.是否提前將自定義介面中的方法都對映為ServiceMethod並快取

      //是否提前將method物件轉換為ServiceMethod並快取
      if (validateEagerly) {
          eagerlyValidateMethods(service);
      }
複製程式碼

可以通過Retrofit.Builder的validateEagerly方法來設定是否提前對映介面方法並快取

3.建立並返回動態代理物件

//通過Java的動態代理返回代理物件
            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, @Nullable Object[] args)
                            throws Throwable {
                        // If the method is a method from Object then defer to normal invocation.
                        // 如果該方法屬於Object的方法,則直接呼叫
                        if (method.getDeclaringClass() == Object.class) {
                            return method.invoke(this, args);
                        }
                        // Android平臺該if分支不會執行
                        if (platform.isDefaultMethod(method)) {
                            return platform.invokeDefaultMethod(method, service, proxy, args);
                        }
                        //將該方法對映為ServiceMethood
                        ServiceMethod<Object, Object> serviceMethod =
                                (ServiceMethod<Object, Object>) loadServiceMethod(method);
                        //建立OkHttpCall物件
                        OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
                        //利用CallAdapter將okHttpCall轉換為指定的型別(預設為Call<Object>)
                        return serviceMethod.callAdapter.adapt(okHttpCall);
                    }
                });
複製程式碼

首先如果呼叫的方法屬於Object類的方法,例如notify,getClass等,則會直接呼叫。而第二個if在Android平臺不會執行,參考Platform中程式碼,其invokeDefaultMethod方法總會返回false,最後程式碼會執行到最下面三行。讓我們另起一個篇幅來看看這幾行程式碼到底做了神馬!!(第三步中講解)

第三步:通過動態代理物件呼叫介面方法返回Call物件

    val call: Call<MsgBean> = service.getMsg()
複製程式碼

在我們呼叫自定義介面的方法後,程式碼會進入到retrofit.create方法中的那個匿名InvocationHandler物件中的invoke方法中,一般情況下都會跳過前面兩個if分支執行最下面的三行程式碼,並返回一個Call物件(上面說過了內建的Call物件為ExecutorCallbackCall)。來讓我們一行行開始看看這幾行程式碼到底做了什麼:

    //將該方法對映為ServiceMethood
    ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method);
複製程式碼

呼叫loadServiceMethod方法並返回ServiceMethod物件,看下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.Builder來建立ServiceMethod物件
                result = new ServiceMethod.Builder<>(this, method).build();
                //快取到集合中
                serviceMethodCache.put(method, result);
            }
        }
        return result;
    }
複製程式碼

loadServiceMethod方法會做這麼幾件事,首先從快取列表中去獲取快取例項,如果未做快取(即為null)則通過ServiceMethod.Builder來建立ServiceMethod,然後快取並返回。serviceMethodCache的定義是這樣子:

private final Map<Method, ServiceMethod<?, ?>> serviceMethodCache = new ConcurrentHashMap<>();
複製程式碼

ConcurrentHashMap是一個執行緒安全且擁有很好的併發寫入能力的HashMap。

順著程式碼我們繼續分析ServiceMethod.Builder這個類,一下進入ServiceMethod分析

ServiceMethod

ServiceMethod.Builder

【原始碼SOLO】Retrofit2原始碼解析(二)
ServiceMethod.Builder擁有這麼些個屬性啊!! 再來看看構造方法:

    Builder(Retrofit retrofit, Method method) {
        this.retrofit = retrofit;
        this.method = method;
        this.methodAnnotations = method.getAnnotations();
        this.parameterTypes = method.getGenericParameterTypes();
        this.parameterAnnotationsArray = method.getParameterAnnotations();
    }
複製程式碼

ServiceMethod.Builder接收從外部傳入的Retrofit(即第一步中你配置好的Retrofit物件)和Method(即你自定義介面中你說呼叫的那個方法的Method物件)兩個引數,並通過method的getAnnotations、getGenericParameterTypes、getParameterAnnotations來分別為其成員屬性初始化賦值。

繼續看build方法:

        public ServiceMethod build() {
            //這裡建立CallAdapter
            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?");
            }
            //這裡建立Converter
            responseConverter = createResponseConverter();

            for (Annotation annotation : methodAnnotations) {
                //解析校驗方法中的註解
                parseMethodAnnotation(annotation);
            }

            ......

            return new ServiceMethod<>(this);
        }
複製程式碼

build方法中會建立CallAdapter和Converter例項(通過Retrofit中指定的CallFactory和ConverterFactory),校驗方法上的註解和方法引數及引數註解。校驗邏輯這裡就跳過了,不難但是多,我們來重點看下CallAdapter和Converter的建立。

先看CallAdapter的建立

CallAdapter的建立

        private CallAdapter<T, R> createCallAdapter() {
            Type returnType = method.getGenericReturnType();
            //返回值校驗
            if (Utils.hasUnresolvableType(returnType)) {
                throw methodError(
                        "Method return type must not include a type variable or wildcard: %s", returnType);
            }
            //返回值型別為Void丟擲異常
            if (returnType == void.class) {
                throw methodError("Service methods cannot return void.");
            }
            Annotation[] annotations = method.getAnnotations();
            try {
                //看這裡!!看這裡!!看這裡!!
                return (CallAdapter<T, R>) 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);
            }
        }
複製程式碼

createCallAdapter中首先對方法的返回值進行校驗,首先校驗是否有無法處理的型別:

    //檢查是否含有無法處理的型別
    static boolean hasUnresolvableType(Type type) {
        if (type instanceof Class<?>) {
            return false;
        }
        //判斷返回值是否是泛型引數型別
        if (type instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) type;
            for (Type typeArgument : parameterizedType.getActualTypeArguments()) {
                //此處遞迴檢查
                if (hasUnresolvableType(typeArgument)) {
                    return true;
                }
            }
            return false;
        }
        if (type instanceof GenericArrayType) {
            return hasUnresolvableType(((GenericArrayType) type).getGenericComponentType());
        }
        //型別引數
        if (type instanceof TypeVariable) {
            return true;
        }
        //通配型別
        if (type instanceof WildcardType) {
            return true;
        }
        String className = type == null ? "null" : type.getClass().getName();
        throw new IllegalArgumentException("Expected a Class, ParameterizedType, or "
                + "GenericArrayType, but <" + type + "> is of type " + className);
    }
複製程式碼

校驗邏輯參考註釋,hasUnresolvableType是一個遞迴方法,用於泛型層次比較深的情況。

其次返回值為void(無返回值)時也會丟擲異常,經過以上兩個檢查後,最終呼叫retrofit.callAdapter(returnType, annotations),跟一下:

【原始碼SOLO】Retrofit2原始碼解析(二)
繼續跟:

    public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
                                             Annotation[] annotations) {
        checkNotNull(returnType, "returnType == null");
        checkNotNull(annotations, "annotations == null");
        
        //呼叫到這裡時skipPast為null,因此start的值為0
        int start = adapterFactories.indexOf(skipPast) + 1;
        //遍歷adapterFactories集合並呼叫get方法,如果不為null則返回
        for (int i = start, count = adapterFactories.size(); i < count; i++) {
            CallAdapter<?, ?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
            if (adapter != null) {
                return adapter;
            }
        }
        ......
        throw new IllegalArgumentException(builder.toString());
    }
複製程式碼

nextCallAdapter會從adapterFactories的首位開始遍歷並呼叫get方法,當找到能夠處理當前呼叫方法的CallAdapter後便停止遍歷並返回該CallAdapter,如果未找到合適的CallAdapter(包括內建的CallAdapter),則丟擲異常。

再看Converter的建立

Converter的建立

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

直接看註釋那裡,繼續跟!:

【原始碼SOLO】Retrofit2原始碼解析(二)
別停!再跟!:

    public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
            @Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
        checkNotNull(type, "type == null");
        checkNotNull(annotations, "annotations == null");
        //skipPast此時也為null,start的值為0
        int start = converterFactories.indexOf(skipPast) + 1;
        //遍歷converterFactories,找到合適的(不為null)則返回
        for (int i = start, count = converterFactories.size(); i < count; i++) {
            Converter<ResponseBody, ?> converter =
                    converterFactories.get(i).responseBodyConverter(type, annotations, this);
            if (converter != null) {
                //noinspection unchecked
                return (Converter<ResponseBody, T>) converter;
            }
        }
        ......
        throw new IllegalArgumentException(builder.toString());
    }
複製程式碼

和nextCallAdapter方法很相似,從converterFactories中遍歷尋找合適的Converter,一旦找到則終止迴圈並返回,如果找不到合適的Converter則丟擲異常。在Retrofit.Builder的build方法中有提到build的時候converterFactories會被新增一個內建的Converter到首位置。這個Converter是BuiltInConverters,現在讓我們來看一下BuiltInConverters這個類的定義:

final class BuiltInConverters extends Converter.Factory {
    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
                                                            Retrofit retrofit) {
        //如果返回結果型別泛型引數為ResponseBody
        if (type == ResponseBody.class) {
            return Utils.isAnnotationPresent(annotations, Streaming.class)
                    ? StreamingResponseBodyConverter.INSTANCE
                    : BufferingResponseBodyConverter.INSTANCE;
        }
        //如果返回結果型別泛型引數為Void
        if (type == Void.class) {
            return VoidResponseBodyConverter.INSTANCE;
        }
        return null;
    }
    
    ......
}
複製程式碼

這裡只給大家貼出了responseBodyConverter這個方法的程式碼,可以看到當自定義介面中的方法的返回值泛型引數型別為ResponseBody時,如果有Streaming註解修飾則返回StreamingResponseBodyConverter,否則返回BufferingResponseBodyConverter;如果返回值泛型引數型別為Void,則返回VoidResponseBodyConverter;上面都不滿足則返回null。

上面是對

ServiceMethod<Object, Object> serviceMethod = (ServiceMethod<Object, Object>) loadServiceMethod(method);
複製程式碼

這一行程式碼的跟蹤和分析,現在回到invoke方法中最下面的部分繼續看剩餘兩行程式碼:

    //建立OkHttpCall物件
    OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
複製程式碼

建立OkHttpCall物件

    //利用CallAdapter將okHttpCall轉換為指定的型別(預設為Call<Object>)
    return serviceMethod.callAdapter.adapt(okHttpCall);
複製程式碼

利用serviceMethod中的callAdapter的adapt方法將內建的OkHttpCall轉換為指定的型別

第四步:通過Call物件發起請求

        call.enqueue(object : Callback<MsgBean> {
            override fun onFailure(call: Call<MsgBean>?, t: Throwable?) {
                Log.i("wh", "onFailure")
            }

            override fun onResponse(call: Call<MsgBean>?, response: Response<MsgBean>?) {
                Log.i("wh", "onResponse")
            }

        })
複製程式碼

使用內建的Call物件(ExecutorCallbackCall)發起請求,前面有將,這裡就不在複述了。

至此本篇部落格就結束了,寫的不太好,希望能讓各位看官有所收穫,或許你在閱讀Retrofit原始碼的時候幫助到你

相關文章