热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Android进阶必学retrofit源码解析,最新Android面试合集

CalllistRepos(Path(“user”)Stringuser);}创建Retrofit并生成API的实现RetrofitretrofitnewRetro

Call listRepos(@Path(“user”) String user);
}

创建Retrofit并生成API的实现

Retrofit retrofit = new Retrofit.Builder()
.baseUrl(“https://api.github.com/”)
.build();
GitHubService service = retrofit.create(GitHubService.class);

调用API方法,生成Call

Call repos = service.listRepos(“octocat”);

Retrofit的创建

retrofit实例的创建,使用了builder模式,从下面的源码中可以看出

public static final class Builder {
Builder(Platform platform) {
this.platform = platform;
converterFactories.add(new BuiltInConverters());
}
public Builder() {
// Platform.get()方法可以用于判断当前的环境
this(Platform.get());
}
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 Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException(“Base URL required.”);
}
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();// 新建Client,留到之后newCall什么的
}
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
// Make a defensive copy of the adapters and add the default Call adapter.
List adapterFactories &#61; new ArrayList<>(this.adapterFactories);
adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
// Make a defensive copy of the converters.
List converterFactories &#61; new ArrayList<>(this.converterFactories);
return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories,
callbackExecutor, validateEagerly);
}
}

retrofit.create

好玩的地方开始了&#xff0c;我们先来看看这个方法

