Retrofit源码分析

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介:

目录介绍

  • 1.首先回顾Retrofit简单使用方法
  • 2.Retrofit的创建流程源码分析
    • 2.1 Retrofit对象调用Builder()源码解析
    • 2.2 Retrofit对象调用baseUrl(url)源码解析
    • 2.3 addConverterFactory(Converter.Factory factory)源码分析
    • 2.4 addCallAdapterFactory(RxJava2CallAdapterFactory.create())源码分析
    • 2.5 client(okHttpClient)源码分析
    • 2.6 Retrofit对象调用build()源码解析
  • 3.创建ServiceMethod流程源码分析
    • 3.1 首先看看请求网络代码过程
    • 3.2 分析create(final Class service)源码
    • 3.3 serviceMethod对象的创建过程
  • 4.注解的解析
    • 4.1 callAdapter的创建源码分析
    • 4.2 responseConverter的创建源码分析
  • 5.OkHttpCall的创建源码分析
    • 5.1 new OkHttpCall<>(serviceMethod, args)源码分析
  • 6.OkHttpCall的网络请求
    • 6.1 OkHttpCall.execute()同步请求
    • 6.2 OkHttpCall.enqueue()异步请求
    • 6.3 parseResponse解析网络数据源码解析

好消息

  • 博客笔记大汇总【16年3月到至今】,包括Java基础及深入知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇到的bug汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是markdown格式的!同时也开源了生活博客,从12年起,积累共计47篇[近20万字],转载请注明出处,谢谢!
  • 链接地址:https://github.com/yangchong211/YCBlogs
  • 如果觉得好,可以star一下,谢谢!当然也欢迎提出建议,万事起于忽微,量变引起质变!
  • 关于Retrofit的使用,可以参考这篇文章:https://www.jianshu.com/p/989c46a858a4

0.思考问题,针对以下问题,看了这篇博客,应该有了初步的认识

  • 0.0.1 Retrofit创建中用了哪些设计模式,请谈谈使用这些设计模式的优势?
  • 0.0.2 Retrofit在创建的时候为什么要判断是否在Android环境中,是如何做到的?
  • 0.0.3 为什么设置baseUrl的时候,会以/结尾,如果没有/会出现什么问题?
  • 0.0.4 addConverterFactory的主要作用是什么?
  • 0.0.5 Factory生产的是CallAdapter,那么CallAdapter又是什么呢?
  • 0.0.6 网络请求的类 service为什么要定义成接口?如果不定义成接口会出现什么情况?
  • 0.0.7 创建了ServiceMethod对象是干什么用的?它是用什么进行存储的?
  • 0.0.8 创建ServiceMethod对象为什么要添加synchronized同步锁
  • 0.0.9 call调用enqueue异步方法中源码是如何实现异步切换线程的?原理是怎样的?
  • 0.1.0 ServiceMethod是如何保存网络请求所需要的数据,具体保存了哪些数据呢?
  • 0.1.1 网络传输都是二进制流,那么解析数据时,如何通过ServiceMethod使用Converter转换成Java对象进行数据解析
      //AdvertCommon是javabean实体类,并没有序列化,那么网络解析数据如何解析java对象呢?
      Call<AdvertCommon> getSplashImage(@Query("type") int type);
    
  • 0.1.2 如下所示,为什么说apiService对象实际上是动态代理对象,而不是真正的网络请求接口创建的对象
      ApiService apiService = retrofit.create(ApiService.class);
    
  • 0.1.3 如何理解动态代理的机制。retrofit是如何加载接口类ApiService的,为什么这个类要设置成接口?

