Retrofit (2.9.0) 的使用及源码分析


一、Retrofit 是什么?

Github的原文是:A type-safe HTTP client for Android and the JVM. 一个适用于Android和JVM的类型安全的HTTP客户端。
在我看来是一个基于OkHttp,通过运用注解和动态代理的方法,实现网络请求的框架。

二、使用步骤(Android代码)

1.引入库

//retrofit版本
implementation 'com.squareup.retrofit2:retrofit:2.9.0' 
//retrofit的数据适配器,支持Gson、jackson、java8、scalar等等,也可以自定义
implementation 'com.squareup.retrofit2:converter-gson:2.9.0' 
//retrofit的请求适配器,支持java8、rxjava1、rxjava2、rxjava3等等,也可以自定义
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.9.0'

2.创建Retrofit实例

//配置OkHttpClient,可以参考OkHttp的教程配置项目中需要的功能
private val client by lazy {
    OkHttpClient.Builder()
        .build()
}

//配置Retrofit
private val retrofit by lazy {
    Retrofit.Builder()
        //配置baseUrl,可以通过反射等方式动态修改
        .baseUrl("https://www.wanandroid.com/") 
        //配置OkHttpClient客户端
        .client(client)
        //添加数据适配器工厂,如果不添加,默认工厂是BuiltInConverters(只能处理ResponseBody、Void、Unit类型)
        //如果是Java8+/Android API 24+ 还有一个OptionalConverterFactory
        .addConverterFactory(GsonConverterFactory.create())
        //添加请求适配器工厂,如果不添加,默认工厂是DefaultCallAdapterFactory(处理Call类型)
        //如果是Java8+/Android API 24+ 还有一个CompletableFutureCallAdapterFactory
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
        .build()
}

3.创建ApiService接口

interface WanAndroidApi {

    @GET("article/list/{page}/json")
    fun getHomeArticles(@Path("page") page: Int): Call<ArticleData> //默认请求方式

    @GET("banner/json")
    fun getBanners(): Observable<Response<BannerData>> //基于rxjava2方式,也可以把Response去掉,直接用Observable<BannerData>

    @GET("hotkey/json")
    suspend fun getHotkeys(): HotkeyData //基于Kotlin协程 挂起函数
}

4.进行网络请求

// 通过动态代理获取接口实例
val wanAndroidApi = retrofit.create(WanAndroidApi::class.java)

// 默认请求方式
val articleDataCall = wanAndroidApi.getHomeArticles(1)
articleDataCall.enqueue(object : Callback<ArticleData> {
    override fun onResponse(call: Call<ArticleData>, response: Response<ArticleData>) {
        //获得response对象,返回内容为response.body()
    }

    override fun onFailure(call: Call<ArticleData>, t: Throwable) {
    }
})
// 基于rxjava2请求方式
wanAndroidApi.getBanners()
    .subscribeOn(Schedulers.newThread())
    .subscribe { response ->
        //获得response对象,返回内容为response.body()
    }
// 基于Kotlin协程请求方式
GlobalScope.launch {
    val hotkeyData = wanAndroidApi.getHotkeys() //获得HotKeyData对象
}

三、源码分析

1.动态代理获取ApiService接口实例

从demo中可以看到ApiService接口是通过Retrofit的create()方法获取的,使用的是Java中动态代理的方法,通过Proxy.newProxyInstance动态获取

Retrofit.class

//获取ApiService实例
public <T> T create(final Class<T> service) {
  //检查service是否为Interface以及service及其父接口是否有泛型
  validateServiceInterface(service); 
  //动态代理返回接口对象
  return (T)
      Proxy.newProxyInstance(
          service.getClassLoader(),
          new Class<?>[] {service},
          new InvocationHandler() {//获取平台信息,此处获取的是Android平台,Platform中定义了一些默认值和方法,如默认的CallAdapterFactory、
            //ConverterFactory、CallBackExecutor,还有处理接口default方法的方法
            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;
              // 判断接口里定义的方法是否是default方法,如果是,按照default实现的内容调用,否则,
              // 调用loadServiceMethod().invoke(),其中loadServiceMethod()是获取方法
              // 从Java8开始支持接口中定义default方法和static方法
              return platform.isDefaultMethod(method)
                  ? platform.invokeDefaultMethod(method, service, proxy, args)
                  : loadServiceMethod(method).invoke(args);
            }
          });
}

//获取接口方法,此处对接口的方法做了一个缓存,为了确保线程安全,使用ConcurrentHashMap来存储
ServiceMethod<?> loadServiceMethod(Method method) {
  ServiceMethod<?> result = serviceMethodCache.get(method);
  if (result != null) return result;

  synchronized (serviceMethodCache) {
    result = serviceMethodCache.get(method);
    if (result == null) {
      //如果缓存中没有,则通过ServiceMethod的方法来获取
      result = ServiceMethod.parseAnnotations(this, method);
      serviceMethodCache.put(method, result);
    }
  }
  return result;
}

2.接口方法的解析、获取

