Retrofit精髓領悟進階篇

Retrofit源碼解析


簡介

在這裏插入圖片描述
從事移動端開發的人應該都知道Retrofit庫,作爲一個第三方網絡封裝庫,許多App都會用到它,他提供了註解方式的網絡接口定義,自定義請求響應參數轉換等等功能,大大提高了開發效率;今天我們嘗試着來看看它背後的邏輯好在哪裏


Retrofit基本使用

public interface NetApi {
	@POST("test/login")
	LiveData<Response> test(@Body Request request);
}
NetApi api = new Retrofit.Builder()
				.baseUrl(url)
				.addConverterFactory(CustomGsonConverterFactory.create())
				.addCallAdapterFactory(new LiveDataCallAdapterFactory())
				.build().create(NetApi.class);

以上代碼就是retrofit的基本使用了

  • 定義網絡接口
  • 配置Retrofit參數
  • 反射創建網絡接口類

這裏大致說一下代碼中的關鍵點:
其一:test方法的POST註解標明這是一個POST方法,Body註解標明request最終要封裝到http的請求體中去
其二:Retrofit.Builder需要add兩個工廠類,一個convert用於轉換http的請求響應參數,另一個CallAdapter調用適配器用於發起網絡接口的調用邏輯及相關的操作

注意:相信你也發現了addCallAdapterFactory可以添加不同的調用適配器,這裏是LiveData的,或者你曾經還用過RXjava的適配器,而且現在還有Kotlin的協程Coroutine適配器,無論哪種適配器,它都需要完成網絡調用的發起工作以及回調工作


源碼解讀


需求分析

試想,你是Retrofit的設計者,思考一下,它要完成哪些功能?
安全與快捷問題,首先我希望用戶能很方便使用庫進行開發;其次,網絡庫中轉處理App的諸多數據,不希望別人能直接訪問網絡中的數據,我希望用戶無法修改(至少有一定難度)網絡請求響應參數
對象轉換問題,app網絡請求可能會有不同的對象封裝類型,最終都需要轉換我這邊的統一請求參數對象格式,以及統一響應對象格式,這塊我定義好接口轉換,讓使用者自己去實現
除了以上問題之外,還有http請求分爲很多方法(get/post/query…),如何根據不同的方法進行參數封裝、調用等;
最後,還有其他一些基本的設計問題,面向對象設計的幾大原則,內聚、耦合、可擴展等等

解讀源碼之前,先解釋後面源碼可能會遇到的成員以及類說明:
Retrofit中:
okhttp3.Call.Factory callFactory; okhttp底層網絡調用引擎,網絡都是由它去發起調用的
List<Converter.Factory> converterFactories; 轉換器工廠類集合,需要用到的轉換器都會add到這個集合
List<CallAdapter.Factory> adapterFactories; 調用適配器工廠類集合,需要用到的適配器都會add到這個集合
類ServiceMethod<R, T> :每個網絡接口方法都會創建一個ServiceMethod,把請求和響應參數以及註解以及適配器、轉換器都會封裝到裏面去
OkHttpCall以及OkHttpClient是最終網絡處理中心

帶着以上的問題,我們就從上面Retrofit.Builder()源碼開始看看吧!


Retrofit.Builder

這是一個建造者模式的類,用於參數較多時,快速構建最終需要的Retrofit類:

==========Retrofit.java==========
public Builder() {
      this(Platform.get());
}
Builder(Platform platform) {
      this.platform = platform;
      converterFactories.add(new BuiltInConverters());
}
=======Retrofit.java=========

