三方库源码学习2-Retrofit

本文详细介绍了Retrofit的工作原理,包括动态代理的应用、Retrofit的创建过程、ServiceMethod的解析、Call的适配与转换、响应结果的解析以及请求参数的处理。重点讲解了Retrofit如何利用动态代理生成接口实现类,通过ServiceMethod构建请求,以及数据转换和回调的执行流程。同时,文章还探讨了Retrofit在Android平台的特殊实现,如主线程回调和平台适配。
摘要由CSDN通过智能技术生成

一.参考博客

三方库源码学习-1 :OkHttp
一定能看懂的 Retrofit 最详细的源码解析!
三方库源码笔记(7)-Retrofit 源码详解
又看一遍Retrofit源码,这次写了篇笔记

二.Retrofit的介绍

官方介绍: A type-safe HTTP client for Android and Java. 这说明 Retrofit 的内部实现并不需要依赖于 Android 平台,而是可以用于任意的 Java 客户端,Retrofit 只是对 Android 平台进行了特殊实现而已。

使用:
可以参考官网:Retrofit
看我之前的博客:Andrioid网络请求—— Retrofit的简单使用

这里给出一个小例子:
版本基于2.9.0


```java
public interface GitHubService {
    @GET("users/{user}/repos")
    Call<List<Repo>> listRepos(@Path("user") String user);
}
public class RetrofitRequest {
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("https://api.github.com/")
            .addConverterFactory(GsonConverterFactory.create())
            .build();

    GitHubService service = retrofit.create(GitHubService.class);
    Call<List<Repo>> repos = service.listRepos("octocat");
}

三.什么是动态代理

在介绍Retrofit之前,先插播一下动态代理

动态 指的是在运行期,而 代理 指的是实现了接口的类,实现了接口的方法,称之为代理。有时候,对于某个既定的interface,我们不希望直接声明并使用其实现类,而是希望实现类可以动态生成,此时就可以通过 Proxy.newProxyInstance 来实现目的。

看一个简单的代理模式的例子。

interface FaceDemo {
   
    fun run(user : String)
}
fun main(args: Array<String>) {
   

    val request = Proxy.newProxyInstance(
        FaceDemo::class.java.classLoader,
        arrayOf<Class<*>>(FaceDemo::class.java),
        object : InvocationHandler{
   
            override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any {
   
                return println("method = ${method?.name} args : ${args?.get(0)} ")
            }
        }
    )  as FaceDemo

    println(request.javaClass)
    //调用方法
    request.run("liHua")
}

运行结果:

可以看到:

  • InvocationHandler.invoke() 方法拦截了我们调用的接口中的方法。
  • 在运行时这个类是一个 $Proxy0 的类。

实际上通过代理模式,我们定义的接口会动态的创建出实现类也就是 $Proxy0 类,它大概长下面这个样子。

class $Proxy0 extends Proxy implements FaceDemo {
   

    protected $Proxy0(InvocationHandler h) {
   
        super(h);
    }
	private static Method m3;
    static 
    {
   
        m3 = Class.forName("FaceDemo").getMethod("FaceDemo", new Class[] {
   
            Class.forName("java.lang.String"),
    	});
    }
    
    @Override
    public void run(String user) {
   
    	/* h就是我们自己创建的InvocationHandler,所以最终就回调到
    	它里面的invoke方法内执行
    	*/
        super.h.invoke(this, m3, new Object[]{
   user});
    }
}

四. Retrofit.Create()方法

create() 方法往下深入。


```java
public <T> T create(final Class<T> service) {
    validateServiceInterface(service);  // 1
    return (T)		//2
        Proxy.newProxyInstance(
            service.getClassLoader(),
            new Class<?>[] {service},
            new InvocationHandler() {
              @Override
              public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
                  throws Throwable {
                  ......
                return platform.isDefaultMethod(method)
                    ? platform.invokeDefaultMethod(method, service, proxy, args)
                    : loadServiceMethod(method).invoke(args);
              }
            });
  }

注释1:这个是用来验证我们定义的GitHubService是一个接口,且不是泛型接口,并且会判断是否进行提前验证,提前验证可以更好的把错误暴漏在编译器,但是比较耗时,可能造成卡顿。