ServiceMethod 是一个抽象类,用来获取解析Retrofit中定义的注解,组合成方法,通过抽象方法invoke(),返回接口方法的调用,实现类为HttpServiceMethod<ResponseT, ReturnT>

abstract class ServiceMethod<T> {
  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
    //获取请求信息工厂类
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
    ......
    //转给子类HttpServiceMethod获取
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }
  //调用接口定义的方法,由子类HttpServiceMethod实现,其实就是调用OkHttp进行网络请求
  abstract @Nullable T invoke(Object[] args);
}

先看RequestFactory如何获取

RequestFactory.class

static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
  //通过建造者模式返回了RequestFactory对象
  return new Builder(retrofit, method).build();
}
RequestFactory的内部类Builder.class

RequestFactory build() {
  for (Annotation annotation : methodAnnotations) {
    //解析方法的注解信息
    parseMethodAnnotation(annotation);
  }
  ......	
  int parameterCount = parameterAnnotationsArray.length;
  parameterHandlers = new ParameterHandler<?>[parameterCount];
  for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
    //解析参数的注解信息
    parameterHandlers[p] =
        parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
  }
  ......	
  return new RequestFactory(this);
}

//解析附加在方法上的注解信息,获取请求方式、路径、Header、
private void parseMethodAnnotation(Annotation annotation) {
  if (annotation instanceof DELETE) {
    parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
  } else if (annotation instanceof GET) {
    parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
  } else ......
}

//解析参数的注解信息,存储到ParameterHandler中
private @Nullable ParameterHandler<?> parseParameter(
    int p, Type parameterType, @Nullable Annotation[] annotations, boolean allowContinuation) {
  ParameterHandler<?> result = null;
  if (annotations != null) {
    for (Annotation annotation : annotations) {
      ParameterHandler<?> annotationAction =
          parseParameterAnnotation(p, parameterType, annotations, annotation);
      ......
    }
  }
	  ......
        //判断是否是协程方法 挂起函数
        if (Utils.getRawType(parameterType) == Continuation.class) {
          isKotlinSuspendFunction = true;
          return null;
        }
	  ......
  }
  return result;
}

通过RequestFactory.Builder类实现了RequestFactory各个属性的赋值。然后我们看HttpServiceMethod怎么获取接口方法的。

HttpServiceMethod.class
  
  //这里解释一下ResponseT和ReturnT,以Call<ArticleData>为例
  //ReturnT是Call<ArticleData>,ResponseT为ArticleData
  static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {
    boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
    boolean continuationWantsResponse = false;
    boolean continuationBodyNullable = false;

    //获取方法的ReturnType
    Annotation[] annotations = method.getAnnotations();
    Type adapterType;
    //区分是否为Kotlin协程
    if (isKotlinSuspendFunction) {
      Type[] parameterTypes = method.getGenericParameterTypes();
      Type responseType =
          Utils.getParameterLowerBound(
              0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]);
      if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
        // Unwrap the actual body type from Response<T>.
        responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);
        continuationWantsResponse = true;
      } else {
      }
      adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
      annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
    } else {
      adapterType = method.getGenericReturnType();
    }
    
    //获取请求适配器,通过adapterType,也就是返回类型,
    //去最开始添加的CallAdaperFactory的数组中匹配对应的CallAdapter,如Observable<T>对应RxJava2CallAdapter
    CallAdapter<ResponseT, ReturnT> callAdapter =
        createCallAdapter(retrofit, method, adapterType, annotations);
    Type responseType = callAdapter.responseType();
    ......

    //获取response数据转换器,通过responseType,去最开始添加的ConverterFactory的数组中匹配对应的Converter
    //如我们添加的GsonConverter
    Converter<ResponseBody, ResponseT> responseConverter =
        createResponseConverter(retrofit, method, responseType);

    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    //通过之前获取的requestFactory,callFactory,responseConverter,callAdapter 四个值
    //获取接口的方法,此处HttpServiceMethod细分了3个子类型
    if (!isKotlinSuspendFunction) { // 非Ktolin协程方式
      return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
    } else if (continuationWantsResponse) { //协程带Response类型
      //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
      return (HttpServiceMethod<ResponseT, ReturnT>)
          new SuspendForResponse<>(
              requestFactory,
              callFactory,
              responseConverter,
              (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
    } else {
      //noinspection unchecked Kotlin compiler guarantees ReturnT to be Object.
      return (HttpServiceMethod<ResponseT, ReturnT>) //协程只返回Body类型
          new SuspendForBody<>(
              requestFactory,
              callFactory,
              responseConverter,
              (CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
              continuationBodyNullable);
    }
  }

至此,接口方法的获取已经解释清楚了。

3.接口的调用

获取到接口方法后,当我们通过ApiService的实力去调用方法时,如wanAndroidApi.getHomeArticles(1).enqueue(…),就会调用我们获取的ServiceMethod对象的invoke方法,即HttpServiceMethod的invoke方法

HttpServiceMethod.class

  HttpServiceMethod(
      RequestFactory requestFactory,
      okhttp3.Call.Factory callFactory,
      Converter<ResponseBody, ResponseT> responseConverter) {
    this.requestFactory = requestFactory;
    this.callFactory = callFactory;
    this.responseConverter = responseConverter;
  }

  @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);

可以看到,这里生成了一个OkHttpCall对象,是Retrofit的Call接口的实现类,然后通过抽象的adapt方法进行适配,即交给3个子类CallAdapted<ResponseT, ReturnT>、SuspendForResponse、SuspendForBody 去实现。我们以CallAdapted子类为例

CallAdapted.class

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

然后发现又转给了CallAdapter去实现,以我们的第一个接口为例,ReturnType是Call,对应的CallAdapter是由DefaultCallAdapterFactory的get方法生成的,如下:

DefaultCallAdapterFactory.class

    @Nullable
    public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
        if (getRawType(returnType) != Call.class) {
            return null;
        } else if (!(returnType instanceof ParameterizedType)) {
            throw new IllegalArgumentException("Call return type must be parameterized as Call<Foo> or Call<? extends Foo>");
        } else {
            final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType)returnType);
            final Executor executor = Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class) ? null : this.callbackExecutor;
            //返回CallAdapter实例
            return new CallAdapter<Object, Call<?>>() {
                public Type responseType() {
                    return responseType;
                }
                public Call<Object> adapt(Call<Object> call) {
                    //此处使用代理模式,将OkHttpCall功能代理到了ExecutorCallbackCall类中。
                    return (Call)(executor == null ? call : new ExecutorCallbackCall(executor, call));
                }
            };
        }
    }