1.首先回顾Retrofit简单使用方法

  • Api接口
      public interface DouBookApi {
          /**
          * 根据tag获取图书
          * @param tag  搜索关键字
          * @param count 一次请求的数目 最多100
          *              https://api.douban.com/v2/book/search?tag=文学&start=0&count=30
          */
          @GET("v2/book/search")
          Observable<DouBookBean> getBook(@Query("tag") String tag,
                                          @Query("start") int start,
                                          @Query("count") int count);
      }
    
  • Model类

      public class DouBookModel {
    
          private static DouBookModel bookModel;
          private DouBookApi mApiService;
    
          public DouBookModel(Context context) {
              mApiService = RetrofitWrapper
                      .getInstance(ConstantALiYunApi.API_DOUBAN)   //baseUrl地址
                      .create(DouBookApi.class);
          }
    
          public static DouBookModel getInstance(Context context){
              if(bookModel == null) {
                  bookModel = new DouBookModel(context);
              }
              return bookModel;
          }
    
          public Observable<DouBookBean> getHotMovie(String tag, int start , int count) {
              Observable<DouBookBean> book = mApiService.getBook(tag, start, count);
              return book;
          }
      }
    
  • 抽取类

      public class RetrofitWrapper {
    
          private static RetrofitWrapper instance;
          private Retrofit mRetrofit;
    
          public RetrofitWrapper(String url) {
              OkHttpClient.Builder builder = new OkHttpClient.Builder();
    
              //打印日志
              HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
              logging.setLevel(HttpLoggingInterceptor.Level.BODY);
              builder.addInterceptor(logging).build();
              OkHttpClient client = builder.addInterceptor(new LogInterceptor("HTTP")).build();
    
              //解析json
              Gson gson = new GsonBuilder()
                      .setLenient()
                      .create();
    
              mRetrofit = new Retrofit
                      .Builder()
                      .baseUrl(url)
                      .addConverterFactory(GsonConverterFactory.create(gson))
                      .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                      .client(client)
                      .build();
          }
    
          public  static RetrofitWrapper getInstance(String url){
              //synchronized 避免同时调用多个接口,导致线程并发
              synchronized (RetrofitWrapper.class){
                  instance = new RetrofitWrapper(url);
              }
              return instance;
          }
    
          public <T> T create(final Class<T> service) {
              return mRetrofit.create(service);
          }
      }
    
  • 使用

      DouBookModel model = DouBookModel.getInstance(activity);
      model.getHotMovie(mType,start,count)
              .subscribeOn(Schedulers.io())
              .observeOn(AndroidSchedulers.mainThread())
              .subscribe(new Subscriber<DouBookBean>() {
                  @Override
                  public void onCompleted() {
    
                  }
    
                  @Override
                  public void onError(Throwable e) {
    
                  }
    
                  @Override
                  public void onNext(DouBookBean bookBean) {
    
                  }
              });
    
  • 针对Retrofit,需要注意
    • Retrofit 是一个 RESTful 的 HTTP 网络请求框架的封装。网络请求的工作本质上是 OkHttp 完成,而 Retrofit 仅负责 网络请求接口的封装。看下图所示,摘自网络
    • image

2.Retrofit的创建流程源码分析

2.1 Retrofit对象调用Builder()源码解析

  • 首先看看里面的源代码,如下所示
    • 可以看到Platform.get()获取的是单利对象。那么也许你会问,这个方法的作用主要是什么呢?通过Class.forName获取类名的方式,来判断当前的环境是否在Android中,这在之后获取默认的CallAdapterFactory时候将会用到。下面我会分析到……
    • 关于单利设计模式,如果还有疑问,或者想知道所有的获取单利的方法,可以参考我的这篇博客:设计模式之一:单例模式
      ```
      //第一步
      public Builder() {
      this(Platform.get());
      }
//第二步,追踪到Platform类中
private static final Platform PLATFORM = findPlatform();
static Platform get() {
    return PLATFORM;
}

private static Platform findPlatform() {
try {
  Class.forName("android.os.Build");
  if (Build.VERSION.SDK_INT != 0) {
    //此处表示:如果是Android平台,就创建并返回一个Android对象
    return new Android();
  }
} catch (ClassNotFoundException ignored) {
}
try {
  Class.forName("java.util.Optional");
  return new Java8();
} catch (ClassNotFoundException ignored) {
}
return new Platform();
}
```
  • 然后看一下new Android()是做了什么?

      static class Android extends Platform {
          @Override public Executor defaultCallbackExecutor() {
              // 返回一个默认的回调方法执行器
              // 该执行器作用:切换线程(子->>主线程),并在主线程(UI线程)中执行回调方法
            return new MainThreadExecutor();
          }
    
          @Override CallAdapter.Factory defaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
            if (callbackExecutor == null) throw new AssertionError();
             // 创建默认的网络请求适配器工厂 
             // 该默认工厂生产的 adapter 会使得Call在异步调用时在指定的 Executor 上执行回调 
             // 采用了策略模式
            return new ExecutorCallAdapterFactory(callbackExecutor);
          }
    
          static class MainThreadExecutor implements Executor {
          // 获取与Android 主线程绑定的Handler 
            private final Handler handler = new Handler(Looper.getMainLooper());
    
            @Override public void execute(Runnable r) {
             // 该Handler是上面获取的与Android 主线程绑定的Handler 
              // 在UI线程进行对网络请求返回数据处理等操作。
              handler.post(r);
            }
          }
      }
    

