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

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

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

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

从调用的流程开始分析

流程

一、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;
   }
  • 这里对Retrofit进行初始化,是通过构造者模式进行构建相关的参数数据
  • addConverterFactory:为对象的序列化和反序列化添加转换器工厂
  • addCallAdapterFactory:添加调用适配器工厂以支持除Call之外的服务方法返回类型

1、baseUrl:

  • baseUrl的方法有多个重载,最终通过构造HttpUrl传入,一直传递给okhttp3.Request

    public Builder baseUrl(HttpUrl baseUrl)  //最终的调用
    public Builder baseUrl(String baseUrl)
    
    Request build() {
      HttpUrl url;
      HttpUrl.Builder urlBuilder = this.urlBuilder;
      if (urlBuilder != null) {
        url = urlBuilder.build();
      } else {
        // No query parameters triggered builder creation, just combine the relative URL and base URL.
        url = baseUrl.resolve(relativeUrl);
        if (url == null) {
          throw new IllegalArgumentException(
              "Malformed URL. Base: " + baseUrl + ", Relative: " + relativeUrl);
        }
      }
      //省略好多...
      return requestBuilder
          .url(url)
          .method(method, body)
          .build();
    }
    
  • 如何动态切换Retrofit的BaseURL?
    Retrofit中接收的baseUrl(String),最终是作为HttpUrl(okhttp3包里的)的类型参数传入的,在HttpUrl中有个host的字段,可以通过反射进行修改

    public Builder baseUrl(String baseUrl) {
        checkNotNull(baseUrl, "baseUrl == null");
        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");
        List<String> pathSegments = baseUrl.pathSegments();
        if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
          throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
        }
        this.baseUrl = baseUrl;
        return this;
      }
    

二、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);
              }
            });
  }
  • 动态代理Proxy.newProxyInstance 的参数和返回结果:

    • classLoader[参数]:类加载器会产生新类(实现的是传入的接口)
    • class[][参数]:新类实现的接口,传进来(放到数组中)
    • invocationHandler 接口[参数]:执行方法体
    • T[返回值]:一个封装了请求参数的接口的类,来发起网络请求
      通过字节码(或打印其getClass())可得出类似于:class com.sun.proxy.$Proxy0
  • 获取(动态代理)接口的实例对象
    Proxy中,通过newProxyInstance获取对象时,其中维护了一个代理类的缓存集合proxyClassCache【是WeakCache,其内部通过ConcurrentMap进行实际的缓存】,从此缓存中获取生成的接口实现类。
    而实现类的创建,是通过其固定的名称组装的类名:

     //Proxy中:
     //固定规则
     if (proxyPkg == null) {
        // if no non-public proxy interfaces, use com.sun.proxy package
        proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";  //com.sun.proxy
     }
     String proxyName = proxyPkg + proxyClassNamePrefix + num;
    
     byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
     try {
         //这里返回结果,加载byte[]来获取对象
         return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
     } catch (ClassFormatError e) {
         throw new IllegalArgumentException(e.toString());
     }
    
  • InvocationHandler#invoke 内部解析:
    1】Retrofit#loadServiceMethod
    在每次调用请求接口方法的时候,每次都会通过调用invocationHandler 中的loadServiceMethod 方法,
    loadServiceMethod方法内部,会解析得到具体的接口方法的实现(如例子中的listRepos),进行调用invoke(args) 执行方法,发起网络请求。
    2】platform.isDefaultMethod(method)
    invocationHandler 中对default 方法进行了判断。如果是默认方法则对默认方法进行调用
    default 是Java8的特性,在接口中可定义一些默认的方法:

     interface GithubApi {
          //默认方法
         default void hello() {
           //do something...
         }
     }
    

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

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

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

  • Retrofit#loadServiceMethod
    loadServiceMethod 方法中,通过传入的Method类型的参数来封装成ServiceMethod 对象。
    获取具体的ServiceMethod对象流程(复用):
    1】先从serviceMethodCache 缓存集合(ConcurrentHashMap)中根据Method取出ServiceMethod对象,有则直接返回
    2】若无,加锁,通过ServiceMethod#parseAnnotations解析方法的注解信息,创建对应的接口方法的实现;
    并缓存到集合中
  • ConcurrentHashMap是个线程安全的集合(数组 + 链表 + 红黑树),因为网络请求都是在线程中去做的
  • ServiceMethod 对象(具体实现为HttpServiceMethod)中封装了各种解析工厂类等请求和响应的数据类信息(CallAdapterConverter 等等)。
  • ServiceMethod#parseAnnotations
    1】通过RequestFactory获取接口方法(如listRepos方法)上的注解和参数等信息
    如:baseUrlheaders等等
    2】获取返回值的泛型参数类型:
    method.getGenericReturnType()
    3】最后返回具体的注解信息结果:
    HttpServiceMethod.parseAnnotations
    通过HttpServiceMethodServiceMethod的实现类)进行 返回值包装类返回值类的处理。

  • HttpServiceMethod#parseAnnotations
    1】通过HttpServiceMethod#createCallAdapter 得到CallAdapter对象,方法内部根据参数进行匹配而得
    2】通过createResponseConverter得到Converter对象,方法内部根据参数匹配而得
    3】通过CallAdapterConverter等对象构造出HttpServiceMethod具体实现的子类对象并返回
    ① 若不是kotlin协程的方法,则返回CallAdapted对象
    ② 否则返回协程方法相关的对象实例:SuspendForResponseSuspendForBody

  • ServiceMethod#invoke(args)
    执行接口方法的调用,进而发起网络请求
    invokeServiceMethod的抽象方法,由其直接子类HttpServiceMethod实现

  • HttpServiceMethod.invoke(args)
    1】实现了父类ServiceMethod的方法
    2】创建Call对象
    Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
    3】在实现的invoke方法中,又转交给HttpServiceMethod.adapt的抽象方法去完成的

  • HttpServiceMethod.adapt
    适配数据类型,并执行网络请求。
    1】具体如何将Call转换为指定的返回值包装类的,最终是交给实现了HttpServiceMethod的子类去完成的。
    2】如在CallAdapted中,其adapt方法是直接交由传入的CallAdapter的实现类来处理的
    在使用的例子中是RxJava3CallAdapterFactory内的具体实现RxJava3CallAdapter

