<源码系列> Retrofit之二:源码分析

2020-06-29  本文已影响0人  玉圣

说明:
本文的源码分析较为粗浅,和其他源码“解析”的文章相比并未特别详细,个人觉得看别人的源码,将整体的思路和大框架了解了,理解了其思想原理足矣

很重要的一点:
一定要带着质疑别人所谓的的“解析”去分析,一定要结合源码有自己的理解,不能完全相信他人的观点,即便是所谓的“大神”,人,总有犯错的时候。

如果文中有哪里不对的地方,请多指教。

从调用的流程开始分析

流程

一、Retrofit的初始化

    // 初始化配置Retrofit
    retrofit = new Retrofit.Builder()
            .baseUrl(AppConfig.BASE_URL)
            //可设置自定义的client
            .client(getOkHttpClient())
            //可设置自定义的执行类CallAdapterFactory,可多个
            .addCallAdapterFactory(new CustCallAdapterFactory())
            //可设置自定义的解析类ConverterFactory,可多个
            .addConverterFactory(new CustConvertFactory())
            .build();

   private OkHttpClient getOkHttpClient() {
      if (okHttpClient == null) {
         okHttpClient = new OkHttpClient().newBuilder()
               .connectTimeout(15 * 1000, TimeUnit.MILLISECONDS)
               .readTimeout(15 * 1000, TimeUnit.MILLISECONDS)
               .retryOnConnectionFailure(true)
               .addInterceptor(xxxInterceptor)
               .build();
      }
      return okHttpClient;
   }

1、baseUrl:

二、Retrofit的请求和响应

    // ① 获取接口实现
    GitHubService service = retrofit.create(GitHubService.class);
    
    // ② 调用(下面同步或异步请求选其一)
    Call<List<Repo>> repos = service.listRepos("octocat");
    
    // ③ 同步请求
    Response<ResponseBody> res = repos.execute();
    
    // ③ 异步请求
    repos.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
            try {
                System.out.println(response.body().string());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {
            t.printStackTrace();
        }
    });

    // 或者通过使用Observable,需要在初始化时进行配置:
    // addCallAdapterFactory(RxJava3CallAdapterFactory.createSynchronous())
   Observable<UserBean> call = mRetrofit.create(GitHubService.class).getUserData();
   call.subsribe(Xxx)

1、接口的实现类:

Retrofit 的示例来看,只需要一个接口及接口方法即可,调用者无需实现接口的具体实现类,就可以调用其内的方法,接口的实现类是从哪来的呢?
通过Retrofit#create 方法获得了接口的实现类,其内部是通过 动态代理(模式) Proxy.newProxyInstance 来实现的:

  public <T> T create(final Class<T> service) {
    validateServiceInterface(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 @Nullable 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);
                }
                args = args != null ? args : emptyArgs;
                return platform.isDefaultMethod(method)  //对default方法进行判断
                    ? platform.invokeDefaultMethod(method, service, proxy, args)
                    : loadServiceMethod(method).invoke(args);
              }
            });
  }

注:其中loadServiceMethod返回的类型是ServiceMethod ,其具体实现类为HttpServiceMethod。(似乎在之前的版本,2.2.0吧,ServiceMethodfinal 的,没有实现类。)

2、解析并装配参数(Okhttp请求)

在发送请求的时候,需要传入一些参数,对于Retrofit ,其请求的参数是放到注解上的,那它是如何对其进行解析的呢?是通过loadServiceMethod 方法内部对接口方法的注解及参数的解析而得。

  • ConcurrentHashMap是个线程安全的集合(数组 + 链表 + 红黑树),因为网络请求都是在线程中去做的
  • ServiceMethod 对象(具体实现为HttpServiceMethod)中封装了各种解析工厂类等请求和响应的数据类信息(CallAdapterConverter 等等)。

3、“处理器”工厂:

这里我将类型适配器CallAdapter和解析转换器Converter称之为处理器,两者都用了 抽象工厂模式
CallAdapter:是对响应的数据进行转换,由T类型转换为R类型,这里用了 适配器模式
Converter:是对请求数据和响应结果进行处理的数据类型转换的处理类。

4、请求的发起OkHttpCall

Retrofit 中真正发起网络请求的,最终还是通过的OkHttp,所以要创建OkHttpCall

5、结果返回类型

例子中的Call<List<Repo>> 是如何返回的呢?

6、如何获取返回结果的泛型实参

问题:java泛型有类型擦除,那是如何在运行时获得XxxBean(如List<Repo>)的呢?

上面说到,在获取returnType 之后,判断是否为泛型参数类型(ParameterizedType),
然后再通过CallAdapter中的getParameterUpperBound (取类型的上限)

 //Factory中
 protected static Type getParameterUpperBound(int index, ParameterizedType type) {
   return Utils.getParameterUpperBound(index, type);
 }
 
 //Utils中
 static Type getParameterUpperBound(int index, ParameterizedType type) {
     //types即为<Xxx, Yyy, ...> 中的 Xxx, Yyy, ...这一数组
     //获取泛型实参,主要在这里:getActualTypeArguments
     Type[] types = type.getActualTypeArguments();
     if (index < 0 || index >= types.length) {
       throw new IllegalArgumentException(
           "Index " + index + " not in range [0," + types.length + ") for " + type);
     }
     Type paramType = types[index];
     //对通配符的判断
     if (paramType instanceof WildcardType) {
       return ((WildcardType) paramType).getUpperBounds()[0];
     }
     return paramType;
 }

