【Android】Retrofit2.0源码解析

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();,这里用的是okhttp3baseUrl(服务器基本地址):这个我们上面配置过; 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分析与实现

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏技术小黑屋

关于Android Log的一些思考

在日常的Android开发中,日志打印是一项必不可少的操作,我们通过分析打印的日志可以分析程序的运行数据和情况。然而使用日志打印的正确姿势又是怎样呢,如何屏蔽日...

9210
来自专栏学海无涯

Android开发之项目经验分享

在Android开发中,除了基本的理论知识,还需要将所学知识运用到真实的项目中,在项目中锻炼自己的分析问题、解决问题的能力,本文将总结一下本人项目中遇到的一些问...

30050
来自专栏向治洪

[置顶] 浅谈我为什么选择用Retrofit作为我的网络请求框架

比较AsyncTask、Volley、Retrofit三者的请求时间 使用 单次请求 7个请求 25个请求 AsyncTask 94...

23550
来自专栏向治洪

android自定义属性

1、引言 对于自定义属性,大家肯定都不陌生,遵循以下几步,就可以实现: 自定义一个CustomView(extends View )类 编写values/a...

214100
来自专栏向治洪

Android的DataBinding原理介绍

Activity在inflate layout时,通过DataBindingUtil来生成绑定,从代码看,是遍历contentView得到View数组对象,然后...

50980
来自专栏移动开发

关于fragment的getActivity()返回null的处理

在fragment的使用中经常要使用getActivity获取依附的activity,但在某些情况下回返回null,如果不加处理可能会因空指针异常导致程序崩溃....

37720
来自专栏非著名程序员

你真的会用Retrofit2吗?Retrofit2完全教程

本文注目录: Retrofit入门 Retrofit注解详解 Gson与Converter RxJava与CallAdapter 自定义Converter 自定...

56370
来自专栏微服务生态

简单比较init-method,afterPropertiesSet和BeanPostProcessor

1、init-method方法,初始化bean的时候执行,可以针对某个具体的bean进行配置。init-method需要在applicationContext....

13820
来自专栏KK的小酒馆

久闻大名的FragmentAndroid应用界面开发

Fragment(碎片),听名字就知道是一种零部件,确实,这是一种寄生于Activity(活动)之中的灵活型Activity,当然,只是种比喻。

11920
来自专栏Java与Android技术栈

基于Kotlin的委托机制实现一个对Extra、SharedPreferences操作的库

本文介绍的库,github地址:https://github.com/fengzhizi715/SAF-Object-Delegate

16530

扫码关注云+社区

领取腾讯云代金券