1. 程式人生 > >Android Retrofit原始碼分析

Android Retrofit原始碼分析

作為我最愛的一個網路請求庫,怎麼不來搞搞它呢?

其實Retrofit是OKHTTP的一個封裝,它的網路請求還是交給OKHTTP來做的。

我們一般情況下,會這樣來做例項化Retrofit:

 Retrofit client = new Retrofit.Builder()
                .baseUrl("xxxxx")
                .addConverterFactory(GsonConverterFactory.create())
                .build();

這裡使用的是建造者模式,什麼是建造者模式呢?將一個複雜物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示。
具體可以參考:

https://www.jianshu.com/p/be290ccea05a

然後我們來看看我們的Retrofit:
首先建立了一個Retrofit.Builder物件,該類是Retrofit的靜態內部類:

 public static final class Builder {
    private final Platform platform;
    
    //網路請求器的工廠,用來產生網路請求其(Call)
    private @Nullable okhttp3.Call.Factory callFactory;
    
    //網路請求的url地址
    private HttpUrl baseUrl;
//資料轉換工廠 private final List<Converter.Factory> converterFactories = new ArrayList<>(); //網路請求介面卡工廠 private final List<CallAdapter.Factory> adapterFactories = new ArrayList<>(); //回撥方法執行器 private @Nullable 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() { //Platform.get()返回一個Platform物件,指定執行平臺 this(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) {
    }
    return new Platform();
  }

對於我們安卓來說,肯定是返回Android例項了。

然後讓converterFactories添加了一個BuiltInConverters物件,它是一個內建的轉換工廠,它禁止重寫它的任何方法,但是這樣可以保證當使用converters的正確行為。

Builder物件構建好了後,呼叫了baseUrl方法:

public Builder baseUrl(String baseUrl) {
		//檢查baseUrl不為null
      checkNotNull(baseUrl, "baseUrl == null");
      //將它轉換為一個HttpUrl物件
      //在這裡會檢查baseUrl的正確性
      HttpUrl httpUrl = HttpUrl.parse(baseUrl);
      if (httpUrl == null) {
        throw new IllegalArgumentException("Illegal URL: " + baseUrl);
      }
      return baseUrl(httpUrl);
    }

繼續:

public Builder baseUrl(HttpUrl baseUrl) {
      checkNotNull(baseUrl, "baseUrl == null");
      //把URL引數分割成幾個路徑碎片
      List<String> pathSegments = baseUrl.pathSegments();
      //檢測最後一個碎片以檢查URL引數是不是以/結尾
      //不是就丟擲異常。
      if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
        throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
      }
      //給baseUrl賦值
      this.baseUrl = baseUrl;
      return this;
    }

新增完baseUrl後,我們就會新增資料轉換工廠:addConverterFactory(GsonConverterFactory.create())

首先我們看一下GsonConverterFactory的建立:

 public static GsonConverterFactory create() {
    return create(new Gson());
  }
  
  @SuppressWarnings("ConstantConditions") // Guarding public API nullability.
  public static GsonConverterFactory create(Gson gson) {
    if (gson == null) throw new NullPointerException("gson == null");
    return new GsonConverterFactory(gson);
  }
 private GsonConverterFactory(Gson gson) {
    this.gson = gson;
  }

可以看到,它建立了一個Gson物件,將Gson物件封裝起來了。

/** Add converter factory for serialization and deserialization of objects. */
    public Builder addConverterFactory(Converter.Factory factory) {
      converterFactories.add(checkNotNull(factory, "factory == null"));
      return this;
    }

上面就是給converterFactories添加了一個factory。

接下來,呼叫build方法,建造Retrofit物件:

public Retrofit build() {
	
	//必須要有baseUrl
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }
      //網路請求執行器
	//如果沒有callFactory,預設使用OKHttpClient
      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));

		//在Builder構造的時候,添加了一個BuildInConverters()
		//資料轉換工廠集合
      // Make a defensive copy of the converters.
      List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
		
		//建立Retrofit物件
      return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
          callbackExecutor, validateEagerly);
    }

接下來,我們來看看Retrofit的構造:

public final class Retrofit {
// 網路請求配置物件(對網路請求介面中方法註解進行解析後得到的物件)
  // 作用:儲存網路請求相關的配置,如網路請求的方法、
  //資料轉換器、網路請求介面卡、網路請求工廠、基地址等
  private final Map<Method, ServiceMethod<?, ?>> serviceMethodCache = new ConcurrentHashMap<>();

  final okhttp3.Call.Factory callFactory;
  final HttpUrl baseUrl;
  final List<Converter.Factory> converterFactories;
  final List<CallAdapter.Factory> adapterFactories;
  final @Nullable Executor callbackExecutor;
  final boolean validateEagerly;

  Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,
      List<Converter.Factory> converterFactories, List<CallAdapter.Factory> adapterFactories,
      @Nullable Executor callbackExecutor, boolean validateEagerly) {
    this.callFactory = callFactory;
    this.baseUrl = baseUrl;
    //只可以讀,不能寫
    this.converterFactories = unmodifiableList(converterFactories); // Defensive copy at call site.
     //只可以讀,不能寫
    this.adapterFactories = unmodifiableList(adapterFactories); // Defensive copy at call site.
    this.callbackExecutor = callbackExecutor;
    this.validateEagerly = validateEagerly;
  }