=======Platform.java=========
private static final Platform PLATFORM = findPlatform();
static Platform get() {
  return PLATFORM;
}
單利模式找到平臺類,
private static Platform findPlatform() {
  try {
    Class.forName("android.os.Build");
    Android設備這裏肯定不爲0,所以PLATFORM=Android
    if (Build.VERSION.SDK_INT != 0) {
      return new Android();
    }
  } catch (ClassNotFoundException ignored) {
  }
}
Android是Platform的子類
static class Android extends Platform {
 返回主線程執行的Executor
 public Executor defaultCallbackExecutor() {
   return new MainThreadExecutor();
 }
 返回一個可以在主線程回調執行的調用適配器工廠類,記住他也是個調用適配器工廠類
 CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
   if (callbackExecutor == null) throw new AssertionError();
   return new ExecutorCallAdapterFactory(callbackExecutor);
 }
 主線程的Looper,這個線程任務最終會在主線程執行
 static class MainThreadExecutor implements Executor {
   private final Handler handler = new Handler(Looper.getMainLooper());
   @Override public void execute(Runnable r) {
     handler.post(r);
   }
 }
}
============Platform.java==============

Builder的默認構造方法獲取一個Android平臺類,它可以提供在主線程執行能力,返回後賦值給Builder的platform成員;在Builder(Platform platform)構造方法內,converterFactories成員是一個ArrayList集合,存儲Converter.Factory類,這個是定義的一個抽象類,提供了java bean對象在http中的轉換標準,而BuiltInConverters是提供的默認convert轉換器,比如StringConvert、BufferConvert等等,默認的轉換器一般用不到,所以看不懂BuiltInConverters裏面的東西也沒關係,這裏看看Converter.Factory定義的接口:

public interface Converter<F, T> {
  javabean對象轉換的方法,要從F類型轉換爲T類型
  T convert(F value) throws IOException;
  內部工廠類,用於提供不同功能的Convert
  abstract class Factory {
  	生產一個轉換之前是ResponseBody類型,轉換之後類型未知的Convert,用於響應時
    public @Nullable Converter<ResponseBody, ?> responseBodyConverter(Type type,
        Annotation[] annotations, Retrofit retrofit) {
      return null;
    }
    生產一個轉換之後是RequestBody類型,轉換之前類型未知的Convert,用於請求時
    public @Nullable Converter<?, RequestBody> requestBodyConverter(Type type,
        Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
      return null;
    }
	同上
    public @Nullable Converter<?, String> stringConverter(Type type, Annotation[] annotations,
        Retrofit retrofit) {
      return null;
    }
	獲取index未知的參數類型Type
    protected static Type getParameterUpperBound(int index, ParameterizedType type) {
      return Utils.getParameterUpperBound(index, type);
    }
	獲取Type類型的class
    protected static Class<?> getRawType(Type type) {
      return Utils.getRawType(type);
    }
  }
}

從上述代碼得知,java bean對象轉換器做哪些工作了吧,實現他的接口即可,然後添加到Builder的converterFactories集合去;這裏我們就看看我們自定義GsonConvert是不是這樣的?


GsonConvert

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

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

響應轉換器
final class CustomGsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
    private final Gson gson;
    private final TypeAdapter<T> adapter;

    CustomGsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
        this.gson = gson;
        this.adapter = adapter;
    }   

    @Override
    public T convert(ResponseBody value) throws IOException {
        String response = value.string();
        HttpStatus httpStatus = gson.fromJson(response, HttpStatus.class);
        if (httpStatus.getcode != HttpStatus.success) {
            value.close();
            throw new ApiException(httpStatus.getCode(), httpStatus.getMessage(),httpStatus.getOperatorName());
        }   
        MediaType contentType = value.contentType();
        Charset charset = contentType != null ? contentType.charset(UTF_8) : UTF_8;
        InputStream inputStream = new ByteArrayInputStream(response.getBytes());
        Reader reader = new InputStreamReader(inputStream, charset);
        JsonReader jsonReader = gson.newJsonReader(reader);
        try {
            return adapter.read(jsonReader);
        } finally {
            value.close();
        }   
    }   
}

這裏我們確實是這麼做的,重寫請求響應方法,提供其轉換器;使用了Gson庫幫我們進行轉換java對象,爲了對網絡請求錯誤統一,我們在Response裏面對服務端返回的code做了判別處理,集中處理異常;回到Builder的.addConverterFactory(GsonConverterFactory.create())這裏,builder源碼:

public Builder addConverterFactory(Converter.Factory factory) {
      converterFactories.add(checkNotNull(factory, "factory == null"));
      return this;
}

把轉換器加入到了converterFactories集合中去