注解2:这里其实就是用了动态代理来处理的。借助动态代理拿到接口的实现类,当调用接口的方法时,会回调到它里面的 invoke() 方法。并在 invoke() 方法中拿到声明listRepos() 时标注的各个配置项。

这个方法的关键代码就是loadServiceMethod(method).invoke(args) 。主要逻辑就是把method对象转换为ServiceMethod对象,并做缓存。在下次使用相同的请求方法时,直接从缓存池中取ServiceMethod对象,优化了性能。

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

ServiceMethod<?> loadServiceMethod(Method method) {
   
    ServiceMethod<?> result = serviceMethodCache.get(method);
    //2
    if (result != null) return result;

    synchronized (serviceMethodCache) {
   
      result = serviceMethodCache.get(method);
      if (result == null) {
   
      //3
        result = ServiceMethod.parseAnnotations(this, method);
        //4
        serviceMethodCache.put(method, result);
      }
    }
    return result;
  }

注解1:创建一个Map集合,把ServiceMethod对象缓存起来,因为同一个请求方法可能会被调用多次,缓存提升性能。

注解2:如果该方法已经被缓存过了,直接用原来缓存的ServiceMethod

注解3:将每个代表接口方法的 method 对象转换为 ServiceMethod 对象,该对象中就包含了接口方法的具体信息。

注解4:把拿到的ServiceMethod对象缓存进集合中。

五. ServiceMethod

刚才的loadServiceMethod(method) 方法返回的就是一个ServiceMethod。它是一个抽象类,主要的逻辑如下:

  1. 拿到 RequestFactory对象,这个是用来解析注解的。后面再讲。
  2. 通过HttpServiceMethod.parseAnnotations 来获取ServiceMethod的实现类。

这里运用了工厂模式,由于最终的网络请求方法可能是多种多样的,既可以通过线程池来执行,又可以通过Kotlin协程来执行。使用工厂模式就可以把这种差异隐藏在不同的ServiceMethod实现类里。而外部都是通过parseAnnotations来获取实现类。

abstract class ServiceMethod<T> {
   
  static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
   
	  //1 这里拿到了requestFactory对象,它是用来解析注解的
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);

    Type returnType = method.getGenericReturnType();
   	.......
	//2  使用工厂模式拿到ServiceMethod实现类
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
  }

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

注意这里还有一个抽象方法 invoke ,而在刚才动态代理的invoke方法中,最终调用的就是一个invoke方法,所以这个方法的实现很关键。

六. 初看HttpServiceMethod

HttpServiceMethod 是 ServiceMethod的唯一直接子类。而它本身也是一个抽象类。我们先顺着刚才的思路往下走,等会在来看这个类。

现在主要看一下 invoke 方法。主要的逻辑是:

  1. 创建OkHttpCall对象。这是实际进行网络请求的类。
  2. 把刚才拿到的 Call < ResponseT > 对象通过adapt进行适配为我们需要的Call并返回。

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

我们先看OkHttpCall这个类。

七. OkHttpCall

这个类是 retrofit2.Call 的实现类,是真正发起OkHttp请求的地方。当我们调用 call.enqueue(Callback) 方法时,最终就会调用到这里的OkHttpCall.enque方法。

  • OkHttpCall.enqueue
@Override
  public void enqueue(final Callback<T> callback) {
   
   	 okhttp3.Call call;
      call = rawCall;
      failure = creationFailure;
      if (call == null && failure == null) {
   
      	//1:创建了okhttp3.Call call
          call = rawCall = createRawCall();
      }
 	 //调用的是okHttp3.call.enqueue,所以这里的代码是发生在OkHttp中的
    call.enqueue(
        new okhttp3.Callback() {
   
          @Override
          public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
   
            Response<T> response;
         
            //2 :对拿到的结果进行解析
              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)}
        });
  }

这个方法的主要逻辑就是创建出OkHttp的Call,并调用它的enqueue方法发起请求。最后对响应结果进行处理。并调用回调接口把结果返回。

  • OkHttpCall.execute
public Response<T> execute() 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值