Retrofit的源码解读之Get请求
发布日期:2021-05-10 17:16:22 浏览次数:0 分类:技术文章

目录


简单的Http请求的几个步骤

1.构建Http请求的参数

2.Http请求不能放到主线程,所以需要子线程去执行请求

3.得到服务器请求之后,回调给上层UI

针对上面几步来分析下Retrofit是怎么实现该过程的

Retrofit的实现过程

1.创建Retrofit对象

用来构建Http的请求参数和请求地址以及根据不同的平台得到对应的请求执行器和回调执行器

2.使用封装OkHttp的Call的OkHttpCall进行Http同步或者请求

创建Retrofit对象

通过建造者模式Retrofit.build()创建Retrofit对象,在该创建的过程中,完成下面几个内容:

1)设置了Http请求依赖的架构默认的为OkHttp

2)根据不同的平台返回默认的回调执行器和请求适配器

3)设置数据转换器

    public Retrofit build() {      if (baseUrl == null) {        throw new IllegalStateException("Base URL required.");      }       //可以看出这里创建OkHttpClient对象      okhttp3.Call.Factory callFactory = this.callFactory;      if (callFactory == null) {        callFactory = new OkHttpClient();      }       //根据不同的平台设置服务器回调      Executor callbackExecutor = this.callbackExecutor;      if (callbackExecutor == null) {        callbackExecutor = platform.defaultCallbackExecutor();      }      // 设置回调的适配器 Make a defensive copy of the adapters and add the default Call adapter.      List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);//设置不同平台的执行Call的适配器      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));        //设置数据转换器 Make a defensive copy of the converters.      List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);      return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,          callbackExecutor, validateEagerly);    }

分别看下这几个过程的源码实现。

1)设置了Http请求依赖的架构默认的为OkHttp,实例化OkHttpClient对象

查看源码,创建 OkHttpClient对象的时候,我们可以看到在OkHttpClient里面主要完成下面两件事情:

    public Builder() {//实例化分发器      dispatcher = new Dispatcher();//。。。省略部分参数的设置//实例化连接池      connectionPool = new ConnectionPool();//。。。省略部分参数的设置    }
  • (1)实例化分发器Dispatcher

Dispatcher主要是用来进行分发请求,可参见

我们从源码中可以看到进行分发异步请求的线程池为单例模式,所以不用考虑线程池资源的问题,该线程池为一个无界的同步线程池,只要系统资源不耗尽,就可以一直新建线程。

 public synchronized ExecutorService executorService() {    if (executorService == null) {      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,          new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));    }    return executorService;  }

当然也可以通过构造函数传入自己的线程池,注意在传入自己的线程池的时候,必须能够满足getMaxRequests()配置的最多并发请求数。

 public Dispatcher(ExecutorService executorService) {    this.executorService = executorService;  }
  • (2)实例化连接池

在实例化ConnectionPool对象的时候,我们可以看到在默认构造函数中已经给到了默认的最大空闲连接数和保活时间,该默认设置主要适合于单用户的应用。

  /**   * Create a new connection pool with tuning parameters appropriate for a single-user application.   * The tuning parameters in this pool are subject to change in future OkHttp releases. Currently   * this pool holds up to 5 idle connections which will be evicted after 5 minutes of inactivity.   */  public ConnectionPool() {    this(5, 5, TimeUnit.MINUTES);  }

同样在ConnectionPool对象中创建了一个无界的同步线程池,只要系统资源不耗尽,就可以一直新建线程。同样这里创建的线程池采用的饿汉的单例模式

  private static final Executor executor = new ThreadPoolExecutor(0 /* corePoolSize */,      Integer.MAX_VALUE /* maximumPoolSize */, 60L /* keepAliveTime */, TimeUnit.SECONDS,      new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp ConnectionPool", true));

所以这样子在Dispatcher和 ConnectionPool中创建的线程池均为单例模式,在整个应用中会公共这两个线程池。

 

2)根据不同的平台返回默认的回调执行器和Call执行器

      // 设置回调的适配器 Make a defensive copy of the adapters and add the default Call adapter.      List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories);//设置不同平台的执行Call的适配器      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));

这两个值都是根据系统默认的返回对应的回调执行器和Call执行器

