前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Android】Retrofit2.0源码解析

【Android】Retrofit2.0源码解析

作者头像
Gavin-ZYX
发布2018-05-18 15:28:59
1K0
发布2018-05-18 15:28:59
举报

Retrofit

前言

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

参靠源码retrofit:2.0.2

基本的用法

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

  • 定义接口
代码语言:javascript
复制
interface Service {
    @GET("News")
    Call<ResponseBody> getNews(
            @Query("limit") String limit);
}
  • 完成一次请求
代码语言:javascript
复制
        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对象的创建

代码语言:javascript
复制
        Retrofit retrofit =new Retrofit.Builder()
                .baseUrl("http://hcy.com/api/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();

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

  • Retrofit.Builder() 看看new Retrofit.Builder()调用的代码
代码语言:javascript
复制
    public Builder() {
      this(Platform.get());
    }

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

代码语言:javascript
复制
  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()是什么

代码语言:javascript
复制
  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。)

代码语言:javascript
复制
    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())
代码语言:javascript
复制
    public Builder addConverterFactory(Converter.Factory factory) {
      converterFactories.add(checkNotNull(factory, "factory == null"));
      return this;
    }

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

  • .build();
代码语言:javascript
复制
    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);
    }

看最后一句

代码语言:javascript
复制
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的创建以及接口的调用

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

代码语言:javascript
复制
Service service = retrofit.create(Service.class);
Call<ResponseBody> call = service.getNews("10");

Service是之前定义的接口,这里代码通过retrofit.create(Service.class)就得到了Service的实例,他是怎么做到的?进入create()方法看看

代码语言:javascript
复制
  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,这才是这次解析的重点。看看里面都做了什么
    • 代码
代码语言:javascript
复制
          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的源码
代码语言:javascript
复制
  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()
代码语言:javascript
复制
  @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.发起请求
代码语言:javascript
复制
      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分析与实现

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016.08.25 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 基本的用法
  • 源码解析
    • 2.Service的创建以及接口的调用
      • 3.发起请求
      • 参考
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档