2.2 Retrofit对象调用baseUrl(url)源码解析

  • 都知道这个方法主要是设置baseUrl。源码如下所示
    • 首先先对baseUrl进行非空判断。然后再解析baseUrl,如果解析的httpUrl为null,则会抛出IllegalArgumentException非法参数异常。那么思考一下,什么情况下解析baseUrl会导致解析内容httpUrl为null呢?
      public Builder baseUrl(String baseUrl) {
      checkNotNull(baseUrl, "baseUrl == null");
      HttpUrl httpUrl = HttpUrl.parse(baseUrl);
      if (httpUrl == null) {
        throw new IllegalArgumentException("Illegal URL: " + baseUrl);
      }
      return baseUrl(httpUrl);
      }
      
  • HttpUrl是如何解析url,对url有什么条件,来看一下parse方法源码

    • 可以看到url必须是以http或者https才可以。如果随便写一个url,则会出问题

      public static @Nullable HttpUrl parse(String url) {
        Builder builder = new Builder();
        Builder.ParseResult result = builder.parse(null, url);
        return result == Builder.ParseResult.SUCCESS ? builder.build() : null;
      }
      
      ParseResult parse(@Nullable HttpUrl base, String input) {
      int pos = skipLeadingAsciiWhitespace(input, 0, input.length());
      int limit = skipTrailingAsciiWhitespace(input, pos, input.length());
      
      // Scheme.
      int schemeDelimiterOffset = schemeDelimiterOffset(input, pos, limit);
      if (schemeDelimiterOffset != -1) {
        if (input.regionMatches(true, pos, "https:", 0, 6)) {
          this.scheme = "https";
          pos += "https:".length();
        } else if (input.regionMatches(true, pos, "http:", 0, 5)) {
          this.scheme = "http";
          pos += "http:".length();
        } else {
          return ParseResult.UNSUPPORTED_SCHEME; // Not an HTTP scheme.
        }
      } else if (base != null) {
        this.scheme = base.scheme;
      } else {
        return ParseResult.MISSING_SCHEME; // No scheme.
      }
      
      //下面代码省略了
      
  • 思考一下,传递的url为什么是String BASE_URL = "http://beta.goldenalpha.com.cn/"这个格式呢?接着看看baseUrl(httpUrl)源码
    • 可以看到这里的url地址必须是以/结尾。所以如果是没有加上/,则会出现异常
      public Builder baseUrl(HttpUrl baseUrl) {
      checkNotNull(baseUrl, "baseUrl == null");
      List<String> pathSegments = baseUrl.pathSegments();
      if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
        throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
      }
      this.baseUrl = baseUrl;
      return this;
      }
      
  • 其实个人感觉这块工作并不难,只要有点英文基础的,就可以完全看的明白。接着往下分析,如果想了解更多,欢迎看我的博客汇总:https://github.com/yangchong211/YCBlogs

2.3 addConverterFactory(Converter.Factory factory)源码分析

  • 在创建的时候会调用addConverterFactory(GsonConverterFactory.create(JsonUtils.getJson()))添加Gson转换器

    • 这个方法主要是添加用于对象序列化和反序列化的转换器工厂
    • 将上面创建的GsonConverterFactory放入到 converterFactories数组

      .addConverterFactory(GsonConverterFactory.create(JsonUtils.getGson()))
      
      //看这行代码
      public Builder addConverterFactory(Converter.Factory factory) {
        //将上面创建的GsonConverterFactory放入到 converterFactories数组
      converterFactories.add(checkNotNull(factory, "factory == null"));
      return this;
      }
      
  • 然后看看GsonConverterFactory.creat()方法源码
    • 使用{@code gson}创建一个实例以进行转换。编码到JSON并从JSON解码(当没有由头指定字符集时)将使用UTF-8。
    • 创建了一个含有Gson对象实例的GsonConverterFactory,并返回给addConverterFactory()
      public static GsonConverterFactory create(Gson gson) {
        if (gson == null) throw new NullPointerException("gson == null");
        return new GsonConverterFactory(gson);
      }
      

2.4 addCallAdapterFactory(RxJava2CallAdapterFactory.create())源码分析

  • 添加一个调用适配器工厂,用于支持服务方法返回类型
      public Builder addCallAdapterFactory(CallAdapter.Factory factory) {
        callAdapterFactories.add(checkNotNull(factory, "factory == null"));
        return this;
      }
    
  • CallAdapterFactory:注意Factory生产的是CallAdapter,那么CallAdapter又是什么呢?
    • 可以看到CallAdapter源代码如下所示,它是一个接口。主要作用是:将响应类型{@代码R}的{@链接调用}改编为{@代码T}的类型。实例由{@LinkplanFactory(一个工厂)创建,该工厂}是{@Link平原Retrofit.Builder#addCallAdapterFactory(Factory)已安装}到{@LinkRetroflit}实例中。
    • 网络请求执行器(Call)的适配器,并且在Retrofit中提供了三种CallAdapterFactory: ExecutorCallAdapterFactory(默认)、DefaultCallAdapterFactory、RxJava2CallAdapterFactory
      public interface CallAdapter<R, T> {
      Type responseType();
      T adapt(Call<R> call);
      abstract class Factory {
        public abstract @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations,
            Retrofit retrofit);
        protected static Type getParameterUpperBound(int index, ParameterizedType type) {
          return Utils.getParameterUpperBound(index, type);
        }
        protected static Class<?> getRawType(Type type) {
          return Utils.getRawType(type);
        }
      }
      }
      
  • 接着,有伙伴可能会问它的作用是什么呢?
    • 将默认的网络请求执行器(OkHttpCall)转换成适合被不同平台来调用的网络请求执行器形式
    • 一开始Retrofit只打算利用OkHttpCall通过ExecutorCallbackCall切换线程;但后来发现使用Rxjava更加方便(不需要Handler来切换线程)。想要实现Rxjava的情况,那就得使用RxJavaCallAdapterFactoryCallAdapter将OkHttpCall转换成Rxjava(Scheduler)
      .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
      