3、“处理器”工厂:

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

  • CallAdapter
    1】Retrofit中定义了接口CallAdapter<R, T>,并由具体的子类实现如下方法:
    Type responseType();:返回具体的内部类型,如UserBean
    T adapt(Call<R> call);:用于将retrofit2.Call转换为 返回值包装类,如Observable,并执行请求
    2】Retrofit默认只支持retrofit2.Call作为接口方法的 返回值包装类
    可通过实现CallAdapter接口进行扩展Call,适配器的添加是通过在初始化Retrofit时配置的:
    Retrofit.Builder.addCallAdapterFactory方法,可加多个适配器来转换 返回值包装类
    如可设置RxJava2CallAdapterFactory的Adapter
    3】而真正使用的只有一个,在Retrofit#nextCallAdapter中处理了相关逻辑
    根据类型和注解的相关信息,循环List<CallAdapter.Factory> 进行匹配,
    根据returnTypeType)、annotationsAnnotation[])等信息,匹配到了适配器(即返回结果不为null),则终止循环,直接返回这个factory。
    public interface CallAdapter<R, T> {
    
      Type responseType();
    
      T adapt(Call<R> call);
    }
    
  • Converter
    1】Retrofit 中定义了接口Converter<F, T>,并由具体的子类实现convert方法:
    T convert(F value):即从FT(数据Bean)之间的转换操作。(多是处理POST请求)
    F 一般是RequestBody(请求体)或是ResponseBody (响应体),而T 是通过convert 方法返回的转换类型(如UserBean)。
    2】Retrofit提供了addConverterFactory方法,来配置添加转换实体的工厂对象,可加多个
    ① 在请求的时候,通过Retrofit#requestBodyConverter 方法(在构建RequestFactory中调用的)获取指定的 请求 数据解析器。
    ② 通过Retrofit#responseBodyConverter 方法(在HttpServiceMethod#createResponseConverter中获得的)获取指定的 响应 数据解析器。
    3】在HttpServiceMethod#parseAnnotations方法中调用了HttpServiceMethod#createResponseConverter
    并在其内部获取到匹配的转换器,实际调用了Retrofit#responseBodyConverter方法
    4】在获取响应结果的转换器Retrofit#responseBodyConverter(实际调用了nextResponseBodyConverter方法)中,会循环遍历converterFactories转换器集合(addConverterFactory方法添加)
    直到根据typeType)、annotationsAnnotation[])等信息,匹配到转换器(即返回结果不为null),则终止循环,直接返回这个factory。
    public interface Converter<F, T> {
      T convert(F value) throws IOException;
    }
    