static class Android extends Platform {    @Override public Executor defaultCallbackExecutor() {//返回主线程中      return new MainThreadExecutor();    }    @Override CallAdapter.Factory defaultCallAdapterFactory(Executor callbackExecutor) {      return new ExecutorCallAdapterFactory(callbackExecutor);    }    static class MainThreadExecutor implements Executor {      private final Handler handler = new Handler(Looper.getMainLooper());      @Override public void execute(Runnable r) {        handler.post(r);      }    }  }

回调执行器为 MainThreadExecutor,从源码中可以看到就是在主线程中执行runnable对象。OkHttp在返回的服务器处理数据是在子线程中,在Android中处理UI需要在主线程中,所以通过该回调执行器将子线程回调到UI线程。

Call执行器为ExecutorCallAdapterFactory,主要工作就是将请求和回调结合到一起,在异步操作的时候将子线程处理完的结果回调到回调执行器所在的线程。

而这个数据转换器是传入到Call执行器中。具体的这两个执行器传入到Retrofit,然后发挥作用,主要在Retrofit的create()中体现出来。具体的后面的进行分析

static final class ExecutorCallbackCall<T> implements Call<T> {    final Executor callbackExecutor;    final Call<T> delegate;    //通过构造函数的方式传入回调执行器和Call    ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {      this.callbackExecutor = callbackExecutor;      this.delegate = delegate;    }    @Override public void enqueue(final Callback<T> callback) {      if (callback == null) throw new NullPointerException("callback == null");      delegate.enqueue(new Callback<T>() {        @Override public void onResponse(Call<T> call, final Response<T> response) {    //在异步操作的时候,将子线程切换成主线程          callbackExecutor.execute(new Runnable() {            @Override public void run() {              if (delegate.isCanceled()) {                // Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.                callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));              } else {                callback.onResponse(ExecutorCallbackCall.this, response);              }            }          });        }        @Override public void onFailure(Call<T> call, final Throwable t) {    //在异步操作的时候,将子线程切换成主线程          callbackExecutor.execute(new Runnable() {            @Override public void run() {              callback.onFailure(ExecutorCallbackCall.this, t);            }          });        }      });    }    @Override public Response<T> execute() throws IOException {      return delegate.execute();    }//。。。省略部分代码}

3)设置数据转换器

支持下面几种数据转换方式

数据格式 对应的Gradle依赖
Gson   'com.squareup.retrofit2:converter-gson:2.1.0'
Protobuf  'com.squareup.retrofit2:converter-protobuf:2.1.0'
XML  'com.squareup.retrofit2:converter-simplexml:2.1.0'
Scalars  'com.squareup.retrofit2:converter-scalars:2.1.0'
Jackson  'com.squareup.retrofit2:converter-jackson:2.1.0'
Moshi 'com.squareup.retrofit2:converter-moshi:2.1.0'

就是将OkHttp返回的数据结构转换上上述表格中支持的数据类型,这个转换操作在后面要介绍的ServiceMethod中。

创建请求网络

该过程主要就是通过Retrofit的Builder设置的一些内容,通过调用Retrofit的create()将上面设置的OkHttpClient、ExecutorCallAdapterFactory以及Converter结合到一起。

主要包括下面几个内容:

1)创建自定义的网络接口

2)通过动态代理解析出对应的请求,得到网络接口类

3)发送请求

1)创建自定义的网络接口 

ICategoryService service = retrofit.create(ICategoryService.class);

ICategoryService为自定义的网络接口,通过注解的方式添加了请求的地址和请求的参数以及包头等信息。

2)通过动态代理解析出对应的请求,得到网络接口类

 ICategoryService service = retrofit.create(ICategoryService.class);

看下下面的源码 