2.5 client(okHttpClient)源码分析

  • 用于请求的HTTP客户端

    • 指定用于创建{@link Call}实例的自定义调用工厂。

      public Builder client(OkHttpClient client) {
      return callFactory(checkNotNull(client, "client == null"));
      }
      
      public Builder callFactory(okhttp3.Call.Factory factory) {
      this.callFactory = checkNotNull(factory, "factory == null");
      return this;
      }
      

2.6 Retrofit对象调用build()源码解析

  • 看看源码

    • 大概的流程就是创建适配器的防御性副本,并添加默认调用适配器。然后复制转换器的防御性副本,在然后添加内置的转化工厂.这可以防止重写其行为,但也可以确保在使用消耗所有类型的转换器时的正确行为。
    • 通过前面步骤设置的变量,将Retrofit类的所有成员变量都配置完毕。就成功创建了对象!

      public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }
      
      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> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
      callAdapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
      
      // Make a defensive copy of the converters.
      List<Converter.Factory> converterFactories =
          new ArrayList<>(1 + this.converterFactories.size());
      
      // Add the built-in converter factory first. This prevents overriding its behavior but also
      // ensures correct behavior when using converters that consume all types.
      converterFactories.add(new BuiltInConverters());
      converterFactories.addAll(this.converterFactories);
      
      return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
          unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
      }
      
  • 然后看看Retrofit的构造方法
    • 成功建立一个Retrofit对象的标准:配置好Retrofit类里的成员变量,即配置好:
      • serviceMethod:包含所有网络请求信息的对象
      • baseUrl:网络请求的url地址
      • callFactory:网络请求工厂
      • adapterFactories:网络请求适配器工厂的集合
      • converterFactories:数据转换器工厂的集合
      • callbackExecutor:回调方法执行器
        Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,
        List<Converter.Factory> converterFactories, List<CallAdapter.Factory> callAdapterFactories,
        @Nullable Executor callbackExecutor, boolean validateEagerly) {
        this.callFactory = callFactory;
        this.baseUrl = baseUrl;
        this.converterFactories = converterFactories; // Copy+unmodifiable at call site.
        this.callAdapterFactories = callAdapterFactories; // Copy+unmodifiable at call site.
        this.callbackExecutor = callbackExecutor;
        this.validateEagerly = validateEagerly;
        }
        
  • 然后总结一下创建的过程
    • 平台类型对象(Platform - Android)
    • 网络请求的url地址(baseUrl)
    • 网络请求工厂(callFactory) 默认使用OkHttpCall
    • 网络请求适配器工厂的集合(adapterFactories) 本质是配置了网络请求适配器工厂- 默认是ExecutorCallAdapterFactory
    • 数据转换器工厂的集合(converterFactories) 本质是配置了数据转换器工厂
    • 回调方法执行器(callbackExecutor) 默认回调方法执行器作用是:切换线程(子线程 - 主线程)

3.创建ServiceMethod流程源码分析

3.1 首先看看请求网络代码过程

  • 大概的流程如下代码所示

    • 定义网络请求的接口类 ApiService

      public interface ApiService {
        @POST("api/v1/user/old")
        Call<ResEntity<UserOld>> isUserOld();
      }
      
      //创建接口类实例
      ApiService apiService = retrofit.create(ApiService.class);
      //生成最终的网络请求对象
      Call<ResEntity<UserOld>> userOld = apiService.isUserOld();
      //异步机制
      userOld.enqueue(new Callback<ResEntity<UserOld>>() {
        @Override
        public void onResponse(Call<ResEntity<UserOld>> call, retrofit2.Response<ResEntity<UserOld>> response) {
      
        }
        @Override
        public void onFailure(Call<ResEntity<UserOld>> call, Throwable t) {
      
        }
      });
      