4、请求的发起OkHttpCall

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

  • OkHttpCall对象的创建:
    HttpServiceMethod.invoke(args)的方法中,创建了OkHttpCall对象
    并将其实例传给了抽象方法adapt,是由子类决定如何通过OkHttp的实例发起网络请求

     @Override
     final @Nullable ReturnT invoke(Object[] args) {
       Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
       return adapt(call, args);
     }
    
  • OkHttpCall#getRawCall -> createRawCall
    在方法中,创建了真正的请求调用对象,并传入了请求的相关信息
    返回的Call实例,是OkHttp中的请求对象,所以具体的请求还是由OkHttp完成的。

      private okhttp3.Call createRawCall() throws IOException {
        okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
        if (call == null) {
          throw new NullPointerException("Call.Factory returned null.");
        }
        return call;
      }
    
  • OkHttpCall#enqueue / execute方法:
    1】OkHttpCall类中是具体发起请求的地方,即重写的enqueue / execute方法
    2】在接口(GitHubService)中定义的方法(listRepos)的返回类型Call<ResponseBody> ,这里的Call 实际上就是OkHttpCall类型
    3】当调用call.enqueue(Callback)时,enqueue就是OkHttpCall中的具体实现,而Callback(retrofit2.Callback)会在okhttp3.Callback回调时进行转换。
    4】HttpServiceMethod的子类实现的adapt方法中,根据传入的Call,又调用了其enqueue来执行请求的异步任务。

5、结果返回类型

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

  • 类型说明
    首先先明确一下,Retrofit中如何对这个返回类型中的各个部分定义的:

    • returnType:接口方法中的 返回值类型,如Call<List<Repo>>
    • rawType:接口方法中的 返回值包装类,如Call
    • responseType:接口方法中返回的数据Bean类型,如List<Repo>
  • 创建对象后的类型返回
    1】ReturnT invoke
    上面1中说到,请求的方法通过invoke方法来调用的,那返回类型就从这里入手,
    invoke 是在ServiceMethod 中定义的,具体实现在HttpServiceMethodinvoke中(之前ServiceMethod无实现类的时候,代码直接写在Retrofit 中来实现的),
    HttpServiceMethod#invoke 中又包了一层了adapt 抽象方法,adapt定义的返回值的类型ReturnT 就是接口方法的返回类型(如Call<List<Repo>>

    @Override
    final @Nullable ReturnT invoke(Object[] args) {
      Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
      return adapt(call, args);
    }
    
    protected abstract @Nullable ReturnT adapt(Call<ResponseT> call, Object[] args);
    

    2】ReturnT adapt(Call<ResponseT> call, Object[] args)
    adapt 方法的具体实现类是CallAdapted<ResponseT, ReturnT>(非kotlin协程),就在HttpServiceMethod 中,是个内部类。此方法是通过CallAdapter 来实现的,它其中的adapt 方法就是一个类型转换器,具体后面介绍。

     @Override
     protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
       return callAdapter.adapt(call);
     }
    

    3】CallAdapter 的具体实现有很多,其中有个最原始的支持:DefaultCallAdapterFactory
    此类中直接创建的匿名内部类CallAdapter的实现没有具体转换,只转成了最基本的Call 类型,是因为要将线程中的请求结果,发送到主线程中,这其中就将Handler的消息发送包装了起来:

      return new CallAdapter<Object, Call<?>>() {
        @Override
        public Type responseType() {
          return responseType;
        }
    
        @Override
        public Call<Object> adapt(Call<Object> call) {
          return executor == null ? call : new ExecutorCallbackCall<>(executor, call);
        }
      };
    
  • 泛型类型的处理
    DefaultCallAdapterFactory 中,对相关的泛型参数类型进行了处理:

    @Override
    public @Nullable CallAdapter<?, ?> get(
        Type returnType, Annotation[] annotations, Retrofit retrofit) {
      if (getRawType(returnType) != Call.class) {
        return null;
      }
      if (!(returnType instanceof ParameterizedType)) {
        throw new IllegalArgumentException(
            "Call return type must be parameterized as Call<Foo> or Call<? extends Foo>");
      }
      final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType);
    

    1】先判断rawTypeCall,在DefaultCallAdapterFactory 的只对Call 类型处理
    2】然后判断其中是否有泛型参数(即returnType[如Call<List<Repo>>] 是否是参数化类型ParameterizedType
    3】再从参数化类型中取出泛型实参(responseType [如List<Repo>]),这里取的上限(Call<? extends Foo>)的第一个(有可能有多个泛型实参)。

  • 如何处理非原始支持的类型,如kotlin的Deffered
    同样的,也是需要实现CallAdapter 来具体的进行转换返回相应的类型。

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;
 }

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

  • 原始代码:
     public static Map<List<String>, Set<Map<Number, String>>> test(Map<String, Set<Map<Number, String>>> map) {
         return null;
     }
    
  • 编译后的字节码:
    // access flags 0x9
    // signature (Ljava/util/Map<Ljava/lang/String;Ljava/util/Set<Ljava/util/Map<Ljava/lang/Number;Ljava/lang/String;>;>;>;)Ljava/util/Map<Ljava/util/List<Ljava/lang/String;>;Ljava/util/Set<Ljava/util/Map<Ljava/lang/Number;Ljava/lang/String;>;>;>;
    // declaration: java.util.Map<java.util.List<java.lang.String>, java.util.Set<java.util.Map<java.lang.Number, java.lang.String>>> test(java.util.Map<java.lang.String, java.util.Set<java.util.Map<java.lang.Number, java.lang.String>>>)
    public static test(Ljava/util/Map;)Ljava/util/Map;
    

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