調用適配器CallAdapter

本篇文章前面說到了,調用適配器我們可以添加RXJava、LiveData和協程Coroutine等等不同的適配器,如果現在我們的Retrofit裏面的adapterFactories集合已經存放了多個適配器了,那麼我們在調用某一個具體網絡方法如本文開始的test方法,那他該使用哪一個適配器呢?這就需要從衆多適配器中去尋找,以接口返回值類型去匹配過濾,我們只需要定義好CallAdapter標準的返回值匹配能力即可,接口如下:

調用適配器T返回值類型  R是請求類型
public interface CallAdapter<R, T> {
  返回響應參數類型
  Type responseType();
  適配器發起網絡調用
  T adapt(Call<R> call);

  abstract class Factory {
  	生成一個CallAdapter調用適配器,完成從R到T的轉換,並且要調用網絡
    public abstract @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations,
        Retrofit retrofit);
	
	以下兩個方法就是CallAdapter提供的參數匹配工具方法,快速查找到index處的參數類型以及class
    protected static Type getParameterUpperBound(int index, ParameterizedType type) {
      return Utils.getParameterUpperBound(index, type);
    }
    protected static Class<?> getRawType(Type type) {
      return Utils.getRawType(type);
    }
  }
}

上訴相關解釋請看註釋,然後我們在實現liveData適配器,只需要處理我們支持的接口方法返回值類型即可:

這個適配器只處理LiveData返回類型的網絡接口方法
public class LiveDataCallAdapterFactory extends CallAdapter.Factory {
    @Override
    public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    	//我只能處理返回值類型爲LiveData.class的
        if(getRawType(returnType) != LiveData.class){
            return null;
        }
        returnType泛型類型,獲取第0處泛型類型
        Type type = getParameterUpperBound(0, (ParameterizedType) returnType);
        Class rawType = getRawType(type);
        如果0處類型不爲ApiResponse,就不處理,拋錯誤,也就是LiveData<ApiResponse>
        if(rawType != ApiResponse.class){
            throw new IllegalArgumentException("type must be a resource");
        }

        if(!(type instanceof ParameterizedType)){
            throw new IllegalArgumentException("resource must be paramterized");
        }
        獲取0處的參數類型,最終返回回來的是type類型即可
        type = getParameterUpperBound(0, (ParameterizedType) type);
        return new LivedataCallAdapter<Object>(type);
    }
}
真正的調用適配器,這是一個LiveData類型的
public class LivedataCallAdapter<T> implements CallAdapter<T, LiveData<ApiResponse<T>>> {
    private Type responseType;
    public LivedataCallAdapter(Type responseType) {
        this.responseType = responseType;
    }
    @Override
    public Type responseType() {
        return responseType;
    }
   adapt發起網絡調用了,參數Call實質是一個OkhttpCall
    @Override
    public LiveData<ApiResponse<T>> adapt(final Call<T> call) {
        return new LiveData<ApiResponse<T>>() {
            private AtomicBoolean stat = new AtomicBoolean(false);
            @Override
            protected void onActive() {
                if(stat.compareAndSet(false, true)){
                	請求入隊,執行網絡操作
                    call.enqueue(new Callback<T>() {
                        @Override
                        public void onResponse(Call<T> call, Response<T> response) {
                        	將返回的數據發送到liveData中去,訂閱了這個liveData的都可以收到
                            postValue(ApiResponse.Companion.<T>create(response));
                        }
                        @Override
                        public void onFailure(Call<T> call, Throwable t) {
                            postValue(ApiResponse.Companion.<T>create(t));
                        }
                    });
                }
            }
        };
    }
}

至此,CallAdapter適配器看完了;我們要記住Retrofit中有兩個工廠類集合ArrayList,一個裝轉換java-bean對象的轉換器Convert,另一個裝發起網絡調用的適配器CallAdapter;但是我們還不清楚這兩個適配器是是如何組織起來使用?如何調用哪些方法,參數封裝如何封裝,最後又返回來是如何實現的?

帶着問題,我們繼續看Retrofit.Builder.build()

