Retrofit流程解析

1,263 阅读7分钟

Retrofit

Retrofit通过组合各种设计模式,封装网络请求接口的框架,具体的请求交给Okhttp去完成。

简单实现

Retrofit对象的配置需要数据序列化、线程调度、适配器等一系列的配置。具体配置可以自由设置,需要注意的是配置数据转换器时,要指定对应的转换器。不然数据序列化时可能会报错。

val client = OkHttpClient.Builder()
        .addInterceptor(LoggingInterceptor())
        .build()

val retrofit = Retrofit.Builder()
        .client(client)
        .baseUrl(HttpService.HttpUrl.url)
        .addCallAdapterFactory(KotlinCallAdapterFactory())
        .addConverterFactory(GsonConverterFactory.create())
        .addConverterFactory(SimpleXmlConverterFactory.create())
        .addConverterFactory(JaxbConverterFactory.create())
        .build()

fun request() {
    retrofit.create(HttpService::class.java)
            .request1()
            .enqueue(object : Callback<ResponseData<List<WxArticle>>> {
                override fun onResponse(call: Call<ResponseData<List<WxArticle>>>, response: Response<ResponseData<List<WxArticle>>>) {
                    Log.i("MDY", "onResponse=" + Thread.currentThread().name)
                    Log.i("MDY", "onResponse: " + response.body().toString())
                }

                override fun onFailure(call: Call<ResponseData<List<WxArticle>>>, t: Throwable) {
                    Log.i("MDY", "onFailure: ")
                }
            })
}

Retrofit创建后,具体的网路请求需要调用create方法,动态生成一个代理类来实现对应的网络请求。

create

Android中创建一个委托类Service时,由于Service是一个接口,请求方法一定是public abstract 类型,所以Retrofit中create方法创建的动态代理类在调用Service中对应的请求方法时,最终会执行到loadServiceMethod方法中:

  ServiceMethod<?> loadServiceMethod(Method method) {
    ServiceMethod<?> result = serviceMethodCache.get(method);
    if (result != null) return result;
    //同步锁,防止ServiceMethod多次创建
    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      if (result == null) {
        //解析Method
        result = ServiceMethod.parseAnnotations(this, method);
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

Retrofit中使用一个Map集合来保存已经解析过的请求方法并封装为对应的实现类ServiceMethod:

private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>()

若请求接口时第一次被调用,会通过ServiceMethodparseAnnotations来解析方法。首先会调用RequestFactoryparseAnnotations方法解析方法的注解、参数注解、返回类型等:

  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
    //......判断返回类型是否合法
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }

HttpServiceMethod本身也是一个抽象类,parseAnnotations方法中会获取对应的CallAdapter、Converter和callFactory对象,最后创建一个CallAdapted对象返回。

createCallAdapter

  private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(
      Retrofit retrofit, Method method, Type returnType, Annotation[] annotations) {
    try {
      return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);
    } catch (RuntimeException e) { // Wide exception range because factories are user code.
      throw methodError(method, e, "Unable to create call adapter for %s", returnType);
    }
  }

调用Retrofit的callAdapter方法,最终会回调到我们在创建Retrofit时传入的CallAdapterFactory中,并调用对应的get方法获取 CallAdapter对象。

createResponseConverter

  private static <ResponseT> Converter<ResponseBody, ResponseT> createResponseConverter(
      Retrofit retrofit, Method method, Type responseType) {
    Annotation[] annotations = method.getAnnotations();
    try {
      return retrofit.responseBodyConverter(responseType, annotations);
    } catch (RuntimeException e) { // Wide exception range because factories are user code.
      throw methodError(method, e, "Unable to create converter for %s", responseType);
    }
  }

调用Retrofit的responseBodyConverter方法,最终会回调到我们在创建Retrofit时传入的Converter.Factory中,并调用对应的responseBodyConverter方法获取Converter<ResponseBody, ?>对象。在后面的OkHttpCall中获取响应体后会调用已创建的Converter来序列化数据。

callFactory

okhttp3.Call.Factory callFactory = retrofit.callFactory;

