阅读 198

Retrofit源码解读并手撸一个Retrofit

学习目标:

  • 搞明白Retrofit实现原理
  • 手撸一个简易版的Retrofit

如何使用Retrofit

1、添加依赖

implementation 'com.squareup.retrofit2:retrofit:(insert latest version)'
复制代码

备注:本文使用的是2.6.2版本

2、如何使用

2.1、创建接口文件:WanAndroidApi

        interface WanAndroidApi {
            @GET("banner/json")
            fun getBannerData(): Call<ResponseBody>
        }
复制代码

2.2、构造Retrofit对象并发起一个简单的请求

        val baseUrl = "https://www.wanandroid.com/"

        val retrofit = Retrofit.Builder().baseUrl(baseUrl).build()

        val wanAndroidApi = retrofit.create(WanAndroidApi::class.java)

        wanAndroidApi.getBannerData().enqueue(object : Callback<ResponseBody> {
            override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
                println("onFailure:   ${t.message}")

            }
            override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
                println("onResponse:   ${response.body()?.string()}")
            }
        })
复制代码

2.3、请求打印结果

        System.out: onResponse:   {"data":[{"desc":"Android高级进阶直播课免费学习","id":23,"imagePath":"https://wanandroid.com/blogimgs/67c28e8c-2716-4b78-95d3-22cbde65d924.jpeg","isVisible":1,"order":0,"title":"Android高级进阶直播课免费学习","type":0,"url":"https://url.163.com/4bj"},{"desc":"一起来做个App吧","id":10,"imagePath":"https://www.wanandroid.com/blogimgs/50c115c2-cf6c-4802-aa7b-a4334de444cd.png","isVisible":1,"order":0,"title":"一起来做个App吧","type":1,"url":"https://www.wanandroid.com/blog/show/2"},{"desc":"","id":6,"imagePath":"https://www.wanandroid.com/blogimgs/62c1bd68-b5f3-4a3c-a649-7ca8c7dfabe6.png","isVisible":1,"order":1,"title":"我们新增了一个常用导航Tab~","type":1,"url":"https://www.wanandroid.com/navi"},{"desc":"","id":20,"imagePath":"https://www.wanandroid.com/blogimgs/90c6cc12-742e-4c9f-b318-b912f163b8d0.png","isVisible":1,"order":2,"title":"flutter 中文社区 ","type":1,"url":"https://flutter.cn/"}],"errorCode":0,"errorMsg":""}
复制代码

单纯的Retrofit用法就是这么简单,几行代码实现一个网络请求,当然配合RxJava一起使用是近几年来最主流的网络架构,本文是解读Retrofit源码就不对RxJava用法做过多说明了。

源码分析

分析源码之前需要做些什么

阅读任何源码都要带着疑问去阅读,通过阅读去寻找答案,就能更深刻的理解源码。

笔者在使用的时候心中就有两个疑问:

  • 1、为什么定义一个interface就能调用接口方法进行请求
  • 2、Retrofit内部到底是如何工作的

接下来就让我们带着疑问去从源码中寻找答案吧。

从哪里开始分析源码

1、我们一般都是先创建一个接口类:WanAndroidApi

备注:这个接口里边是各种注解,关于注解的用法请移步什么是注解

        interface WanAndroidApi {
            @GET("banner/json")
            fun getBannerData(): Call<ResponseBody>
        }
复制代码

2、然后通过retrofit.create(WanAndroidApi::class.java)返回一个WanAndroidApi对象,然后调用这个对象的getBannerData()方法进行网络请求。

看到此处就会心有疑问,我们知道接口是不能直接创建对象的,为什么通过retrofit.create()之后就能创建WanAndroidApi对象呐,接下来我们看下create方法的源码

  public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    //看到Proxy似乎有点明白,是通过动态代理去处理的
    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);
            }
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
                    //重点1                  //重点2
            return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
          }
        });
  }
复制代码

retrofit核心就是使用动态代理创建接口代理对象进行处理的,关于动态代理的用法请移步Java 动态代理详解10分钟看懂动态代理设计模式

源码解析---重点1---loadServiceMethod(method)

  ServiceMethod<?> loadServiceMethod(Method method) {
    //首先从缓存中获取ServiceMethod
    ServiceMethod<?> result = serviceMethodCache.get(method);
    if (result != null) return result;

    synchronized (serviceMethodCache) {
      result = serviceMethodCache.get(method);
      //没有缓存过的话再去创建并存入到缓存
      if (result == null) {
        //重点3
        result = ServiceMethod.parseAnnotations(this, method);
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }
复制代码

通过缓存读取,没有缓存的话通过ServiceMethod.parseAnnotations创建出ServiceMethod对象,然后缓存起来,方便下次使用

源码解析---重点3---ServiceMethod.parseAnnotations(this, method)

  abstract class ServiceMethod<T> {
    static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
    //重点4: 通过RequestFactory.parseAnnotations解析注解中请求方法和参数信息封装到RequestFactory中
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);

    Type returnType = method.getGenericReturnType();
    if (Utils.hasUnresolvableType(returnType)) {
      throw methodError(method,
          "Method return type must not include a type variable or wildcard: %s", returnType);
    }
    if (returnType == void.class) {
      throw methodError(method, "Service methods cannot return void.");
    }
    //重点5:把RequestFactory传给HttpServiceMethod进一步解析
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }

  abstract @Nullable T invoke(Object[] args);
}