public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }
      okhttp3.Call.Factory callFactory = this.callFactory;
      因爲我們沒有傳入callFactory,所以builder代替我們new一個OkHttpClient
      if (callFactory == null) {
        callFactory = new OkHttpClient();
      }
	 callbackExecutor爲null的,所以從platform獲取默認的回調執行器,這個platform
	 前文講過,defaultCallbackExecutor主要是獲取一個能回到主線程執行的能力
      Executor callbackExecutor = this.callbackExecutor;
      if (callbackExecutor == null) {
        callbackExecutor = platform.defaultCallbackExecutor();
      }

      這裏把build進來的CallAdapter添加到adapterFactories去
      List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);
      同時這裏也會添加一個默認的CallAdapter,這個默認的也能完成網絡操作工作;
      所以如果你不添加調用適配器CallAdapter也是可以的
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

      添加轉換器Convert
      List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
      return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
          callbackExecutor, validateEagerly);
    }

=============================================================================
上訴代碼會添加一個默認的調用適配器platform.defaultCallAdapterFactory(callbackExecutor),這個適配器可以自動把網絡放到子線程操作,同時網絡返回時,在返回到主線程,所以我們可以不添加其他的RXJava、LiveData等適配器,具體用法如下:

1. 定義這樣的網絡接口
@POST("xyyc/login/")
Call<LoginResponse> test(@Body LoginRequest request);
2. 調用
apiService.test(request)
  .enqueue(new Callback<LoginResponse>() {
        @Override
         public void onResponse(Call<LoginResponse> call, Response<LoginResponse> response) {
         }

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

第二部回到Callback就已經在主線程執行了,是不是很方便!而且它也支持任務取消、終止等;雖然沒有RXJava那樣花裏胡哨的功能強大,但是它簡單快捷,代碼量少,邏輯簡單

=============================================================================


Retrofit

接着上面看到,Builder就已經把Retrofit創建給配置好了,然後就是Retrofit調用Create創建我們定義的接口類;下面代碼要仔細看了,是Retrofit的核心邏輯:

參數service是我們定義的接口類class
 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, @Nullable Object[] args)
             throws Throwable {
           // If the method is a method from Object then defer to normal invocation.
           調用method的類是否爲Object,顯然不是
           if (method.getDeclaringClass() == Object.class) {
             return method.invoke(this, args);
           }
           method是否爲platform的默認方法  顯示也不是
           if (platform.isDefaultMethod(method)) {
             return platform.invokeDefaultMethod(method, service, proxy, args);
           }
           邏輯周到這裏了,根據調用方法的method從緩存中去取出
           ServiceMethod<Object, Object> serviceMethod =
               (ServiceMethod<Object, Object>) loadServiceMethod(method);
           創建一個OkHttpCall請求
           OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
           用callAdapter.adapt發起執行,這裏是不是很熟悉,對了,你沒猜錯,這就是前文
           講到的CallAdapter,他會執行adapt方法,在adapte方法發起網絡調用,將這個okHttpCall
           入隊enqueue到網絡任務中去
           return serviceMethod.callAdapter.adapt(okHttpCall);
         }
       });
 }

這裏我們看到發起調用是serviceMethod裏面的callAdapter裏面的adapt發起調用的,但是callAdapter裝載到Retrofit集合裏面去,可能有好幾個,他怎麼知道調用哪個呢?不慌,我們看看serviceMethod是如何來的,看以下代碼:

這個就是快速緩存接口類的所有方法
private void eagerlyValidateMethods(Class<?> service) {
    Platform platform = Platform.get();
    遍歷接口類中所有的method
    for (Method method : service.getDeclaredMethods()) {
      默認都是false,所以都會執行loadServiceMethod
      if (!platform.isDefaultMethod(method)) {
        loadServiceMethod(method);
      }
    }
  }