如果需要获取泛型类型,就要在混淆的时候,保留相关的方法处理,不能进行混淆。
若混淆了,生成的如下面的字节码上的注释都会被干掉,因此这些泛型类型都会被去掉,就无法获取到了。

泛型擦除是一个无奈之举,因为在java1.5出现泛型之前的1.4版本及之前的版本,已经使用的很广泛了,为了兼容之前的代码程序。
C#是将泛型作为一个真实的类型存在,没有泛型擦除的问题。

7、Retrofit中的kotlin协程

    interface GitHubService {
    
        @GET("getUserData")
        suspend fun getUserData(): UserBean
    
    }

    data class UserBean(val userName: String, val userAge: Long)

    val retrofit = Retrofit.Builder()
        .baseUrl(BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .build()
    val service = retrofit.create(GitHubService::class.java)
    val job: Job = launch {
       try {
           val userBean: UserBean = service.getUserData()
           println("userBean: $userBean")
       } catch (e: Throwable) {
           println("onFailure: $e")
       }
    }

8、Retrofit在Android中的支持

支持主要在两方面:
① 是否支持 Java8,根据Android上是否启用 Java 8来判断,(Gradle中配置)
② 实现UI线程回调的Executor,将回调结果切到UI线程

  private static Platform findPlatform() {
    //根据 JVM 名字来判断使用方是否是 Android 平台
    return "Dalvik".equals(System.getProperty("java.vm.name"))
        ? new Android()
        : new Platform(true);
  }
    Android() {
      super(Build.VERSION.SDK_INT >= 24);
    }

9、如何支持RxJava

10、如何在反序列化时实例化对象

具体处理类是GsonConverterFactory
其中在处理request请求和response响应时,通过 gson.getAdapter 来返回TypeAdapter ,并传入 GsonRequestBodyConverterGsonResponseBodyConverter 构造来处理解析的数据。在 TypeAdapterFactory 获取的TypeAdapter 实现的,一般是ReflectiveTypeAdapterFactory ,而ObjectTypeAdapter 中的read 方法是对基本数据类型进行处理解析。
具体的实例化是,在 Gson 构造函数中,创建了一个ConstructorConstructor 构造器的构造器,接收一个instanceCreators(调用者传进来的):

Gson(final Excluder excluder, final FieldNamingStrategy fieldNamingStrategy, final Map<Type,
      InstanceCreator<?>> instanceCreators,
      ..., ...//省略好多入参....
    ) {
    //省略好多....
    this.constructorConstructor = new ConstructorConstructor(instanceCreators);
    //省略好多....
}

ConstructorConstructor 中对对象的构造做了很多流程判断(如图),判断了是否有无参构造(通过newDefaultConstructor)
终极方案就是使用newUnsafeAllocator 中的native 方法来开辟对象内存空间:

public native Object allocateInstance(Class<?> var1) throws InstantiationException;

在终极方案中,进行实例化对象:

private <T> ObjectConstructor<T> newUnsafeAllocator(
      final Type type, final Class<? super T> rawType) {
    return new ObjectConstructor<T>() {
      private final UnsafeAllocator unsafeAllocator = UnsafeAllocator.create();
      @SuppressWarnings("unchecked")
      @Override public T construct() {
        try {
           //看这里
          Object newInstance = unsafeAllocator.newInstance(rawType);
          return (T) newInstance;
        } catch (Exception e) {
          throw new RuntimeException(("Unable to invoke no-args constructor for " + type + ". "
              + "Registering an InstanceCreator with Gson for this type may fix this problem."), e);
        }
      }
    };
  }

最终实例化了对象,实例化的时机,是在read的时候:

//ReflectiveTypeAdapterFactory <-- 大多数都是这个factory
public static final class Adapter<T> extends TypeAdapter<T> {
    private final ObjectConstructor<T> constructor;
    private final Map<String, BoundField> boundFields;

    Adapter(ObjectConstructor<T> constructor, Map<String, BoundField> boundFields) {
      this.constructor = constructor;
      this.boundFields = boundFields;
    }

    @Override
    public T read(JsonReader in) throws IOException {
      if (in.peek() == JsonToken.NULL) {
        in.nextNull();
        return null;
      }
      //看这里啊,这里啥都木有,就是实例化一个对象,字段全是默认值
      T instance = constructor.construct();
      //省略好多..... 
      return instance;
    }

    @Override
    public void write(JsonWriter out, T value) throws IOException {
      //省略好多.....
    }
  }
Gson对象实例化流程

设计模式举例:

1、Builder模式

image.png

2、工厂模式

抽象工厂模式
Retrofit中的抽象工厂模式
工厂方法模式

3、适配器模式

适配器模式 Retrofit中的适配器模式

4、代理模式

代理模式

访问子类(真正实现类)不可直接访问,要通过Proxy 的访问策略,来对内部私有的子类成员来进行控制访问。都实现了同样的接口(父类Subject)

需要注意的是,在Retrofit中创建实例的时候,虽然用了java中的动态代理,但并不是严格意义上的代理?难道不是代理了传入的接口service 么?

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() {
          //省略好多...
}

如何分析源码总结:

上一篇 下一篇

猜你喜欢

热点阅读