复制代码

通过RequestFactory.parseAnnotations(retrofit, method)解析注解中请求方法和参数信息封装到RequestFactory中

源码解析---重点5---HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory)

  static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
      Retrofit retrofit, Method method, RequestFactory requestFactory) {
   
    Annotation[] annotations = method.getAnnotations();
    ······
    //Retrofit的CallAdapter
    CallAdapter<ResponseT, ReturnT> callAdapter =
        createCallAdapter(retrofit, method, adapterType, annotations);
    ······
    Retrofit的ConverterAdapter
    Converter<ResponseBody, ResponseT> responseConverter =
        createResponseConverter(retrofit, method, responseType);

    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    if (!isKotlinSuspendFunction) {
      //重点6 
      return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
    }
    ······
  }
复制代码

源码解析---重点6---CallAdapted

    CallAdapted(RequestFactory requestFactory, okhttp3.Call.Factory callFactory,
        Converter<ResponseBody, ResponseT> responseConverter,
        CallAdapter<ResponseT, ReturnT> callAdapter) {
      //里边这是各种参数的赋值,看super方法即可,把上边所有解析出来的参数赋值到对应的对象
      super(requestFactory, callFactory, responseConverter);
      this.callAdapter = callAdapter;
    }
    
复制代码

至此这条分析路线1->3->4->5->6逻辑全部跟踪完毕,此时回到回到文中标记的重点1处,为了方便查看直接在下边贴出对应代码

            //重点1                  //重点2
    return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
复制代码

通过以上分析我们知道loadServiceMethod方法就是解析各种注解参数设置给相应对象,然后调用invoke方法,我们再来看看invoke方法做了什么

源码解析---重点2---invoke

  @Override final @Nullable ReturnT invoke(Object[] args) {
    //重点7
    Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
    return adapt(call, args);
  }
复制代码

invoke方法创建了OkHttpCall对象,我们看看OkHttpCall是什么东东

final class OkHttpCall<T> implements Call<T> {

  @Override public synchronized Request request() {
    okhttp3.Call call = rawCall;
    try {
      return (rawCall = createRawCall()).request();
    }
  }

  @Override public void enqueue(final Callback<T> callback) {
    ...
    call = rawCall = createRawCall();
    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
        Response<T> response;
        try {
          response = parseResponse(rawResponse);
        }
        callback.onResponse(OkHttpCall.this, response);
      }

      @Override public void onFailure(okhttp3.Call call, IOException e) {
        callFailure(e);
      }
      private void callFailure(Throwable e) {
        callback.onFailure(OkHttpCall.this, e);
      }
    });
  }
  @Override public Response<T> execute() throws IOException {
    okhttp3.Call call;
    ...
    call = rawCall = createRawCall();
    return parseResponse(call.execute());
  }

  private okhttp3.Call createRawCall() throws IOException {
    okhttp3.Call call = callFactory.newCall(requestFactory.create(args));

    return call;
  }

}
复制代码

OkHttpCall实现了Call接口方法,在里边是通过okhttp进行网络请求,并把请求结果通过callback回调出去,至此整个流程已经全部分析完毕

通过上边对源码的分析我们可以总结retrofit的几个关键点:

  • 通过注解方式把请求path、参数等信息标记在xxxApi接口中;
  • retrofit.create里边的动态代理方法返回xxxApi的代理对象;
  • 代理方法内部进行注解解析,并构建出okhttp对象方式请求,返回call对象;
  • 调用xxxApi的接口方法返回的call对象通过callback拿到请求结果;

接下来就我们就仿照retrofit的实现方式手撸一个简易版的retrofit试试

手撸一个Retrofit

参照Retrofit我们需要定一个请求的注解方法 以get方法为例

@kotlin.annotation.Target(AnnotationTarget.FUNCTION)
@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
annotation class GET(
    val value: String = ""
)
复制代码

有了GET请求注解我们就可以定义我们的接口api了,细心的你会发下接口里边需要一个返回值Call,那我们就定义一个Call,代码如下

//参照Rerofit的Call,我们只写一个方法enqueue即可
interface Call<T> {
    fun enqueue(callBack: CallBack<T>)
}
复制代码

这里又用到了CallBack,那继续写一个CallBack接口