loadServiceMethod查找方法也會調用它,serviceMethodCache類型是ConcurrentHashMap,
key爲method,value爲ServiceMethod;爲了保證併發效率,採用了分段鎖
ServiceMethod<?, ?> loadServiceMethod(Method method) {
	先去serviceMethodCache集合中找,看是否找得到
  ServiceMethod<?, ?> result = serviceMethodCache.get(method);
  if (result != null) return result;
  雙檢測機制,保證查找效率
  synchronized (serviceMethodCache) {
    result = serviceMethodCache.get(method);
    if (result == null) {
      創建一個ServiceMethod並添加到緩存中去
      result = new ServiceMethod.Builder<>(this, method).build();
      serviceMethodCache.put(method, result);
    }
  }
  return result;
}

因爲後面的邏輯很多都和ServiceMethod有關,所以我們必須去ServiceMethod裏面看看,它是如何被創建的?


ServiceMethod

首先從new ServiceMethod.Builder<>(this, method).build()這裏開始:

Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit;
      this.method = method;
      獲取這個method的所有註解
      this.methodAnnotations = method.getAnnotations();
      獲取method的參數類型Type
      this.parameterTypes = method.getGenericParameterTypes();
      獲取method參數的註解
      this.parameterAnnotationsArray = method.getParameterAnnotations();
}
構建一個serviceMethod
public ServiceMethod build() {
	得到這個method的調用適配器,看到沒?這裏已經綁定好了,每個ServiceMethod從
	一開始就綁定了調用適配器CallAdapter
      callAdapter = createCallAdapter();
      確定method返回類型
      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?");
      }
      同上也一開始就綁定好轉換器
      responseConverter = createResponseConverter();
	  依次遍歷每個註解
      for (Annotation annotation : methodAnnotations) {
      	這裏主要是根據註解解析註解中的參數,比如@POST會有參數路徑,以及body參數
      	設置好相對訪問路徑,參數等
        parseMethodAnnotation(annotation);
      }

      int parameterCount = parameterAnnotationsArray.length;
      parameterHandlers = new ParameterHandler<?>[parameterCount];
      for (int p = 0; p < parameterCount; p++) {
        Type parameterType = parameterTypes[p];
        if (Utils.hasUnresolvableType(parameterType)) {
        獲取每個參數的註解
        Annotation[] parameterAnnotations = parameterAnnotationsArray[p];
		註解轉換器,根據參數中不同的註解產生不同的處理句柄
        parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);
      }
      return new ServiceMethod<>(this);
    }

上面的邏輯業務都在註釋裏面了,邏輯比較簡單,但是仔細一看,會發現三個問題

(1)method怎麼知道自己綁定哪個調用適配器CallAdapter ?
(2)同上,使用哪個轉換器Convert ?
(3)參數如何封裝進去,如POST方式的Body的請求是一個java對象,怎麼弄到http的body ?


綁定調用適配器

話不多說,看源碼:

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);
  }
  if (returnType == void.class) {
    throw methodError("Service methods cannot return void.");
  }
  獲取方法method的註解
  Annotation[] annotations = method.getAnnotations();
  try {
    從Retrofit的CallAdapter集合中去找,注意參數類型和註解
    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);
  }
}

查看retrofit源碼

public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
    return nextCallAdapter(null, returnType, annotations);
}
public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
    Annotation[] annotations) {
	start爲0
  int start = adapterFactories.indexOf(skipPast) + 1;
  遍歷所有的CallAdapter
  for (int i = start, count = adapterFactories.size(); i < count; i++) {
   調用每個CallAdapter的get方法,get方法返回他的適配器,這個get方法
   傳入了method的返回值類型和方法註解
    CallAdapter<?, ?> adapter = adapterFactories.get(i).get(returnType, annotations, this);
    if (adapter != null) {
      return adapter;
    }
  }
}

再次回頭看看CallAdapter的get方法,這裏我們用自定義LiveData的

@Override
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
	首先就判斷返回值類型是不是一個LiveData的class,如果不是就沒必要執行了
	這個returnType就是我們定義網絡接口方法的返回值類型
    if(getRawType(returnType) != LiveData.class){
        return null;
    }
    查看returnType的0處返回值類型,理解爲泛型
    Type type = getParameterUpperBound(0, (ParameterizedType) returnType);
    Class rawType = getRawType(type);
    泛型必須是APIResponse,後面類似
    if(rawType != ApiResponse.class){
        throw new IllegalArgumentException("type must be a resource");
    }

    if(!(type instanceof ParameterizedType)){
        throw new IllegalArgumentException("resource must be paramterized");
    }
    type = getParameterUpperBound(0, (ParameterizedType) type);
    就創建了LivedataCallAdapter調用適配器
    return new LivedataCallAdapter<Object>(type);
}