7、Retrofit中的kotlin协程

  • 使用:可直接返回实体Bean,而不需要任何包装类
    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")
       }
    }

  • 内部解析
    1】Retrofit是以Java语言实现的,而suspend 挂起函数只能用于ktolin,不过都会编译为JVM语言
    通过将接口ApiService 反编译得到的Java类,结果如下:

     public interface ApiService {
        @GET("getUserData")
        @Nullable
        Object getUserData1(@NotNull Continuation var1);
     }
    

    2】在RequestFactory中包含一个isKotlinSuspendFunction的成员,用来标记当前解析的Method是否为suspend函数。
    isKotlinSuspendFunction的值,是在RequestFactory#build方法中跟进方法参数的最后一个是否为Continuation.class来判定的。

    3】在处理方法的解析中,HttpServiceMethod#parseAnnotations内,根据isKotlinSuspendFunction来处理kotlin协程的接口方法的解析结果
    如果是,则返回SuspendForResponse(得到Response对象)或SuspendForBody(得到数据Bean对象,如UserBean)对象,皆为HttpServiceMethod 的子类

    4】SuspendForBody实现的adapt方法中,
    将接口方法(getUserData)最后一个参数强转为Continuation<ResponseT>
    最终会调用KotlinExtensions.await这个kotlin扩展方法

    5】KotlinExtensions.await方法中,
    suspendCancellableCoroutine 支持cancel的CoroutineScope 为作用域,依旧以Call.enqueue发起OkHttp 请求,得到responseBody 后将其回调出去。

