Retrofit源码分析

yangchong211 / 文 发表于2018-10-29 11:51 次阅读

目录介绍

  • 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的创建流程源码分析

  • Retrofit实例是使用建造者模式通过Builder类进行创建的。Builder模式的优势:将一个复杂对象的构建与表示分离,使得用户在不知道对象的创建细节情况下就可以直接创建复杂的对象。
  • 针对builder设计模式,可以参考我的这篇博客:https://blog.csdn.net/m0_37700275/article/details/78298505
  • 关于使用builder模式写代码的案例,可以参考我的弹窗封装lib:https://github.com/yangchong211/YCDialog

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 = apiService.isUserOld(); //异步机制 userOld.enqueue(new Callback<ResEntity>() { @Override public void onResponse(Call<ResEntity> call, retrofit2.Response<ResEntity> 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<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 Observable 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<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 delegate) { this.callbackExecutor = callbackExecutor; this.delegate = delegate; }

    @Override public void enqueue(final Callback callback) { checkNotNull(callback, "callback == null");

    delegate.enqueue(new Callback() { @Override public void onResponse(Call call, final Response 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.关于我的博客

  • 我的个人站点:www.yczbj.org,www.ycbjie.cn
  • github:https://github.com/yangchong211
  • 知乎:https://www.zhihu.com/people/yang-chong-69-24/pins/posts
  • 简书:http://www.jianshu.com/u/b7b2c6ed9284
  • csdn:http://my.csdn.net/m0_37700275
  • 喜马拉雅听书:http://www.ximalaya.com/zhubo/71989305/
  • 开源中国:https://my.oschina.net/zbj1618/blog
  • 泡在网上的日子:http://www.jcodecraeer.com/member/content_list.php?channelid=1
  • 邮箱:yangchong211@163.com
  • 阿里云博客:https://yq.aliyun.com/users/article?spm=5176.100- 239.headeruserinfo.3.dT4bcV
  • segmentfault头条:https://segmentfault.com/u/xiangjianyu/articles
收藏 赞 (0) 踩 (0)