3.2 分析create(final Class service)源码

  • 源代码如下所示,这段代码很重要。

    • 创建接口定义的API端点的实现。给定方法的相对路径是从描述请求类型的方法的注释中获得的。
    • 先对service类进行判断是否是接口。这个时候你就知道为何只能定义service为接口呢……
    • 接着就创建了ServiceMethod对象,并且把这个对象以键的形式存储到ConcurrentHashMap集合中
    • 最后创建了网络请求接口的动态代理对象,通过代理模式中的动态代理模式,动态生成网络请求接口的代理类,并将代理类的实例创建交给InvocationHandler类 作为具体的实现,并最终返回一个动态代理对象。
      • service.getClassLoader()作用是动态生成接口的实现类
      • new Class<?>[] { service }作用是动态创建实例
      • new InvocationHandler()作用是将代理类的实现交给 InvocationHandler类作为具体的实现
    • 即通过动态生成的代理类,调用interfaces接口的方法实际上是通过调用InvocationHandler对象的invoke方法来完成指定的功能

      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, @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);
            }
      
            //读取网络请求接口里的方法,并根据前面配置好的属性配置serviceMethod对象
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            //根据配置好的serviceMethod对象创建okHttpCall对象 
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            //调用OkHttp,并根据okHttpCall返回rejava的Observe对象或者返回Call
            return serviceMethod.adapt(okHttpCall);
          }
        });
      }
      
  • 接着看一下validateServiceInterface方法操作了什么?
    • 通过这个方法可知,如果service类不是接口则会抛异常。同时需要注意API接口不能扩展其他接口
      static <T> void validateServiceInterface(Class<T> service) {
        if (!service.isInterface()) {
          throw new IllegalArgumentException("API declarations must be interfaces.");
        }
        // Prevent API interfaces from extending other interfaces. This not only avoids a bug in
        // Android (http://b.android.com/58753) but it forces composition of API declarations which is
        // the recommended pattern.
        if (service.getInterfaces().length > 0) {
          throw new IllegalArgumentException("API interfaces must not extend other interfaces.");
        }
      }
      
  • 接着看看eagerlyValidateMethods这个方法的源码

    • 判断是否需要提前验证,主要是给接口中每个方法的注解进行解析并得到一个ServiceMethod对象,然后以Method为键将该对象存入serviceMethodCache集合中。该集合是一个ConcurrentHashMap集合。
    • 关于ConcurrentHashMap集合的源码分析,可以参考我的这篇博客ConcurrentHashMap

      private void eagerlyValidateMethods(Class<?> service) {
        Platform platform = Platform.get();
        for (Method method : service.getDeclaredMethods()) {
          if (!platform.isDefaultMethod(method)) {
            loadServiceMethod(method);
          }
        }
        }
      
        ServiceMethod<?, ?> loadServiceMethod(Method method) {
        ServiceMethod<?, ?> result = serviceMethodCache.get(method);
        if (result != null) return result;
      
        synchronized (serviceMethodCache) {
          result = serviceMethodCache.get(method);
          if (result == null) {
            result = new ServiceMethod.Builder<>(this, method).build();
            serviceMethodCache.put(method, result);
          }
        }
        return result;
      }
      
  • 知道return (T) roxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler invocationHandler)通过代理模式中的动态代理模式,在面试中也经常会问到该模式,那么该模式有什么特点呢?
    • 当NetService对象调用getCall()接口中方法时会进行拦截,调用都会集中转发到 InvocationHandler#invoke (),可集中进行处理
    • 获得网络请求接口实例上的所有注解
  • 接着看看loadServiceMethod(Method method)方法源码
    • 可以看到先从serviceMethodCache集合中获取result对象,然后对result进行非空判断
    • 并且通过synchronized关键字设置了线程同步锁,创建ServiceMethod对象前,先看serviceMethodCache有没有缓存之前创建过的网络请求实例,若没缓存,则通过建造者模式创建 serviceMethod 对象。创建实例的缓存机制:采用单例模式从而实现一个 ServiceMethod 对象对应于网络请求接口里的一个方法
    • 针对synchronized关键字的作用可以参考我的这篇博客:https://blog.csdn.net/m0_37700275/article/details/83151850
    • 针对单利设计模式总结笔记:https://blog.csdn.net/m0_37700275/article/details/78276558
    • image