... ...
}

可以看到,Retrofit中的成員變數和Builder中的差不多,在構造Retrofit物件的時候,進行賦值。

當我們構造好了Retrofit後,我們會像這樣進行網路請求,以get請求為例:

public interface GetService {

    @GET("book/{id}")
    Call<String> getRequest(@Path("id") int id);

}

 GetService service = client.create(GetService.class);
        Call<String> call = service.getRequest(1);
        call.enqueue(new Callback<String>() {
            @Override
            public void onResponse(Call<String> call, Response<String> response) {
                
            }

            @Override
            public void onFailure(Call<String> call, Throwable t) {

            }
        });

首先我們呼叫create方法得到GetService 物件:
在這是, Retrofit是通過外觀模式&代理模式,使用create方法建立網路請求介面例項:

public <T> T create(final Class<T> service) {
	//驗證service是interface
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
    //判斷是否需要提前驗證
    //預設為false
      eagerlyValidateMethods(service);
    }
    //建立網路請求介面的動態代理物件,即通過動態代理建立網路請求介面例項
    //該動態代理主要是為了拿到網路請求介面例項上的所有註解
    return (T) Proxy.newProxyInstance(
    			service.getClassLoader(), //動態生成介面實現類
     			new Class<?>[] { service }, //動態建立例項
        		new InvocationHandler() { //代理類的方法的呼叫會交給它的invoke方法
        		
          		private final Platform platform = Platform.get();

          @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)throws Throwable {

			 //proxy是代理類物件
          //method是呼叫的代理類的哪個方法
          //args是方法的餐忽
            // 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<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

在上面的程式碼中,我們可以看到的是,主要是呼叫了Proxy.newProxyInstance方法,在這裡主要使用到了動態代理,什麼是動態代理呢?代理類在程式執行時建立的代理方法被稱為動態代理,也就是說,在這種情況下,代理類並不是在Java程式碼中定義的,而是在執行時根據我們在Java程式碼中的“指示”動態生成的。
關於動態代理,可以參考:https://juejin.im/post/5ad3e6b36fb9a028ba1fee6a

我們先看看Proxy.newProxyInstance方法中做了什麼:

 public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
         //查詢或生成指定的代理類
        Class<?> cl = getProxyClass0(loader, intfs);
			//得到cl的構造器
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            //檢查cl的訪問許可權
            if (!Modifier.isPublic(cl.getModifiers())) {
                // Android-changed: Removed AccessController.doPrivileged
                cons.setAccessible(true);
            }
            //返回它的例項
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

在這裡,通過Proxy工具,為委託類的介面自動生成一個代理物件,後續的方法呼叫都是這個代理物件進行發起的,最終會執行到InvocationHandler#invoke方法。

然後我們看看這裡InvocationHandler#invoke方法的實現:

@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);
            }
            //關注1
            //讀取網路請求接口裡的方法,並根據前面的配置好的屬性配置serviceMethod物件
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            //關注2
            //根據配置好的serviceMethod物件建立OkHttpCall物件
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
			//關注點3
			//呼叫OkHttp,並根據oKHttpCall返回Call(不考慮RxJava)
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }

我們先看關注點1:

 ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);

我們先看看看ServiceMethod是什麼:

Adapts an invocation of an interface method into an HTTP call
將介面方法的呼叫調整為一個HTTP call
也就是哦說,一個serviceMethod物件對應於網路請求接口裡的一個方法


private final Map<Method, ServiceMethod<?, ?>> serviceMethodCache 
														= new ConcurrentHashMap<>();
ServiceMethod<?, ?> loadServiceMethod(Method method) {
//在建立ServiceMethod物件前,先看serviceMethodCach有沒有快取之前建立過的網路請求
    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的建立:
首先是new ServiceMethod.Builder<>(this, method)

 Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit;
      this.method = method;
      //獲取網路請求介面方法裡的註釋
      this.methodAnnotations = method.getAnnotations();
      //獲取網路請求介面方法裡的引數型別
      this.parameterTypes = method.getGenericParameterTypes();
      //獲取網路請求介面方法裡的註釋內容
      this.parameterAnnotationsArray = method.getParameterAnnotations();
    }

然後呼叫build方法:

 public ServiceMethod build() {
 	//根據網路請求介面方法的返回值和註解型別,從Retrofit物件中獲取對應的網路請求介面卡
      callAdapter = createCallAdapter();
      //獲得該網路介面卡返回的資料型別
      //如Call<String>
      responseType = callAdapter.responseType();
      if (responseType == Response.class || responseType == okhttp3.Response.class) {