Retrofit源码分析

目录介绍

  • 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<T> 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的使用,可以参考这篇文章://www.greatytc.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<T> 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<R, T> 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<ResEntity<UserOld>> userOld = apiService.isUserOld();
    
    
    //返回的则是Observable<T>
    Observable<AdvertCommon> advert = mApiService.getSplashImage(method)
    

6.OkHttpCall的网络请求

6.1 OkHttpCall.execute()同步请求

  • 使用方法Response<Bean> response = call.execute();
    • 实际开发中这种我也没有用过……哈哈
    • 首先添加一个synchronized同步锁。先创建一个OkHttp的Request对象请求,然后调用OkHttpCall的execute()发送网络请求,再然后解析网络请求返回的数据。
    • 需要注意:
      • 发送网络请求时,OkHttpCall需要从ServiceMethod中获得一个Request对象
      @Override public Response<T> 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<T> 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.关于我的博客

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容