Retrofit源码解读并手撸一个Retrofit

学习目标:

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

如何使用Retrofit

一、添加依赖html

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

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

二、如何使用android

2.一、建立接口文件:WanAndroidApigit

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

2.二、构造Retrofit对象并发起一个简单的请求github

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.三、请求打印结果json

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用法作过多说明了。设计模式

源码分析

分析源码以前须要作些什么

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

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

  • 一、为何定义一个interface就能调用接口方法进行请求
  • 二、Retrofit内部究竟是如何工做的

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

从哪里开始分析源码

一、咱们通常都是先建立一个接口类:WanAndroidApi

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

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

二、而后经过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。

相关文章
相关标签/搜索