Retrofit 2.0源码解析

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-设计模式

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容

  • 在开发Android APP时,肯定会使用到Http请求与服务器通信,上传或下载数据等功能。目前开源的Http请求...
    wangling90阅读 472评论 0 0
  • 前言 使用Retrofit已经一段时间了,这货挺好用的,还很特别,特别是使用接口来定义请求方式,这用法让我对它的源...
    带心情去旅行阅读 3,348评论 3 21
  • 首先介绍下Retrofit基本用法,先创建接口,注解申明、请求方式Post/Get等 基本使用如下 上面是简单的网...
    _SHYII阅读 979评论 0 3
  • 说明:在文件管理器中,可以使用这个app来打开图片 布局文件 ImageView 常用的一些XML属性和方法: 支...
    KokutouDa阅读 3,527评论 0 2
  • 刚看完韩寒导演的处女座《后会无期》。江河写《旅行者》纪念一路走来,而我在替未来提供回忆。 陈乔恩饰演周沫,很美的名...
    公子明清阅读 664评论 0 3