至此,調用適配器找到了;
做一個method綁定CallAdapter小結:
1 確定自己method的返回值returnType和註解annotation
2 遍歷retrofit的所有CallAdapterFactory工廠類,並且依次工廠類的get方法
3 get方法中,會根據returnType是否支持,支持就返回創建調用適配器,不支持返回null
4 找到adapter就綁定到ServiceMethod中去

這裏有兩個個疑問如下:(1)Retrofit會爲每個Method方法緩存到Map (2)每個Method都會綁定自己的適配器和轉換器,並且大部分適配器和轉換器都是new新的出來,而不是每個Method共用;結合上面兩點,而網絡訪問接口類一般生命週期較長,這樣會導致app的內存消耗較大,Retrofit爲什麼不根據網絡接口來存儲method,當某一個網絡接口類死亡後,就銷燬它裏面提供的所有method,否則這個Retrofit的method緩存就會越來越大
當然他這個也可以解決,使用多個Retrofit,這樣把Retrofit生命週期完結後,他這部分的緩存都會被清理掉,不過這個有點不合適


綁定轉換器

綁定轉換器和上面綁定調用適配器原理差不多,感興趣可以自行去查看源碼,這裏就不繼續闡述了


參數封裝

參數封裝也是一大核心點,比如GET我們可能傳遞的是動態路徑,POST需要封裝到Body中去,這裏以Body封裝爲例,在ServiceMethod.Builder的build方法中有一句:parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations),就是爲參數類型爲parameterType,參數註解爲parameterAnnotations提供一個參數處理句柄:

