Retrofit 2.0源码解析

作者: Code猎人 | 来源:发表于2018-04-17 17:16 被阅读17次

    Retrofit

    Retrofit 是square公司开发的一款对OKHttp进行了进一步封装的网络框架,现在也是android网络请求中非常火的一个网络请求框架,花了点时间研究了一下Retrofit2.0源码。

    Retrofit2.0原理

    Retrofit2.0用了动态代理技术,通过解析注解生成Http请求,把请求交给OkHttp,然后通过我们设置的ConverterFactory进行serialization和deserialization,最后通过CallAdapter把结果进行进一步适配,实现了对Rxjava,Guava和java8的支持。

    源码剖析

    Retrofit retrofit = new Retrofit.Builder()
                                 .baseUrl("http://www.baidu.com/")
                                 .addConverterFactory(GsonConverterFactory.create())
                                 .build();
    retrofit.create(GitHub.class);
    

    Retrofit实例是使用建造者模式通过Builder类进行创建的

    建造者模式:将一个复杂对象的构建与表示分离,使得用户在不知道对象的创建细节情况下就可以直接创建复杂的对象

    Retrofit类

    public final class Retrofit {
    
    private final Map<Method, ServiceMethod> serviceMethodCache = new LinkedHashMap<>();
    // 网络请求配置对象(对网络请求接口中方法注解进行解析后得到的对象)
    // 作用:存储网络请求相关的配置,如网络请求的方法、数据转换器、网络请求适配器、网络请求工厂、基地 
    址等
    
    private final HttpUrl baseUrl;
    // 网络请求的url地址
    
    private final okhttp3.Call.Factory callFactory;
    // 网络请求器的工厂
    // 作用:生产网络请求器(Call)
    // Retrofit是默认使用okhttp
    
    private final List<CallAdapter.Factory> adapterFactories;
    // 网络请求适配器工厂的集合
    // 作用:放置网络请求适配器工厂
    // 网络请求适配器工厂作用:生产网络请求适配器(CallAdapter)
    // 下面会详细说明
    
    
    private final List<Converter.Factory> converterFactories;
    // 数据转换器工厂的集合
    // 作用:放置数据转换器工厂
    // 数据转换器工厂作用:生产数据转换器(converter)
     private final Executor callbackExecutor;
    // 回调方法执行器
    
    private final boolean validateEagerly; 
    // 标志位
    // 作用:是否提前对业务接口中的注解进行验证转换的标志位
    
    Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,  
    List<Converter.Factory> converterFactories, List<CallAdapter.Factory> adapterFactories,  
    Executor callbackExecutor, boolean validateEagerly) {  
    this.callFactory = callFactory;  
    this.baseUrl = baseUrl;  
    this.converterFactories = unmodifiableList(converterFactories); 
    this.adapterFactories = unmodifiableList(adapterFactories);   
    // unmodifiableList(list)近似于UnmodifiableList<E>(list)
    // 作用:创建的新对象能够对list数据进行访问,但不可通过该对象对list集合中的元素进行修改
    this.callbackExecutor = callbackExecutor;  
    this.validateEagerly = validateEagerly;  
    }
    

    1、serviceMethod:包含所有网络请求信息的对象
    2、baseUrl:网络请求的url地址
    3、callFactory:网络请求工厂
    4、adapterFactories:网络请求适配器工厂的集合
    5、converterFactories:数据转换器工厂的集合
    6、callbackExecutor:回调方法执行器

    new Retrofit.Builder()做了什么?我们来揭开神秘面纱

    public static final class Builder {
    private Platform platform;
    private okhttp3.Call.Factory callFactory;
    private HttpUrl baseUrl;
    private List<Converter.Factory> converterFactories = new ArrayList<>();
    private List<CallAdapter.Factory> adapterFactories = new ArrayList<>();
    private Executor callbackExecutor;
    private boolean validateEagerly;
    //Builder类的成员变量与Retrofit类的成员变量是对应的 所以Retrofit类的成员变量基本上是通过Builder类进行配置
    
    Builder(Platform platform) {
      //接收Platform对象(Android平台)
      this.platform = platform;
    
     // 通过传入BuiltInConverters()对象配置数据转换器工厂(converterFactories)
     // converterFactories是一个存放数据转换器Converter.Factory的数组
     // 配置converterFactories即配置里面的数据转换器
      converterFactories.add(new BuiltInConverters());
    
    }
    
    public Builder() {
      this(Platform.get());
    }
    //Platform.get()通过源码可以看到这里面有三种平台支持android、ios、java,这里返回的是一个Android对象
    

    接下来看一下Platformy源码

    class Platform {
    
    private static final Platform PLATFORM = findPlatform();
    
    static Platform get() {
    return PLATFORM;    
    }
    
    private static Platform findPlatform() {
    try {
    // 支持Android平台
      Class.forName("android.os.Build");
      if (Build.VERSION.SDK_INT != 0) {
        return new Android(); 
      }
    } catch (ClassNotFoundException ignored) {
    }
    
    try {
      // 支持Java平台
      Class.forName("java.util.Optional");
      return new Java8();
    } catch (ClassNotFoundException ignored) {
    }
    
    try {
      // 支持iOS平台
      Class.forName("org.robovm.apple.foundation.NSObject");
      return new IOS();
    } catch (ClassNotFoundException ignored) {
    }
    platform)  
    return new Platform();
    }
    }
    

    这样就很清晰了,主要就是这个findPlatform()方法。这里面的代码,就是判断当前运行的平台。可以看到里面有Android、Java、IOS。
    我们在Android上运行的话,就调用了return new Android()。

    看一下new Android里面是什么东东

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

    Android继承了Platform重写了defaultCallbackExecutor和defaultCallAdapterFactory方法。
    defaultCallbackExecutor:返回的是用于执行 Callback 的 线程池。可以看到MainThreadExecutor 获取了主线程的 Looper 并构造了一个主线程的 Handler,调用 Callback 时会将该请求 post 到主线程上去执行。这就解释了为什么请求后完成的回调都是在主线中。
    defaultCallAdapterFactory:将返回的适配类型默认为Call类型(如果使用RxJava的话,就可以通过配置.addCallAdapterFactory(RxJavaCallAdapterFactory.create())将配置类型改成Observable。)

    baseUrl()

    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);
    }
    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;
    }
    

    这里有两个重载的方法,创建了okhttp3 的 HttpUrl 实例。

    addConverterFactory(GsonConverterFactory.create())

    public Builder addConverterFactory(Converter.Factory factory) {
      converterFactories.add(checkNotNull(factory, "factory == null"));
      return this;
    }
    

    往转换工厂集合中添加了我们指定的转换工厂,最后将返回的数据类型转换成对应的实体类对象的Converter类型。在我们的例子里面 GsonConverterFactory 将选用 GsonConverter 来转换。

    build()

    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> adapterFactories = new ArrayList<>(this.adapterFactories);
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
    
      // Make a defensive copy of the converters.
      List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories);
      return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
          callbackExecutor, validateEagerly);
    }
    

    new Retrofit这里才是创建Retrofit对象的地方,之前的只是一些配置。里面的参数:
    callFactory(Call工厂):看到了吧callFactory = new OkHttpClient();,这里用的是okhttp3;
    baseUrl(服务器基本地址):这个我们上面配置过;
    converterFactories(对象的序列号/反序列化组件):我们上面配置过。
    adapterFactories(适配类型)、callbackExecutor(执行 Callback 的线程池):从我们上面提到的platform中获取默认值。

    Retrofit 2.0所使用的动态代理

    Retrofit2.0用了动态代理技术,通过解析注解生成Http请求,把请求交给OkHttp,然后通过我们设置的ConverterFactory进行serialization和deserialization,最后通过CallAdapter把结果进行进一步适配,实现了对Rxjava,Guava和java8的支持。

    我们看一下retrofit.create()里面是什么鬼?

    public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          private final Platform platform = Platform.get();
    
          @Override public Object invoke(Object proxy, Method method, Object... args)
              throws Throwable {
            // 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);
            }
            //看缓存里面有没有这个method,要是有,就返回,要是没有,就生成一个,然后加入缓存
            ServiceMethod serviceMethod = loadServiceMethod(method);
            //生成一个OkHttpCall对象
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
            //调用OkHttp,然后根据okHttpCall返回rxjava的Observe对象或者返回Call
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
    }
    

    这里的Platform 其实是检测retrofit所运行的平台,是java8还是android还是ios。这里主要是在builder的时候,如果没有设置适配器,那么retrofit就会通过运行时的不同平台,然后选择不同的CallAdapterFactory。从上面的代码可以看出,create 方法返回了一个动态代理对象,通过Github接口生成代理类,并将代理类的实现交给 InvocationHandler 作为具体的实现。这里使用动态代理的好处是简化复杂的网络请求和解析、封装ServiceMethod。

    看一下ServiceMethod

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

    loadServiceMethod要处理的事物
    1.先去serviceMethodCache中查找否存在method(看来这货是有缓存的,这里采用了LinkedHashMap来缓存这些Method的解析结果),存在的话跳过第二步;
    2.method不存在的话就创建一个,然后添加到缓存中;
    3.返回ServiceMethod 对像。

    看一下OkHttpCall是如何请求和回调的

    @Override 
    public void enqueue(final Callback<T> callback) {
    if (callback == null) throw new NullPointerException("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) {
          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)
          throws IOException {
        Response<T> response;
        try {
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          callFailure(e);
          return;
        }
        callSuccess(response);
      }
    
      @Override public void onFailure(okhttp3.Call call, IOException e) {
        try {
          callback.onFailure(OkHttpCall.this, e);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }
    
      private void callFailure(Throwable e) {
        try {
          callback.onFailure(OkHttpCall.this, e);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }
    
      private void callSuccess(Response<T> response) {
        try {
          callback.onResponse(OkHttpCall.this, response);
        } catch (Throwable t) {
          t.printStackTrace();
        }
      }
    });
    }
    
    private okhttp3.Call createRawCall() throws IOException {
    Request request = serviceMethod.toRequest(args);
    okhttp3.Call call = serviceMethod.callFactory.newCall(request);
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
    

    }
    这里的call就是okhttp3.Call,是我们外面传进来的serviceMethod来构造出来的okhttp3.Call,在这里通过用call.enqueue(...)把请求交给okhttp的队列中,然后再通过异步回调切换到主线程

    总结

    啊.....舒了一口气,整个源码使用大量的设计模式

    • retrofit.builder 建造者模式
    • Android extends Platform 适配器模式
    • 各种...Factory 工厂模式
    • 在Retrofit中提供了四种CallAdapterFactory: ExecutorCallAdapterFactory(默认)、GuavaCallAdapterFactory、Java8CallAdapterFactory、RxJavaCallAdapterFactory
      adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor)) 策略模式
    • create(final Class<T> service) 代理模式
      等等一系列的设计模式的组合,使得各个功能模块高度解耦
    本章主要说了一下Retrofit是如何构建的,通过动态代理技术,通过解析注解生成Http请求,把请求交给OkHttp队列,然后在回调的这样一个流程。

    点赞加关注是给我最大的鼓励!

    相关文章阅读
    Android-设计模式

    相关文章

      网友评论

        本文标题:Retrofit 2.0源码解析

        本文链接:https://www.haomeiwen.com/subject/jjnukftx.html