public T create(final Class service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
// 动态代理&#xff0c;啦啦啦
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class[] { service },
new InvocationHandler() {
// platform 可以分辨出你是在android&#xff0c;还是java8&#xff0c;又或者别的
private final Platform platform &#61; Platform.get();
&#64;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.
// 这里的invoke&#xff0c;Object方法都走这里&#xff0c;比如equals、toString、hashCode什么的
if (method.getDeclaringClass() &#61;&#61; Object.class) {
return method.invoke(this, args);
}
// java8默认方法&#xff0c;1.8的新特性
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
// 这里是核心代码了
ServiceMethod serviceMethod &#61;
(ServiceMethod) loadServiceMethod(method);
OkHttpCall okHttpCall &#61; new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}

可以看出创建API使用了动态代理&#xff0c;根据接口动态生成的代理类&#xff0c;将接口的都转发给了负责连接代理类和委托类的InvocationHandler实例&#xff0c;接口方法也都通过其invoke方法来处理。
在invoke方法中&#xff0c;首先会通过Platform.get()方法判断出当前代码的执行环境&#xff0c;之后会先把Object和Java8的默认方法进行一个处理&#xff0c;也是在进行后续处理之前进行去噪。其中的关键代码其实就是最后三句&#xff0c;这也是这篇文章将要分析的

创建ServiceMethod

erviceMethod loadServiceMethod(Method method) {
// 从缓存里面取出&#xff0c;如果有的话&#xff0c;直接返回好了
ServiceMethod result &#61; serviceMethodCache.get(method);
if (result !&#61; null) return result;
synchronized (serviceMethodCache) {
result &#61; serviceMethodCache.get(method);
if (result &#61;&#61; null) {
// 为null的话&#xff0c;解析方法的注解和返回类型、参数的注解he参数类型&#xff0c;新建一个ServiceMethod
result &#61; new ServiceMethod.Builder<>(this, method).build();// ->
// 新建的ServiceMethod加到缓存列表里面
serviceMethodCache.put(method, result);
}
}
return result;
}

注解的解析

CallAdapter和Converter等到后面再分析&#xff0c;这里先看看parseMethodAnnotation(annotation)&#xff0c;功能和其名字一样&#xff0c;其对方法注解进行了解析

/**

  • 解析方法注解&#xff0c;呜啦啦
  • 通过判断注解类型来解析
  • &#64;param annotation
    */
    private void parseMethodAnnotation(Annotation annotation) {
    if (annotation instanceof DELETE) {
    parseHttpMethodAndPath(“DELETE”, ((DELETE) annotation).value(), false);
    } else if (annotation instanceof GET) {
    parseHttpMethodAndPath(“GET”, ((GET) annotation).value(), false);
    }
    // 其他的一些方法注解的解析

    }
    private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
    if (this.httpMethod !&#61; null) {// 已经赋值过了
    throw methodError(“Only one HTTP method is allowed. Found: %s and %s.”,
    this.httpMethod, httpMethod);
    }
    this.httpMethod &#61; httpMethod;
    this.hasBody &#61; hasBody;
    // value为设置注解方法时候&#xff0c;设置的值&#xff0c;官方例子中的users/{user}/repos or user
    if (value.isEmpty()) {
    return;
    }
    // 查询条件的一些判断

    this.relativeUrl &#61; value;
    this.relativeUrlParamNames &#61; parsePathParameters(value);
    }
    &#96;

在解析注解时&#xff0c;先通过instanceof判断出注解的类型&#xff0c;之后调用parseHttpMethodAndPath方法解析注解参数值&#xff0c;并设置httpMethod、relativeUrl、relativeUrlParamNames等属性。

上面说了API中方法注解的解析&#xff0c;现在来看看方法参数注解的解析&#xff0c;这是通过调用parseParameterAnnotation方法生成ParameterHandler实例来实现的&#xff0c;代码比较多&#xff0c;这里挑选&#64;Query来看看。

else if (annotation instanceof Query) {
Query query &#61; (Query) annotation;
String name &#61; query.value();
boolean encoded &#61; query.encoded();
Class rawParameterType &#61; Utils.getRawType(type);// 返回基础的类
gotQuery &#61; true;
// 可以迭代&#xff0c;Collection
if (Iterable.class.isAssignableFrom(rawParameterType)) {
if (!(type instanceof ParameterizedType)) {
throw parameterError(p, rawParameterType.getSimpleName()

  • " must include generic type (e.g., "
  • rawParameterType.getSimpleName()
  • “)”);
    }
    ParameterizedType parameterizedType &#61; (ParameterizedType) type;
    Type iterableType &#61; Utils.getParameterUpperBound(0, parameterizedType);// 返回基本类型
    Converter converter &#61;
    retrofit.stringConverter(iterableType, annotations);
    return new ParameterHandler.Query<>(name, converter, encoded).iterable();
    } else if (rawParameterType.isArray()) {// Array
    Class arrayComponentType &#61; boxIfPrimitive(rawParameterType.getComponentType());// 如果是基本类型&#xff0c;自动装箱
    Converter converter &#61;
    retrofit.stringConverter(arrayComponentType, annotations);
    return new ParameterHandler.Query<>(name, converter, encoded).array();
    } else {// Other
    Converter converter &#61;
    retrofit.stringConverter(type, annotations);
    return new ParameterHandler.Query<>(name, converter, encoded);
    }

在&#64;Query中&#xff0c;将分成Collection、array、other三种情况处理参数&#xff0c;之后根据这些参数&#xff0c;调用ParameterHandler中的Query静态类&#xff0c;创建出一个ParameterHandler实例。这样循环直到解析了所有的参数注解&#xff0c;组合成为全局变量parameterHandlers&#xff0c;之后构建请求时会用到

OkHttpCall

ServiceMethod创建完成之后&#xff0c;我们来看看下一行代码中的OkHttpCall类&#xff0c;里面的包含了请求的执行和响应处理&#xff0c;我们来看看异步请求的做法

OkHttpCall(ServiceMethod serviceMethod, Object[] args) {
this.serviceMethod &#61; serviceMethod;
this.args &#61; args;
}
&#64;Override public void enqueue(final Callback callback) {
checkNotNull(callback, “callback &#61;&#61; null”);
okhttp3.Call call;
Throwable failure;
synchronized (this) {
if (executed) throw new IllegalStateException(“Already executed.”);
executed &#61; true;
call &#61; rawCall;
failure &#61; creationFailure;
if (call &#61;&#61; null && failure &#61;&#61; null) {
try {
call &#61; rawCall &#61; createRawCall();// 创建OkHttp3.Call
} catch (Throwable t) {
failure &#61; creationFailure &#61; t;
}
}
}
if (failure !&#61; null) {
callback.onFailure(this, failure);
return;
}
if (canceled) {
call.cancel();
}
call.enqueue(new okhttp3.Callback() {

资源分享
  • 最新大厂面试专题

这个题库内容是比较多的&#xff0c;除了一些流行的热门技术面试题&#xff0c;如Kotlin&#xff0c;数据库&#xff0c;Java虚拟机面试题&#xff0c;数组&#xff0c;Framework &#xff0c;混合跨平台开发&#xff0c;等

  • 对应导图的Android高级工程师进阶系统学习视频
    最近热门的&#xff0c;NDK&#xff0c;热修复&#xff0c;MVVM&#xff0c;源码等一系列系统学习视频都有&#xff01;

些流行的热门技术面试题&#xff0c;如Kotlin&#xff0c;数据库&#xff0c;Java虚拟机面试题&#xff0c;数组&#xff0c;Framework &#xff0c;混合跨平台开发&#xff0c;等

[外链图片转存中…(img-WajqqjLL-1644035867733)]

  • 对应导图的Android高级工程师进阶系统学习视频
    最近热门的&#xff0c;NDK&#xff0c;热修复&#xff0c;MVVM&#xff0c;源码等一系列系统学习视频都有&#xff01;

[外链图片转存中…(img-xYBDTmpP-1644035867734)]

下载方法&#xff1a;点赞&#43;关注后 点击【Android高级工程师进阶学习】即可领取&#xff01;


推荐阅读
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 使用nodejs爬取b站番剧数据,计算最佳追番推荐
    本文介绍了如何使用nodejs爬取b站番剧数据,并通过计算得出最佳追番推荐。通过调用相关接口获取番剧数据和评分数据,以及使用相应的算法进行计算。该方法可以帮助用户找到适合自己的番剧进行观看。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 本文总结了在开发中使用gulp时的一些技巧,包括如何使用gulp.dest自动创建目录、如何使用gulp.src复制具名路径的文件以及保留文件夹路径的方法等。同时介绍了使用base选项和通配符来保留文件夹路径的技巧,并提到了解决带文件夹的复制问题的方法,即使用gulp-flatten插件。 ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • 本文介绍了如何使用Express App提供静态文件,同时提到了一些不需要使用的文件,如package.json和/.ssh/known_hosts,并解释了为什么app.get('*')无法捕获所有请求以及为什么app.use(express.static(__dirname))可能会提供不需要的文件。 ... [详细]
  • 树莓派语音控制的配置方法和步骤
    本文介绍了在树莓派上实现语音控制的配置方法和步骤。首先感谢博主Eoman的帮助,文章参考了他的内容。树莓派的配置需要通过sudo raspi-config进行,然后使用Eoman的控制方法,即安装wiringPi库并编写控制引脚的脚本。具体的安装步骤和脚本编写方法在文章中详细介绍。 ... [详细]
  • 本文记录了在vue cli 3.x中移除console的一些采坑经验,通过使用uglifyjs-webpack-plugin插件,在vue.config.js中进行相关配置,包括设置minimizer、UglifyJsPlugin和compress等参数,最终成功移除了console。同时,还包括了一些可能出现的报错情况和解决方法。 ... [详细]
  • 欢乐的票圈重构之旅——RecyclerView的头尾布局增加
    项目重构的Git地址:https:github.comrazerdpFriendCircletreemain-dev项目同步更新的文集:http:www.jianshu.comno ... [详细]
author-avatar
htqdw
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有