所以,当我们调用wanAndroidApi.getHomeArticles(1).enqueue(…)时,其实最后是走到了ExecutorCallbackCall的enqueue(…)方法。

ExecutorCallbackCall.class
    //异步请求
    public void enqueue(final Callback<T> callback) {
        Objects.requireNonNull(callback, "callback == null");
        //此处的delegate其实是OkHttpCall
        this.delegate.enqueue(new Callback<T>() {
            public void onResponse(Call<T> call, Response<T> response) {
                //通过配置的callbackExecutor将线程切回到MainThread
                ExecutorCallbackCall.this.callbackExecutor.execute(() -> {
                    if (ExecutorCallbackCall.this.delegate.isCanceled()) {
                        //此处的callback就是我们调用wanAndroidApi.getHomeArticles(1).enqueue(...)时传入的callback
                        callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
                    } else {
                        callback.onResponse(ExecutorCallbackCall.this, response);
                    }
                });
            }
            public void onFailure(Call<T> call, Throwable t) {
                ExecutorCallbackCall.this.callbackExecutor.execute(() -> {
                    callback.onFailure(ExecutorCallbackCall.this, t);
                });
            }
        });
    }

当然,此处只是针对Call这种默认的请求方式来解释源码,我们添加的Rxjava2CallAdapter或者其他的CallAdapter会有各自的adapt方法去实现请求,但都是代理了OkHttpCall去实现网络请求的。而OkHttpCall里其实是封装了OkHttp去进行的请求。

OkHttpCall.class

  //生成最终网络请求对象其实是okhttp3.Call
  private okhttp3.Call createRawCall() throws IOException {
    okhttp3.Call call = callFactory.newCall(requestFactory.create(args));//通过RequestFactory生成OkHttp的Request
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }

当我们配置了Converter后,OkHttpCall中会通过Converter转换成我们需要的Response

OkHttpCall.class

    //解析OkHttp访问接口返回的数据
	Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
	  ResponseBody rawBody = rawResponse.body();
	  ......
	  ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
	  try {
	    //通过我们设置的Converter提供的responseConverter去转换返回数据,返回我们需要的类型
	    T body = responseConverter.convert(catchingBody);
	    return Response.success(body, rawResponse);
	  } catch (RuntimeException e) {
	 ......
	  }
	}

至此,我们完成了对Retrofit框架的源码解析。
根据对源码的解析我们发现这个框架的重中之重就是CallAdapter的adapt(),这是真正实现网络请求的关键点。

总结

根据源码分析,总结一下Retrofit中使用的设计模式吧,对我们以后设计代码也有所帮助。

  1. 构造者模式(Builder) : 例如Retrofit的Builder。
    我们通过链式调用和步骤化的方式创建复杂对象。该模式将对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。
  2. 抽象工厂模式:例如CallAdapter.Factory。
    通常用于创建一组相关或依赖的对象,这些对象共同完成一项任务或工作。通过使用抽象工厂模式,可以确保创建的对象家族在逻辑上是一致的,并且能够相互协作。
  3. 适配器模式:例如CallAdapter
    用于解决两个不兼容接口之间的兼容性问题。适配器模式将一个类的接口转换成客户端所期望的另一个接口,从而使得原本由于接口不兼容而无法在一起工作的类能够协同工作。
  4. 代理模式:例如ExecutorCallbackCall
    可以用来控制对对象的访问、提供额外的功能、保护真实对象

以上内容属于个人见解,如有不对请留言指正,谢谢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ByeMoon丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值