3.3 serviceMethod对象的创建过程

  • 创建之前,首先会尝试根据方法从一个缓存列表中取出ServiceMethod实例,如果没有,在锁保护之后,还有再尝试一次,还是没有的情况下,才会去创建ServiceMethod。
    • image
  • 第一步,先看看ServiceMethod的Builder方法
    • 除了传递了两个参数外,还获取网络请求接口方法里的注释,获取网络请求接口方法里的参数类型,获取网络请求接口方法里的注解内容
      Builder(Retrofit retrofit, Method method) {
      this.retrofit = retrofit;
      this.method = method;
      this.methodAnnotations = method.getAnnotations();
      this.parameterTypes = method.getGenericParameterTypes();
      this.parameterAnnotationsArray = method.getParameterAnnotations();
      }
      
  • 第二步,然后看看ServiceMethod的build()方法

    • 根据网络请求接口方法的返回值和注解类型,从Retrofit对象中获取对应的网络请求适配器callAdapter对象
    • 网络请求接口方法的返回值和注解类型,从Retrofit对象中获取该网络适配器返回的数据类型responseType
    • 然后对responseType类型进行判断,如果是Response类型或者okhttp3.Response类型,则抛出异常
    • 根据网络请求接口方法的返回值和注解类型,从Retrofit对象中获取对应的数据转换器responseConverter对象
    • 然后采用for循环解析网络请求接口中方法的注解,注解包括:DELETE、GET、POST、HEAD、PATCH、PUT、OPTIONS、HTT等等
    • 如果httpMethod为null。则抛出异常

      public ServiceMethod build() {
      callAdapter = createCallAdapter();
      responseType = callAdapter.responseType();
      if (responseType == Response.class || responseType == okhttp3.Response.class) {
        throw methodError("'"
            + Utils.getRawType(responseType).getName()
            + "' is not a valid response body type. Did you mean ResponseBody?");
      }
      responseConverter = createResponseConverter();
      
      for (Annotation annotation : methodAnnotations) {
        parseMethodAnnotation(annotation);
      }
      
      if (httpMethod == null) {
        throw methodError("HTTP method annotation is required (e.g., @GET, @POST, etc.).");
      }
      
      if (!hasBody) {
        if (isMultipart) {
          throw methodError(
              "Multipart can only be specified on HTTP methods with request body (e.g., @POST).");
        }
        if (isFormEncoded) {
          throw methodError("FormUrlEncoded can only be specified on HTTP methods with "
              + "request body (e.g., @POST).");
        }
      }
      
      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);
      }
      
      if (relativeUrl == null && !gotUrl) {
        throw methodError("Missing either @%s URL or @Url parameter.", httpMethod);
      }
      if (!isFormEncoded && !isMultipart && !hasBody && gotBody) {
        throw methodError("Non-body HTTP method cannot contain @Body.");
      }
      if (isFormEncoded && !gotField) {
        throw methodError("Form-encoded method must contain at least one @Field.");
      }
      if (isMultipart && !gotPart) {
        throw methodError("Multipart method must contain at least one @Part.");
      }
      
      return new ServiceMethod<>(this);
      }
      
  • 第三步,看看ServiceMethod(Builder builder) 构造方法
    • 可以看到这里都是参数赋值操作
      ServiceMethod(Builder<R, T> builder) {
        this.callFactory = builder.retrofit.callFactory();
        this.callAdapter = builder.callAdapter;
        this.baseUrl = builder.retrofit.baseUrl();
        this.responseConverter = builder.responseConverter;
        this.httpMethod = builder.httpMethod;
        this.relativeUrl = builder.relativeUrl;
        this.headers = builder.headers;
        this.contentType = builder.contentType;
        this.hasBody = builder.hasBody;
        this.isFormEncoded = builder.isFormEncoded;
        this.isMultipart = builder.isMultipart;
        this.parameterHandlers = builder.parameterHandlers;
      }
      

4.注解的解析

4.1 callAdapter的创建源码分析

  • 首先获取method的对象表示的方法的形式类型。然后获取method的注解。重点看看retrofit.callAdapter(returnType, annotations)主要做了什么?
      private CallAdapter<T, R> createCallAdapter() {
        Type returnType = method.getGenericReturnType();
        if (Utils.hasUnresolvableType(returnType)) {
          throw methodError(
              "Method return type must not include a type variable or wildcard: %s", returnType);
        }
        if (returnType == void.class) {
          throw methodError("Service methods cannot return void.");
        }
        Annotation[] annotations = method.getAnnotations();
        try {
          //noinspection unchecked
          return (CallAdapter<T, R>) retrofit.callAdapter(returnType, annotations);
        } catch (RuntimeException e) { // Wide exception range because factories are user code.
          throw methodError(e, "Unable to create call adapter for %s", returnType);
        }
      }
    
  • 看看retrofit.callAdapter(returnType, annotations)源码

      public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
          return nextCallAdapter(null, returnType, annotations);
      }
    
      public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
        Annotation[] annotations) {
      checkNotNull(returnType, "returnType == null");
      checkNotNull(annotations, "annotations == null");
    
      int start = callAdapterFactories.indexOf(skipPast) + 1;
      for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
        CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
        if (adapter != null) {
          return adapter;
        }
      }
    
      StringBuilder builder = new StringBuilder("Could not locate call adapter for ")
          .append(returnType)
          .append(".\n");
      if (skipPast != null) {
        builder.append("  Skipped:");
        for (int i = 0; i < start; i++) {
          builder.append("\n   * ").append(callAdapterFactories.get(i).getClass().getName());
        }
        builder.append('\n');
      }
      builder.append("  Tried:");
      for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
        builder.append("\n   * ").append(callAdapterFactories.get(i).getClass().getName());
      }
      throw new IllegalArgumentException(builder.toString());
      }
    

4.2 responseConverter的创建源码分析

5.OkHttpCall的创建源码分析

