• 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的使用,可以参考这篇文章: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<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(".
      ");
      if (skipPast != null) {
        builder.append("  Skipped:");
        for (int i = 0; i < start; i++) {
          builder.append("
         * ").append(callAdapterFactories.get(i).getClass().getName());
        }
        builder.append('
      ');
      }
      builder.append("  Tried:");
      for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
        builder.append("
         * ").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.关于我的博客

  • 相关阅读:
    2015.7.23 开始记录一些学习情况
    poj 3299 java
    在线编辑~
    MATLAB加载数据来绘图《原创翻译Loading Data into MATLAB for Plotting》
    【转载】matlab的reshape函数的作用
    (转载)CUDA 6.0 安装及配置( WIN7 64位 / 英伟达G卡 / VS2010 )
    简单文本处理
    【转】CUDA与二维动态数组
    《转载》 cpp文件调用CUDA .cu文件实现显卡加速相关编程
    cuda_opencv之向量相加
  • 原文地址:https://www.cnblogs.com/yc211/p/9869857.html
Copyright © 2020-2023  润新知