【Android】Retrofit2.0源码解析

作者: 带心情去旅行 | 来源:发表于2016-08-25 22:51 被阅读1853次
    Retrofit

    前言

    使用Retrofit已经一段时间了,这货挺好用的,还很特别,特别是使用接口来定义请求方式,这用法让我对它的源码很是好奇。今天就来看看源码吧...

    参靠源码retrofit:2.0.2

    基本的用法

    首先来简单得实现一次GET请求

    • 定义接口
    interface Service {
        @GET("News")
        Call<ResponseBody> getNews(
                @Query("limit") String limit);
    }
    
    • 完成一次请求
            Retrofit retrofit =new Retrofit.Builder()
                    .baseUrl("http://hcy.com/api/")
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
            Service service = retrofit.create(Service.class);
            Call<ResponseBody> call = service.getNews("10");
            call.enqueue(new Callback<ResponseBody>() {
                @Override
                public void onResponse(Call<ResponseBody> call,
                                       Response<ResponseBody> response) {
                    Log.i(TAG, "onResponse");
                }
                @Override
                public void onFailure(Call<ResponseBody> call, Throwable t) {
                    Log.i(TAG, "onFailure");
                }
            });
    

    根据这个请求一步步进行解析,接下来就是这篇的重点,Retrfit内部的实现。
    还不会用Retrofit?少年去看看Retrofit 2.0 的使用吧!!!

    源码解析

    这里分别说明了都调用了哪些源码,都是怎么实现的。(里面涉及到一些设计模式,什么?你还不知道有什么设计模式?下面会涉及到Builder模式(外观模式)、工厂模式、动态代理。stay 的 Retrofit分析-经典设计模式

    • 1.Retrofit的创建

    首先看下Retrofit对象的创建

            Retrofit retrofit =new Retrofit.Builder()
                    .baseUrl("http://hcy.com/api/")
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
    

    这里使用了Builder设计模式(外观模式),Retrofit.BuilderRetrofit的一个内部类,用来配置一些成员变量,这里配置了baseUrlConverterFactory(对象的序列号/反序列化组件),然后创建一个Retrofit对象。这些代码都做了什么,下面一一说明。

    • Retrofit.Builder()
      看看new Retrofit.Builder()调用的代码
        public Builder() {
          this(Platform.get());
        }
    

    Platform.get()又是什么?抱着一贯的好奇,点进去看看。主要代码如下

      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) {
              return new Android();
            }
            } catch (ClassNotFoundException ignored) {
            }
          try {
              Class.forName("java.util.Optional");
              return new Java8();
            } catch (ClassNotFoundException ignored) {
            }
          try {
              Class.forName("org.robovm.apple.foundation.NSObject");
              return new IOS();
            } catch (ClassNotFoundException ignored) {
            }
          return new Platform();
      }
    

    机智的你一定能看出来,主要就是这个findPlatform()方法。这里面的代码,就是判断当前运行的平台。可以看到里面有AndroidJava8IOS。等下,怎会有IOS,什么鬼(为什么会有IOS就交给你去研究了)。
    我们在Android上运行的话,就调用了return new Android()。进一步往下看,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);
            }
          }
      }
    

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

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

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

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

    看最后一句

    return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
             callbackExecutor, validateEagerly);
    

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

    • 总:完成基本的配置,创建一个Retrofit对象

    2.Service的创建以及接口的调用

    我们创建了一个接口的实例,用于调用接口。看下代码吧

    Service service = retrofit.create(Service.class);
    Call<ResponseBody> call = service.getNews("10");
    

    Service是之前定义的接口,这里代码通过retrofit.create(Service.class)就得到了Service的实例,他是怎么做到的?进入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);
                }
                ServiceMethod serviceMethod = loadServiceMethod(method);
                OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
                return serviceMethod.callAdapter.adapt(okHttpCall);
              }
            });
      }
    

    看到这里的代码,相信有些同学开始懵逼了,Proxy.newProxyInstance...这是什么鬼?这代码鬼认识...

    哈哈,这叫动态代理,可以生成接口对应的对象,之后使用这个对象调用方法时都会调用InvocationHandler中的invoke方法。(我不会告诉你们我一开始也是懵逼的~~)
    对动态代理还不熟悉的看看这里: 公共技术点之 Java 动态代理
    下面我们来一步步分析这个create方法:

    • Utils.validateServiceInterface(service);
      源码我就不贴出来了,这个方法主要就是判断了参数service是否为Interface,是否包含了其他接口;
    • eagerlyValidateMethods(service);
      这里根据validateEagerly判断是否需要提前创建ServiceMethod,调用loadServiceMethod()方法,这个方法我们自后面会讲到。
    • invoke
      接下来看Proxy.newProxyInstance中重写的方法invoke,这才是这次解析的重点。看看里面都做了什么
      • 代码
              if (method.getDeclaringClass() == Object.class) {
                return method.invoke(this, args);
              }
              if (platform.isDefaultMethod(method)) {
                return platform.invokeDefaultMethod(method, service, proxy, args);
              }
    

    第一个if用来判断的是否为Object方法,如果是就直调用;
    第二个if则是判断平台,不过进入.isDefaultMethod(method)源码可以看到,直接返回false,应该是为了之后的扩展用的。

    • ServiceMethod serviceMethod = loadServiceMethod(method);
      创建了一个ServiceMethod对象,看下loadServiceMethod的源码
      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 对像。
    可以看到ServiceMethod也使用了Bulider设计模式,继续往里面看?看看new ServiceMethod.Builder(this, method).build();都干嘛了?

    这里就简单说说ServiceMethod的功能,再讲下去这层次结果有点深...
    ServiceMethod的定义:把对接口中的方法的调用转化成一次HTTP调用。
    (说人话...)
    呃...,就是解析了接口中@GET("News")@Query("limit") String limit等一些列有关请求的信息,然后还保存了Retrofit中的一些重要信息,如:
    1、callFactory:Call工厂,负责创建 HTTP 请求
    2、callAdapter:确定返回的retrofit2.Call<T>类型(接口定义时的返回类型,例子中的Call<ResponseBody>);
    3、responseConverter:数据转换类型,负责将服务器返回的数据(Json、xml等各式)转换成我们需要用到的T类型的对象;
    4、parameterHandlers:则负责解析 API 定义时每个方法的参数,并在构造 HTTP 请求时设置参数。(如例子中的@Query("limit")中的limit)
    总之就是基本包含了这次请求的全部内容

    • OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
      创建了一个OkHttpCall 对象,用来发起请求。OkHttpCall 中有个我们常用的方法enqueue()
      @Override public void enqueue(final Callback<T> callback) {
          if (callback == null) throw new NullPointerException("callback == null");  
            okhttp3.Call call;
             ....
            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();
                    }
                  }
                ...
              });
      }
    

    都说Retrofit内部是用okhttp实现请求的,原来在这里,哈哈...
    拿着这个对象我们就可以发起请求了,不过Retrofit还要适配返回类型,所以还要下面这句代码。

    • return serviceMethod.callAdapter.adapt(okHttpCall);
      这里将我们创建的OkHttpCall 对象适配成对应的类型(例子中得到的是Call,如果用RxJava得到的就是Observable)。

    3.发起请求

          call.enqueue(new Callback<ResponseBody>() {
              @Override
              public void onResponse(Call<ResponseBody> call,
                                     Response<ResponseBody> response) {
                  Log.i(TAG, "onResponse");
              }
              @Override
              public void onFailure(Call<ResponseBody> call, Throwable t) {
                  Log.i(TAG, "onFailure");
              }
          });
    

    最后就是使用得到的call对象来发起请求(这个callretrofit2.Call)。
    通过上面的解析可以知道,这里其实就是调用了okhttp3里面的okhttp3.Call来完成这次请求。
    还不满足?想知道okhttp3是怎么完成请求的?
    自己去研究吧,少年~~

    以上有错之处,还望不吝赐教。谢谢!!

    参考

    拆轮子系列:拆 Retrofit
    Retrofit源码1: 为什么写一个interface就可以实现http请求
    Retrofit2 源码解析
    Retrofit 源码分析之 Retrofit 对象
    Retrofit分析-漂亮的解耦套路
    Retrofit分析与实现

    相关文章

      网友评论

      • zoro_x:有 iOS 是因为有一个 RoboVM 的是实现, 是一个在 iOS 平台运行的 JVM
        飞奔的小马:@带心情去旅行 在最新的源码中已经将IOS的删除了,只有Android和Java8
        带心情去旅行:@zoro_x 谢谢

      本文标题:【Android】Retrofit2.0源码解析

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