5.1 new OkHttpCall<>(serviceMethod, args)源码分析

  • 可以看到创建OkHttpCall对象需要两个参数,参数分别是配置好的ServiceMethod对象和输入的请求参数

    • 源码如下所示

      final class OkHttpCall<T> implements Call<T> {
      private final ServiceMethod<T, ?> serviceMethod;
      private final @Nullable Object[] args;
      
      OkHttpCall(ServiceMethod<T, ?> serviceMethod, @Nullable Object[] args) {
        //含有所有网络请求参数信息的对象
        this.serviceMethod = serviceMethod;
        //网络请求接口的参数 
        this.args = args;
      }
      }
      
  • 接着看看return serviceMethod.adapt(okHttpCall)源码分析
    • 创建的OkHttpCall对象传给第一步创建的serviceMethod对象中对应的网络请求适配器工厂的adapt()
    • 返回对象类型:Android默认的是Call<>;若设置了RxJavaCallAdapterFactory,返回的则是Observable<>。如果这个地方不理解,可以继续往下看
      T adapt(Call<R> call) {
        return callAdapter.adapt(call);
      }
      
  • 接着看看实际的调用
    • ApiService对象实际上是动态代理对象Proxy.newProxyInstance(),并不是真正的网络请求接口创建的对象
    • 当ApiService对象调用isUserOld()时会被动态代理对象Proxy.newProxyInstance()拦截,然后调用自身的InvocationHandler # invoke()
    • invoke(Object proxy, Method method, Object... args)会传入3个参数:Object proxy:(代理对象)、Method method(调用的isUserOld()),Object... args(方法的参数,即getCall(*)中的)
    • 接下来利用Java反射获取到isUserOld()的注解信息,配合args参数创建ServiceMethod对象。
    • 最终创建并返回一个OkHttpCall类型的Call对象或者Observable
      • OkHttpCall类是OkHttp的包装类
      • 创建了OkHttpCall类型的Call对象还不能发送网络请求,需要创建Request对象【也就是异步请求方法】才能发送网络请求
        ```
        ApiService apiService = retrofit.create(ApiService.class);
        //返回Android默认的Call
        Call> userOld = apiService.isUserOld();
//返回的则是Observable<T>
Observable<AdvertCommon> advert = mApiService.getSplashImage(method)
```

6.OkHttpCall的网络请求

6.1 OkHttpCall.execute()同步请求

  • 使用方法Response response = call.execute();

    • 实际开发中这种我也没有用过……哈哈
    • 首先添加一个synchronized同步锁。先创建一个OkHttp的Request对象请求,然后调用OkHttpCall的execute()发送网络请求,再然后解析网络请求返回的数据。
    • 需要注意:

      • 发送网络请求时,OkHttpCall需要从ServiceMethod中获得一个Request对象
        ```
        @Override public Response execute() throws IOException {
        okhttp3.Call call;

        synchronized (this) {
        if (executed) throw new IllegalStateException("Already executed.");
        executed = true;

        if (creationFailure != null) {
        if (creationFailure instanceof IOException) {

        throw (IOException) creationFailure;
        

        } else if (creationFailure instanceof RuntimeException) {

        throw (RuntimeException) creationFailure;
        

        } else {

        throw (Error) creationFailure;
        

        }
        }

        call = rawCall;
        if (call == null) {
        try {

        call = rawCall = createRawCall();
        

        } catch (IOException | RuntimeException | Error e) {

        throwIfFatal(e); //  Do not assign a fatal error to creationFailure.
        creationFailure = e;
        throw e;
        

        }
        }
        }

        if (canceled) {
        call.cancel();
        }

        return parseResponse(call.execute());
        }

      //从serviceMethod一个Request对象
      private okhttp3.Call createRawCall() throws IOException {
      okhttp3.Call call = serviceMethod.toCall(args);
      if (call == null) {

      throw new NullPointerException("Call.Factory returned null.");
      

      }
      return call;
      }
      ```

6.2 OkHttpCall.enqueue()异步请求

  • 关于异步操作封装库,可以看我的开源线程池封装库:https://github.com/yangchong211/YCThreadPool
  • 如何调用可以看前面的代码介绍。这里就不介绍呢!

    • 首先添加一个synchronized同步锁。创建OkHttp的Request对象,然后发送网络请求,然后解析返回数据。在这里,可能会想到,究竟是如何做到异步操作的呢?
      ```
      @Override public void enqueue(final Callback callback) {
      checkNotNull(callback, "callback == null");

      okhttp3.Call call;
      Throwable failure;

      synchronized (this) {

      if (executed) throw new IllegalStateException("Already executed.");
      executed = true;
      
      call = rawCall;
      failure = creationFailure;
      if (call == null && failure == null) {
        try {
          call = rawCall = createRawCall();
        } catch (Throwable t) {
          throwIfFatal(t);
          failure = creationFailure = t;
        }
      }
      

      }

      if (failure != null) {

      callback.onFailure(this, failure);
      return;
      

      }

      if (canceled) {

      call.cancel();
      

      }

      call.enqueue(new okhttp3.Callback() {

      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
        Response<T> response;
        try {
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          callFailure(e);
          return;
        }
      
        try {
          callback.onResponse(OkHttpCall.this, response);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }
      
      @Override public void onFailure(okhttp3.Call call, IOException e) {
        callFailure(e);
      }
      
      private void callFailure(Throwable e) {
        try {
          callback.onFailure(OkHttpCall.this, e);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }
      

      });
      }

private okhttp3.Call createRawCall() throws IOException {
    okhttp3.Call call = serviceMethod.toCall(args);
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
 }
```
  • 从上可以知道,call操作异步,那么这个call是什么呢?那么我们看一下ExecutorCallAdapterFactory这个类,关于CallAdapterFactory是做什么用的?前面已经介绍呢!

    • 如果你对异步线程还不是很熟悉,可以参考我的线程池封装库,里面已经很详细实现了异步线程操作,参考链接:https://github.com/yangchong211/YCThreadPool
    • 线程切换,即将子线程切换到主线程,从而在主线程对返回的数据结果进行处理

      static final class ExecutorCallbackCall<T> implements Call<T> {
      final Executor callbackExecutor;
      final Call<T> delegate;
      
      ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
      this.callbackExecutor = callbackExecutor;
      this.delegate = delegate;
      }
      
      @Override public void enqueue(final Callback<T> callback) {
      checkNotNull(callback, "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);
            }
          });
        }
      });
      }
      