8、Retrofit在Android中的支持

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

  • 是否为Android平台的判断:
    根据虚拟机的名称来判断,其中定义的Android 类,是Platform的唯一子类
  private static Platform findPlatform() {
    //根据 JVM 名字来判断使用方是否是 Android 平台
    return "Dalvik".equals(System.getProperty("java.vm.name"))
        ? new Android()
        : new Platform(true);
  }
  • 是否支持Java 8
    通过Platform中的hasJava8Types成员来判断,在Platform#Android类中,是通过构造方法传入的条件判断的:
    Android() {
      super(Build.VERSION.SDK_INT >= 24);
    }
  • UI线程的回调实现:
    1】在Platform#Android中,重写了defaultCallbackExecutor方法,
    返回了创建的MainThreadExecutor对象,其中通过Handler实现了将执行任务Runnable转发到UI线程的逻辑

     static final class MainThreadExecutor implements Executor {
       private final Handler handler = new Handler(Looper.getMainLooper());
    
       @Override
       public void execute(Runnable r) {
         handler.post(r);
       }
     }
    

    2】上面说到的CallAdapter.Factory ,是用于处理接口返回值包装类(如Observable)的适配器,在Retrofit#build的时候,会将DefaultCallAdapterFactory添加进去。而DefaultCallAdapterFactory 中就是处理对应的线程切换的操作。

    3】DefaultCallAdapterFactory#get方法返回的CallAdapter对象,就是包装了ExecutorCallbackCall(实现了Call接口)对象,即具体实现了执行请求的ExecutorCallbackCall#enqueue方法,在此方法中,进行了任务的执行:

     @Override
     public void enqueue(final Callback<T> callback) {
       Objects.requireNonNull(callback, "callback == null");
    
       delegate.enqueue(
           new Callback<T>() {
             @Override
             public void onResponse(Call<T> call, final Response<T> response) {
               callbackExecutor.execute(
                   () -> {
                     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(() -> callback.onFailure(ExecutorCallbackCall.this, t));
             }
           });
     }
    

9、如何支持RxJava

  • 在构建Retrofit时,通过添加CallAdapterFactory

      retrofit = new Retrofit.Builder()
                 .client(OKHttpFactory.INSTANCE.getOkHttpClient())
                 .baseUrl(AppConfig.BASE_URL)
                 .addConverterFactory(GsonConverterFactory.create())
                 //看这里
                 .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                 .build();
    
  • RxJava2CallAdapterFactory内部实现:
    RxJava2CallAdapterFactory中对返回值类型进行一系列操作,处理泛型参数等,并处理返回
    其中的returnType 就是Observable<XxxBean>XxxBean 就是自己定义的需要解析的类型),拿到returnType 之后,获取rawType 并进行一系列判断,判断各种RxJava 中定义的类型,最终将Obervable的这种rawType 类型进行处理,然后在对泛型参数类型(即Observable 尖括号中的泛型类型)进行获取并处理,最终进行相关赋值,并返回CallAdapter<?>

  • 还支持Observable<Response<XxxBean>>
    请求的时候,具体实现为BodyObservable 的类型返回,真正实现adapt 的相关工作

     Type observableType = getParameterUpperBound(0, (ParameterizedType) returnType);
     Class<?> rawObservableType = getRawType(observableType);
     if (rawObservableType == Response.class) {
       if (!(observableType instanceof ParameterizedType)) {
         throw new IllegalStateException(
             "Response must be parameterized" + " as Response<Foo> or Response<? extends Foo>");
       }
       responseType = getParameterUpperBound(0, (ParameterizedType) observableType);
     } else if (rawObservableType == Result.class) {
       if (!(observableType instanceof ParameterizedType)) {
         throw new IllegalStateException(
             "Result must be parameterized" + " as Result<Foo> or Result<? extends Foo>");
       }
       responseType = getParameterUpperBound(0, (ParameterizedType) observableType);
       isResult = true;
     } else {
       responseType = observableType;
       isBody = true;
     }
    
     return new RxJava2CallAdapter(
         responseType, scheduler, isAsync, isResult, isBody, isFlowable, isSingle, isMaybe, false);
    }
    

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中的抽象工厂模式

工厂方法模式
  • 简单工厂:根据传递的参数(String、class等等)来创建对应的类对象(产品)
    特点:接口较少,简单方便
    缺点:增加产品的时候,需要增加/修改simpleFactory的代码


    简单工厂

3、适配器模式

适配器模式
  • adapt 接收一种类型(接口),让实现者去转换为具体的结果,接收的是R 类型,返回的是T 类型。
    除了Retrofit的CallAdapter,还有Gson中的TypeAdapter同样也是适配器模式
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() {
          //省略好多...
}
  • 代理模式 vs 装饰模式
    • 代理模式:控制访问
    • 装饰模式:增强功能

如何分析源码总结:

  • 带着问题入手
  • 从最熟悉的部分切入
  • 看看有哪些可扩展性设计
  • 分析下运用了哪些设计模式
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342