public <T> T create(final Class<T> service) {    Utils.validateServiceInterface(service);    if (validateEagerly) {      eagerlyValidateMethods(service);    }    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },        new InvocationHandler() {          private final Platform platform = Platform.get();          @Override public Object invoke(Object proxy, Method method, Object... args)              throws Throwable {            //...省略部分代码            //获取到注解对应的内容            ServiceMethod serviceMethod = loadServiceMethod(method);            //实例化OkHttpCall            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);            //最终调用到Retrofit,查看代码看到就是调用的之前Builder Retrofit的时候,            //传入的adapterFactories里面的那个CallAdapter            return serviceMethod.callAdapter.adapt(okHttpCall);          }        });  }

通过动态代理的方式获取到注解中的请求地址和参数,实例化OkHttpCall,同时然后跟进ServiceMethod中的代码发现ServiceMethod的callAdapter其实就是在创建Retrofit的时候,设置的 adapterFactories集合中的CallAdapterFactory,默认的为ExecutorCallAdapterFactory。调用adapt()之后, 返回对应的Call。

通过源码分析下这个过程

  • (1)解析注解对应的内容,获取到请求地址和参数
ServiceMethod serviceMethod = loadServiceMethod(method);

从loadServiceMethod()中可以看到每个被解析的请求地址和参数封装成的ServiceMethod是有一个缓存机制的,也就是说请求同一个请求,第二次的请求是直接从缓存中读取出来的。

  ServiceMethod loadServiceMethod(Method method) {    ServiceMethod result;    synchronized (serviceMethodCache) {      result = serviceMethodCache.get(method);      if (result == null) {        result = new ServiceMethod.Builder(this, method).build();        serviceMethodCache.put(method, result);      }    }    return result;  }

这里缓存集合采用了LinkedHashMap。

这里看下ServiceMethod这个类

(1.1)构造函数

    public Builder(Retrofit retrofit, Method method) {      this.retrofit = retrofit;      this.method = method;      this.methodAnnotations = method.getAnnotations();      this.parameterTypes = method.getGenericParameterTypes();      this.parameterAnnotationsArray = method.getParameterAnnotations();    }

就是解析出传入的方法对应的注解、参数类型等基本信息

(1.2)builder()来创建ServiceMethod对象

public ServiceMethod build() {//创建Call适配器      callAdapter = createCallAdapter();      responseType = callAdapter.responseType();//...省略代码//创建数据转换器      responseConverter = createResponseConverter();//解析注解的参数内容中的Http请求类型、url地址、包头信息      for (Annotation annotation : methodAnnotations) {        parseMethodAnnotation(annotation);      }//...省略代码      if (!hasBody) {//...省略代码      }//解析注解中对应的请求参数      int parameterCount = parameterAnnotationsArray.length;      parameterHandlers = new ParameterHandler<?>[parameterCount];      for (int p = 0; p < parameterCount; p++) {        Type parameterType = parameterTypes[p];        if (Utils.hasUnresolvableType(parameterType)) {          throw parameterError(p, "Parameter type must not include a type variable or wildcard: %s",              parameterType);        }        Annotation[] parameterAnnotations = parameterAnnotationsArray[p];        if (parameterAnnotations == null) {          throw parameterError(p, "No Retrofit annotation found.");        }        parameterHandlers[p] = parseParameter(p, parameterType, parameterAnnotations);      }//...省略代码      return new ServiceMethod<>(this);    }

 从builder()方法中主要就是创建Call适配器和数据转换器以及解析出通过注解设置的请求类型、url地址、包头信息、请求参数等。

从源码中跟踪createCallAdapter()和createResponseConverter()可以看到,其实这里就是得到Retrofit的builder过程中设置的callAdapterFactory和converterFactory

(1.3)ServiceMethod构造函数

  ServiceMethod(Builder<T> builder) {    this.callFactory = builder.retrofit.callFactory();    this.callAdapter = builder.callAdapter;    this.baseUrl = builder.retrofit.baseUrl();    this.responseConverter = builder.responseConverter;    //..省略代码  }

主要就是将之前Builder创建的时候设置的参数设置到ServiceMethod中。

(1.4)几个方法

Request toRequest(Object... args)

主要就是在OkHttpCall在创建Request请求的时候,根据之前设置的参数创造出适合OkHttp 的Request

 T toResponse(ResponseBody body)

将OkHttp返回的ResponseBody转换成之前设置的Converter对应的数据结构。

上面两个方法都是在OkHttpCall中进行调用

3)发送请求

    Call<CategoryBean> call = service.getCategory();    call.enqueue(new Callback<CategoryBean>() {        @Override        public void onResponse(Call<CategoryBean> call, Response<CategoryBean> response) {        }        @Override        public void onFailure(Call<CategoryBean> call, Throwable t) {        }    });

在2)通过动态代理解析网络请求的时候,我们也看到实例化的为OKHttpCall,在定义网络接口类的时候,对应的getCategory()返回的就是Call,所以真正的进行网络请求主要就是OKHttpCall。

上述方法就是其中OkHttpCall中对应的一些方法,就是对OkHttp的封装。在同步和异步请求的时候,在请求之前都会首先调用到刚才在介绍ServiceMethod的(1.4)小节中提到的将Retrofit的Request转换成OkHttp的Request,将OkHttp的Response转换成Retrofit设置的数据格式。

具体代码不在详细展开。

总结

看了好几遍Retrofit的源码,每一次都会有不同的收获。里面一些设计模式的运用,一些设计思想都值得自己在以后开发中运用。下一个想去看看OkHttp的几个拦截器的运用,特别是可以添加自定义的拦截器,看看在项目中怎么去运用。

 

 

 

 

上一篇:机器学习之入门研究(一)
下一篇:计算机网络·通俗理解RIP协议(距离向量算法计算)