6.3 parseResponse解析网络数据源码解析

  • 解析网络数据

    • 关于网络状态栏,我已经整理了一篇十分详细的博客,可以看我的这篇文章:07.Http状态码详解
    • 调用serviceMethod.toResponse(catchingBody),解析数据时,还需要通过ServiceMethod使用Converter(数据转换器)转换成Java对象进行数据解析
    • 关于网络请求的基础介绍,可以参考我的这篇博客:https://blog.csdn.net/m0_37700275/article/details/78533930

      Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
        ResponseBody rawBody = rawResponse.body();
      
        // Remove the body's source (the only stateful object) so we can pass the response along.
        rawResponse = rawResponse.newBuilder()
            .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
            .build();
      
        int code = rawResponse.code();
        if (code < 200 || code >= 300) {
          try {
            // Buffer the entire body to avoid future I/O.
            ResponseBody bufferedBody = Utils.buffer(rawBody);
            return Response.error(bufferedBody, rawResponse);
          } finally {
            rawBody.close();
          }
        }
      
        if (code == 204 || code == 205) {
          rawBody.close();
          return Response.success(null, rawResponse);
        }
      
        ExceptionCatchingRequestBody catchingBody = new ExceptionCatchingRequestBody(rawBody);
        try {
          T body = serviceMethod.toResponse(catchingBody);
          return Response.success(body, rawResponse);
        } catch (RuntimeException e) {
          // If the underlying source threw an exception, propagate that rather than indicating it was
          // a runtime exception.
          catchingBody.throwIfCaught();
          throw e;
        }
      }
      

关于其他内容介绍

01.关于博客汇总链接

02.关于我的博客

目录
相关文章
|
5月前
retrofit+okhttp+rxjava
retrofit+okhttp+rxjava
|
7月前
|
JSON Java 数据格式
rxjava2+retrofit2
rxjava2+retrofit2
55 1
|
设计模式 缓存 监控
OKHttp3 从使用到原理分析
Okhttp3 是我们经常使用的一个网络框架,可扩展性强,支持 get 缓存, spdy、http2.0,gzip 压缩减少数据流量,同步和异步请求,连接池复用机制等特性让广大 android 开发者深爱不已,今天我就带大家从 Okhttp 简单使用,到各种好用拦截器原理了解 Okhttp3
1912 0
OKHttp3 从使用到原理分析
|
JSON 安全 Java
Retrofit入门
Retrofit入门
|
JSON Android开发 数据格式
RxJava+Retrofit示例 ,Retrofit 注解学习
RxJava+Retrofit示例 ,Retrofit 注解学习
174 0
|
JSON Java 数据格式
rxjava2+retrofit2 简介
rxjava2+retrofit2 简介
107 0
|
域名解析 存储 缓存
【OkHttp】OkHttp 源码分析 ( OkHttpClient.Builder 构造器源码分析 )
【OkHttp】OkHttp 源码分析 ( OkHttpClient.Builder 构造器源码分析 )
401 0
|
Android开发 Java JSON
RxJava2 和 Retrofit2 结合使用详解
不讲 rxjava 和 retrofit 而是直接上手 2 了,因为 2 封装的更好用的更多。 1. 观察者模式 常见的 button 点击事件为例,button 是被观察者,listener 是观察者,setOnClickListener 过程是订阅,有了订阅关系后在 button 被点击的时候,监听者 listener 就可以响应事件。
|
缓存 程序员 数据格式
源码分析Retrofit请求流程
Retrofit 是 square 公司的另一款广泛流行的网络请求框架。前面的一篇文章《源码分析OKHttp执行过程》已经对 OkHttp 网络请求框架有一个大概的了解。今天同样地对 Retrofit 的源码进行走读,对其底层的实现逻辑做到心中有数。
1144 0
|
Android开发
Retrofit2源码解析(一)
从源码分析Retrofit的原理
2978 0