interface CallBack<T> {
    //请求成功回调
    fun onResponse(response: Response?)
    //请求失败回调
    fun onFailure(t: Throwable)
}
复制代码

有了以上定义的方法我们就可以写ApiService

interface MyApiService {
    @GET("banner/json")
    fun getBannerData():Call<ResponseBody>
}
复制代码

定义好ApiService接口之后就要创建一个我们自己的retrofit

创建MyRetrofit类

class MyRetrofit(private val baseUrl: String?) {
    //也使用Builder模式构建retrofit对象
    class Builder {
        var baseUrl: String? = null
        fun setBaseUrl(baseUrl: String): Builder {
            this.baseUrl = baseUrl
            return this
        }

        fun build(): MyRetrofit {
            return MyRetrofit(baseUrl)
        }
    }
    //参照retrofit的源码使用Proxy.newProxyInstance动态代理处理
    fun <T> create(service: Class<T>): T {
        return Proxy.newProxyInstance(
            service.classLoader,
            arrayOf(service),

            InvocationHandler { any, method, arrayOfAnys ->
                var call: Call<T>? = null
                val annotations = method.declaredAnnotations
                for (annotation in annotations) {
                    call = parseHttpMethodAndPath(annotation)
                }

                return@InvocationHandler call

            }
        ) as T
    }

    private fun <T> parseHttpMethodAndPath(annotation: Annotation): Call<T>? {
        var call: Call<T>? = null
        if (annotation is GET) {
            val path = annotation.value
            val url = "$baseUrl$path"

            println("url= $url")

            val okHttpClient: OkHttpClient = OkHttpClient().newBuilder().build()
            val request: Request = Request.Builder().url(url).build()
            call = object : Call<T> {
                override fun enqueue(callBack: CallBack<T>) {
                    okHttpClient.newCall(request).enqueue(object : Callback {
                        override fun onFailure(call: okhttp3.Call, e: IOException) {
                            callBack.onFailure(e)
                        }

                        override fun onResponse(call: okhttp3.Call, response: Response) {
                            callBack.onResponse(response)
                        }
                    })
                }
            }
        }
        return call
    }
}
复制代码

在判断是不是GET请求,是的话解析注解path和参数等信息,拼接一个完整的url,然后构建出okhttp对象进行网络请求,把结果回调给callback 至此简易版的retrofit已经写完了,接下来我们就验证一下是否能请求成功

        val myRetrofit = MyRetrofit.Builder().setBaseUrl(baseUrl).build()
        val apiService = myRetrofit.create(MyApiService::class.java)

        apiService.getBannerData().enqueue(object : CallBack<ResponseBody> {
            override fun onResponse(response: okhttp3.Response?) {
                println("MyRetrofit---->onResponse:   ${response?.body()?.string()}")
            }
            override fun onFailure(t: Throwable) {
                println("MyRetrofit---->onFailure:   ${t.message}")
            }
        })
复制代码

请求结果如下:

System.out: MyRetrofit---->onResponse:   {"data":[{"desc":"Android高级进阶直播课免费学习","id":23,"imagePath":"https://wanandroid.com/blogimgs/67c28e8c-2716-4b78-95d3-22cbde65d924.jpeg","isVisible":1,"order":0,"title":"Android高级进阶直播课免费学习","type":0,"url":"https://url.163.com/4bj"},{"desc":"一起来做个App吧","id":10,"imagePath":"https://www.wanandroid.com/blogimgs/50c115c2-cf6c-4802-aa7b-a4334de444cd.png","isVisible":1,"order":0,"title":"一起来做个App吧","type":1,"url":"https://www.wanandroid.com/blog/show/2"},{"desc":"","id":6,"imagePath":"https://www.wanandroid.com/blogimgs/62c1bd68-b5f3-4a3c-a649-7ca8c7dfabe6.png","isVisible":1,"order":1,"title":"我们新增了一个常用导航Tab~","type":1,"url":"https://www.wanandroid.com/navi"},{"desc":"","id":20,"imagePath":"https://www.wanandroid.com/blogimgs/90c6cc12-742e-4c9f-b318-b912f163b8d0.png","isVisible":1,"order":2,"title":"flutter 中文社区 ","type":1,"url":"https://flutter.cn/"}],"errorCode":0,"errorMsg":""}
复制代码

结果符合我们的预期,证明我们前面的分析是正确的!

最后

可以看看我们只创建了几个类写了几行代码就可以像使用Retrofit一样的用法进行网络请求了,当然示例代码只是实现了一个最基础的,毕竟源码还是很复杂的,他们考虑了扩展性,易用性等,里边各种封装各种设计模式随处可见,是一个非常值得学习的库,感谢Retrofit团队的付出。不管他封装的再复杂,万变不离其宗,我们掌握了它的实现原理以后再项目中遇到问题就能做到心中有数,做到知己知彼,方能解出bug。

关注下面的标签,发现更多相似文章
评论