Retrofit源码分析

简介

Retrofit turns your HTTP API into a Java interface.
官网简介,言简意赅。它可以将你的HTTP API简化成一种Java接口的方式书写。

1、地址

retrofit github地址:https://github.com/square/retrofit
retrofit官网:https://square.github.io/retrofit/
gradle依赖:

implementation 'com.squareup.retrofit2:retrofit:2.9.0'

2、简单使用

STEP 1:定义接口

public interface GitHubService {
    @GET("users/{user}/repos")
    Call<List<Repo>> listRepos(@Path("user") String user);
}

STEP 2:创建Retrofit实体类,并发起请求

// 1、创建Retrofit实体类
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build();

// 2、创建接口实现类
GitHubService service = retrofit.create(GitHubService.class);

// 3、通过实现类得到Call实例
// https://api.github.com/users/octocat/repos
Call<List<Repo>> repos = service.listRepos("octocat");

// 4、Call发起同步/异步请求
/*同步请求
try {
    Response<List<Repo>> execute = repos.execute();
} catch (IOException e) {
    e.printStackTrace();
}
*/
repos.enqueue(new Callback<List<Repo>>() {
    @Override
    public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {

    }

    @Override
    public void onFailure(Call<List<Repo>> call, Throwable t) {

    }
});


原理

1、动态代理

retrofit的基础就是动态代理,通过retrofit.create()方法点进去,可以看到,create方法实际就是使用动态代理生成了GitHubService接口的实现类:

public <T> T create(final Class<T> service) {
    validateServiceInterface(service);
    return (T) Proxy.newProxyInstance(
            service.getClassLoader(),
            new Class<?>[] {service},
            new InvocationHandler() {
              private final Platform platform = Platform.get();
              private final Object[] emptyArgs = new Object[0];

              @Override
              public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
                  throws Throwable {
                // If the method is a method from Object then defer to normal invocation.
                // 判断方法是否是Object的,如果是则不处理。
                if (method.getDeclaringClass() == Object.class) {
                  return method.invoke(this, args);
                }
                args = args != null ? args : emptyArgs;
                return platform.isDefaultMethod(method)
                    ? platform.invokeDefaultMethod(method, service, proxy, args)
                    // 加载方法,此处为了效率,添加了缓存serviceMethodCache
                    : loadServiceMethod(method).invoke(args);
              }
            });
  }

简化一下就是这样的API,

    /**
     * @param loader     classLoader
     * @param interfaces 被代理的class,数组
     * @param h          被代理类的方法会被InvocationHandler中的invoke(Object proxy, Method method, Object[] args)接收
     * @return 生成的代理类
     */
    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

2、反射

由上面的代码可以知道,通过使用动态代理,我们可以通过InvocationHandler拿到接口定义的方法。那我们怎么拿到我们定义的API呢?又如何拿到定义的参数呢?答案就是反射。

通过调用Retrofit.create()->loadServiceMethod()->ServiceMethod.parseAnnotations()->HttpServiceMethod.parseAnnotations()来对method的注解进行解析。

// 此处为了效率,添加了缓存
ServiceMethod<?> loadServiceMethod(Method method) {
    ......
    result = serviceMethodCache.get(method);
    ......
    result = ServiceMethod.parseAnnotations(this, method);
    serviceMethodCache.put(method, result);
    ......
}

// 此处有部分检查返回值类型代码
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
    ......
    if (Utils.hasUnresolvableType(returnType)) {
      throw methodError(method,"Method return type must not include a type variable or wildcard: %s",returnType);
    }
    if (returnType == void.class) {
      throw methodError(method, "Service methods cannot return void.");
    }
    ......
    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}

static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(Retrofit retrofit, Method method, RequestFactory requestFactory) {
    ......
    adapterType = method.getGenericReturnType();
    CallAdapter<ResponseT, ReturnT> callAdapter = createCallAdapter(retrofit, method, adapterType, annotations);
    Type responseType = callAdapter.responseType();
    ......
    Converter<ResponseBody, ResponseT> responseConverter = createResponseConverter(retrofit, method, responseType);
    ......
    okhttp3.Call.Factory callFactory = retrofit.callFactory;
    return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
  }

HttpServiceMethod.parseAnnotations()会返回CallAdapted对象,通过调用invoke()方法,生成OkHttpCall对象,即我们通过接口定义的返回值。

  @Override
  final @Nullable ReturnT invoke(Object[] args) {
    Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
    return adapt(call, args);
  }

当通过OkHttpCall发起请求时,会调用OkHttpCall.createRawCall()方法,通过反射获取到的参数就是在这里被拼接成Okhttp.Request的,生成Okhttp.Call对象,便可以通过Okhttp发起请求。

  private okhttp3.Call createRawCall() throws IOException {
    okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
    if (call == null) {
      throw new NullPointerException("Call.Factory returned null.");
    }
    return call;
  }


调用图解

1、retrofit.create()流程

retrofit流程create.jpg

2、call.enqueue()流程

retrofit enqueue流程.jpg

其他

用到的部分反射方法

// 获取方法的特定注解
MyGet myGet = method.getAnnotation(MyGet.class);

// 获取参数的注解
Annotation[][] paramsAnnotations = method.getParameterAnnotations();

// 获取返回方法返回值类型(包含泛型)
Type returnType = method.getGenericReturnType();

// 获取泛型的类型
Type[] types = ((ParameterizedType) returnType).getActualTypeArguments();

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

推荐阅读更多精彩内容

  • 版本号:2.5.0 一.基本使用 1.定义请求接口 2.创建Retrofit对象,通过Call对象发送网络请求 通...
    慕涵盛华阅读 961评论 1 5
  • retrofit 源码分析 retrofit 源码分析源码执行流程new Retrofit.Builder().b...
    nightSin阅读 391评论 0 1
  • 前言 现代Android开发中,Retrofit是主流的网络请求框架,内部封装OkHttp发起请求,也是声明式Ht...
    h2coder阅读 627评论 0 2
  • 你在使用 Retrofit 的时候,是否会有如下几点疑惑? 什么是动态代理? 整个请求的流程是怎样的? 底层是如何...
    AboBack阅读 423评论 0 3
  • 本文概述Retrofit作为主流的网络框架,采用注解和接口的方式封装请求,使得调用过程变得优雅又简洁,优雅的背后肯...
    Horps阅读 202评论 0 1