Call.Factory就是我们传递到Retrofit中的OkhttpClient对象。

CallAdapted

ServiceMethodHttpServiceMethod都是抽象类,我们需要的是一个具体的实现类,所以CallAdapted来了。CallAdapted继承自HttpServiceMethod并保存了以下对象:

  • requestFactory 解析请求方法后的数据保存类。
  • callFactory OkhttpClient对象,用于具体的网络请求。
  • responseConverter 数据转换器,用于序列化请求返回的响应。
  • callAdapter 调用适配器,或区域一个call的对象,可以实现请求的发起和线程的调度。

执行到了这里,我们回到最开始的create方法中,当通过动态代理调用请求方法时,执行到loadServiceMethod最终会获取到一个CallAdapted对象。紧接着调用invoke方法,CallAdapted中并没有实现该方法,具体的在HttpServiceMethod中:

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

这里出现一个OkHttpCall类,这个类用于具体的网络请求,内部创建了OKhttp中对应的RealCall对象,并添加了同步和异步的方法,具体的使用和Okhttp区别不大。由于本文是分析Retrofit的原理流程,所以不多加解释,有兴趣的可以进去看看。紧接着会回调CallAdaptedadapt方法:

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

这里的CallAdapter是我们在HttpServiceMethod中创建的callAdapter,像我们使用到RxJava的话,这里传递的就是对应的RxJavaCallAdapterFactory对象内部返回的CallAdapter。在调用adapt方法时,将invoke方法中创建的OkHttpCall传递进去,因为OkHttpCall中封装了对应的Realcall,所以不难猜测,具体的网络请求和解析肯定在对应的CallAdapter实现类中。

CallAdapter

CallAdapter称为调用适配器,在创建Retrofit对象时,我们可以调用addCallAdapterFactory(KotlinCallAdapterFactory())传入一个指定的调用适配工厂类,用于创建具体的调用适配器。具体的工厂类需要继承Factory类,通过get方法返回一个CallAdapter对象。

public interface CallAdapter<R, T> {
  // 请求方法返回类型
  Type responseType();
  // 返回一个OkHttpCall的委托实例,或者执行请求
  T adapt(Call<R> call);
  
  //作为一个抽象工厂,返回一个调用适配器
  abstract class Factory {
    public abstract @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations,
        Retrofit retrofit);
   ......
  }
}

Retrofit对RxJava做了适配,提供了RxJavaCallAdapterFactory工厂类。若不做设置的话,Retrofit提供了默认的工厂类实现DefaultCallAdapterFactory。在上文我们提到过当调用Retrofit的动态代理来调用对应的网络请求时,会最终回调到callAdapteradapt方法并传递一个OkHttpCall实例对象。获取到了该实例对象,我们就可以执行具体的网络请求,下面我们就实现一个简单的调用适配器工厂类和对应的适配器:

class KotlinCallAdapterFactory : CallAdapter.Factory() {
    override fun get(
        returnType: Type,
        annotations: Array<Annotation>,
        retrofit: Retrofit
    ): CallAdapter<*, *>? {
        val rawType = getRawType(returnType)

        check(rawType !is Call<*>){
            error("返回类型不对")
        }

        return KotlinCallAdapter(returnType)
    }
}

class KotlinCallAdapter(private val returnType: Type) : CallAdapter<Any, Any> {
    override fun responseType(): Type {
        return getParameterUpperBound(0, returnType as ParameterizedType)
    }

    override fun adapt(call: Call<Any>): Any {
       return call
    }

    private fun getParameterUpperBound(index: Int, type: ParameterizedType): Type {
        val types = type.actualTypeArguments
        require(!(index < 0 || index >= types.size)) { "Index " + index + " not in range [0," + types.size + ") for " + type }
        val paramType = types[index]
        return if (paramType is WildcardType) {
            paramType.upperBounds[0]
        } else paramType
    }
}

实现了KotlinCallAdapterFactory对象后,我们可以直接使用:

fun request() {
    retrofit.create(HttpService::class.java)
            .request1()
            .enqueue(object : Callback<ResponseData<List<WxArticle>>> {
                override fun onResponse(call: Call<ResponseData<List<WxArticle>>>, response: Response<ResponseData<List<WxArticle>>>) {
                    Log.i("MDY", "onResponse=" + Thread.currentThread().name)
                    Log.i("MDY", "onResponse: " + response.body().toString())
                }

                override fun onFailure(call: Call<ResponseData<List<WxArticle>>>, t: Throwable) {
                    Log.i("MDY", "onFailure: ")
                }
            })
}

这里只是举了一个简单的实现,具体的细节比如线程切换,参数判断,数据回调等都可以自由实现。通过对Retrofit自带的DefaultCallAdapterFactory和Rxjava的RxJavaCallAdapterFactory工厂类,都可以具体的参考并体会人家实现的细节和思路。

在DefaultCallAdapterFactory中通过Handler将返回数据post到主线程。

本来到这里Retrofit的流程就分析的差不多了,但我觉得还得加入一个Converter,该接口的作用在于将HTTP交互相关的请求、响应转换成具体的对象。

Converter

Converter可以被认为是数据转换器,具体的数据类型比如json、xml等需要对应的转换器去实现。在创建Retrofit对象时,我们可以通过addConverterFactory(GsonConverterFactory.create())方法传递不同的数据解析器,可以传递多个,内部会根据数据类型来解析数据。Retrofit中数据转换器的实现采用抽象工厂来实现,具体的工厂类的创建需要继承Factory类,并实现对应的responseBodyConverterrequestBodyConverter方法。

解析请求参数时,会调用requestBodyConverter方法获取一个Converter实例,调用convert方法转换成一个RequestBody请求体。OkHttpCall中获取到响应时,会调用responseBodyConverter方法获取一个Converter实例,调用convert方法将ResponseBody转换成对应的返回数据类型。

public interface Converter<F, T> {
  @Nullable T convert(F value) throws IOException;

  abstract class Factory {
    public @Nullable Converter<ResponseBody, ?> responseBodyConverter(Type type,
        Annotation[] annotations, Retrofit retrofit) {
      return null;
    }
    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;
    }
   ......
  }
}

看到上面是不是感觉非常类似,没错ConverterCallAdapter的实现设计一致,都是采用抽象工厂的方式,方便我们自由设置数据转换器和调用适配器,实现了高内聚低耦合的特点。

Converter基本情况下不需要我们去自定义,Retrofit中提供了丰富的Converter实现类:

  • GsonConverterFactory Gson转换器,是用于json数据格式。
  • SimpleXmlConverterFactory Xml转换器,已废弃。
  • JaxbConverterFactory 新的Xml转换器。 还有其他的类型,都可以看一看 Converter转换器实现类

Retrofit中提供了一种转换器的写法,通过在接口中声明请求的返回值类型,来动态制定转换器: JsonAndXmlConverters

总结

Retrofit中一次完整的流程包括以下几个步骤:

  1. Retrofit的创建,可以配置OkhttpClient、CallAdapter适配器、Converter转换器等。
  2. 调用create方法动态生成代理类发起网络请求,内部会调用到loadServiceMethod方法获取一个ServiceMethod对象。
  3. 经过ServiceMethodparseAnnotations来解析请求方法的注解、参数注解、返回类型等数据传递给HttpServiceMethod.
  4. HttpServiceMethod中会根据method、返回类型、注解等获取CallAdapterConveter实现类,并返回一个CallAdapted对象。
  5. 回调CallAdaptedinvoke方法,获取网络请求的封装类OkHttpCall对象。
  6. OkHttpCall对象会传递给CallAdapter对象来发起网络请求,并在OkHttpCall中调用Conveter解析响应。

Retrofit中通过大量的设计模式,将HTTP请求抽象成Java接口。通过注解 来描述和配置网络请求。通过动态代理来实现Service接口方法。通过抽象工厂模式,我们可以设置CallAdapter来实现请求的发起、线程的调度等操作。可以说Retrofit的代码体现了一种设计之美,对于源码的阅读,和设计模式的理解有很大的帮助。最后就到这里吧,后面我会随着Retrofit来进一步学习他的设计模式。