private ParameterHandler<?> parseParameter(
        int p, Type parameterType, Annotation[] annotations) {
      ParameterHandler<?> result = null;
      因爲註解annotation裏面可能還有註解參數等
      for (Annotation annotation : annotations) {
        ParameterHandler<?> annotationAction = parseParameterAnnotation(
            p, parameterType, annotations, annotation);
		.......
      return result;
    }
private ParameterHandler<?> parseParameterAnnotation(
        int p, Type type, Annotation[] annotations, Annotation annotation) {
      if (annotation instanceof Url) {
      } else if (annotation instanceof Path) {
      } else if (annotation instanceof Query) {
      } else if (annotation instanceof QueryName) {
      } else if (annotation instanceof QueryMap) {
      } else if (annotation instanceof Header) {
      } else if (annotation instanceof HeaderMap) {
      } else if (annotation instanceof Field) {
      } else if (annotation instanceof FieldMap) {
      } else if (annotation instanceof Part) {
      } else if (annotation instanceof PartMap) {
      } else if (annotation instanceof Body) {
        if (isFormEncoded || isMultipart) {
          throw parameterError(p,
              "@Body parameters cannot be used with form or multi-part encoding.");
        }
        if (gotBody) {
          throw parameterError(p, "Multiple @Body method annotations found.");
        }

        Converter<?, RequestBody> converter;
        try {
          哈哈,終於找到了,找到轉換器,這裏會從retrfoit的轉換工廠集合去找,也是根據type
          去找,找到了也要new一個綁定
          converter = retrofit.requestBodyConverter(type, annotations, methodAnnotations);
        } catch (RuntimeException e) {
          // Wide exception range because factories are user code.
          throw parameterError(e, p, "Unable to create @Body converter for %s", type);
        }
        gotBody = true;
        return new ParameterHandler.Body<>(converter);
      }
      return null; // Not a Retrofit annotation.
    }

至此,ServiceMethod就構建好了,請求轉換器/響應轉換器Convert,調用適配器;

再次回到Retrofit的create方法中去,裏面用到了動態代理調用方法,在動態代理的方法調用最後幾句

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

發起調用

從上面代碼可知,執行了serviceMethod.callAdapter適配器的adapt方法,這個adapt方法以我們自定義LiveData看看

	@Override
    public LiveData<ApiResponse<T>> adapt(final Call<T> call) {
        return new LiveData<ApiResponse<T>>() {
            private AtomicBoolean stat = new AtomicBoolean(false);
            @Override
            protected void onActive() {
                if(stat.compareAndSet(false, true)){
                	重點看這裏,就是講Call入隊,這個Call是一個OkHttpCall
                    call.enqueue(new Callback<T>() {
                        @Override
                        public void onResponse(Call<T> call, Response<T> response) {
                            postValue(ApiResponse.Companion.<T>create(response));
                        }
                        @Override
                        public void onFailure(Call<T> call, Throwable t) {
                            postValue(ApiResponse.Companion.<T>create(t));
                        }
                    });
                }
            }
        };
    }

從OkHttpCall看看enqueue:

@Override public void enqueue(final Callback<T> callback) {
    checkNotNull(callback, "callback == null");

    okhttp3.Call call;
    Throwable failure;

    synchronized (this) {
      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;

      call = rawCall;
      failure = creationFailure;
      if (call == null && failure == null) {
        try {
        	createRawCall實質就是retrofit中OkHttpClien創建的RealCall
          call = rawCall = createRawCall();
        } catch (Throwable t) {
          failure = creationFailure = t;
        }
      }
    }

    if (failure != null) {
      callback.onFailure(this, failure);
      return;
    }

    if (canceled) {
      call.cancel();
    }
	將請求入隊,並配置okhttp3.Callback()回調接口,這裏實質就是Okhttp的用法了
	用過okhttp的都知道,這個就是okhttp的調用方式
    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
          throws IOException {
        Response<T> response;
        try {
          網絡響應java bean對象換磚
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          callFailure(e);
          return;
        }
        callSuccess(response);
      }

      @Override public void onFailure(okhttp3.Call call, IOException e) {
        
      }

      private void callFailure(Throwable e) {
        ....
      }

      private void callSuccess(Response<T> response) {
       .....
      }
    });
  }
Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
	......
	調用ServiceMethod的toResponse,進行對象轉換,實質就是ServiceMethod裏面的
	responseConvert,前文已講過
 	T body = serviceMethod.toResponse(catchingBody);
	......
}

小結:
上面代碼可以看出okhttp的調用邏輯,以及網絡請求響應response的對象轉換;但是還看不見請求參數是如何封裝的?首先上面call會調用createRawCall創建,這個函數裏面

private okhttp3.Call createRawCall() throws IOException {
	封裝了請求參數,會使用到ServiceMethod之前綁定的RequestConvert
    Request request = serviceMethod.toRequest(args);
    callFactory是一個OkhttpClient,newCall是一個RealCall類型
    okhttp3.Call call = serviceMethod.callFactory.newCall(request);
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
}

最後就是把這個請求入隊,交給okHttp底層的任務去了,OKHttp模塊沒有做過深入的研究,大致就是有幾個任務隊列,每個網絡請求都會加到任務隊列裏面去,線程池的方式去這些任務隊列中去取任務執行

至此,Retrofit的源碼大致就分析完結!


總結

在這裏插入圖片描述
Retrofit主要的東西就是上面這些東西,轉換器Convert提供java對象在Http傳遞過程中的轉換能力,調用適配器CallAdapter提供了根據不能類型的網絡訪問方法不同的調用邏輯,而組織他們的關係都是ServiceMethod來實現的,根據每個網絡接口方法的返回值類型、註解、請求參數格式等,保存到ServiceMethod中去,然後ServiceMethod根據這些信息從Retrofit的兩個工廠類集合去搜尋屬於自己的轉換器Convert和調用適配器,然後封裝請求參數、發起網絡調用以及最後的網絡返回響應數據轉換等工作。

Retrofit的設計方式,在於把核心的工作都用統一的標準(接口)定義好,自己實現一些默認的能力,更多的則是由用戶自己去實現,使Retrofit開發更自由化,根據自己的使用場景;所以,我們做設計時,不是把重要的內容自己實現,而是爲重要內容定義標準,由使用者自己去實現

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