当前位置: 首页 > news >正文

Android 第三方框架 相关

1. Retrofit 的工作原理?如何通过动态代理将接口注解转换为网络请求?

Retrofit 是基于 OkHttp 封装的 RESTful 网络请求框架,本身不做实际的网络请求,只负责创建动态代理、接口解析、参数封装、构建请求对象、解析响应数据,最终交给 OkHttp 发送请求。

原理:

  • 先定义网路请求接口,用注解@GET@POST@Path@Query等)标记请求方式、路径参数等;
  • 然后调用retrofit.create(Servcie::class.java),通过动态代理Proxy.newProxyInstance)的方式生成接口的实现类;
  • 调用接口方法时,代理拦截该方法,解析注解、参数、返回类型等(InvocationHandler.invoke),封装成 ServiceMethod 对象;ServiceMethod 对象会根据实际传入的参数,生成OkHttp 的 Request 对象,由OkHttpCall执行网络请求;
  • 最后通过 Converter(如 Gson)将响应数据解析为方法声明的返回值类型

记忆:动态代理拦方法,注解转 Request,OkHttp 发请求,Converter 解响应。


2. 为什么 Retrofit 接口方法不能有重载?

  • 原因:Retrofit 通过方法名注解来唯一确定一个网络请求。重载方法同名但参数不同,动态代理在解析时无法区分,也无法根据参数类型推断注解含义,会导致歧义和实现复杂。
  • 替代:使用不同方法名,或将参数封装成对象。

记忆:重载同名难区分,注解解析易混淆,换不同名或对象。


3. Retrofit 是如何支持协程挂起函数的?

  • Continuation:是 Kotlin 协程里的“续体对象”,本质是一个回调接口,协程挂起时保存状态,恢复时从 Continuation 继续执行(唤醒协程 + 把结果/异常丢出去 + 让代码继续往下执行);

  • suspend函数如何变成 Continuation?

    • Kotlin 编译器遇到suspend函数,会自动做CPS(Continuation Passing Style 续体传递风格)转换。给函数自动加一个Continuation 参数
    • 函数不再直接返回结果,而是把结果交给continuation.resumeWith()
  • Retrofit 依赖 Kotlin 编译器,通过 Continuation 把 OkHttp 异步回调结果交给continuation.resumeWith()

    • 内部自动调度线程:IO 子线程请求,主线程恢复协程更新 UI;

4. 多个 BaseUrl 如何动态切换?

  • 注解 @Url:接口方法不传相对路径,直接传完整全路径 URL,灵活切换;
  • 多个 Retrofit 实例:不同 BaseUrl 单独创建 Retrofit + Service,隔离使用;
  • 全局配置转换器:实现BaseUrl替换工厂,统一规则动态适配域名;
  • 拦截器动态替换:自定义 OkHttp 拦截器,拦截请求,动态修改 Request 的 host 和 path;

5. 如何实现请求缓存(无网络时返回缓存数据)?

  • 方案:结合 OkHttp 的缓存拦截器网络拦截器,配合CacheControl策略。
  • 步骤
    1. 创建 OkHttpCache对象,设置缓存目录和大小。
    2. 添加自定义拦截器:若无网络,强制使用缓存(CacheControl.FORCE_CACHE);若有网络,根据缓存策略决定是否读缓存。
    3. 服务端需支持Cache-Control响应头。
  • Retrofit:通过 OkHttp 的Cache和拦截器实现,对业务层透明。

记忆:OkHttp 缓存 + 拦截器,无网强制缓存,有网按策略走。


6. OkHttp 的拦截器机制(Interceptor Chain)?责任链模式处理请求和响应?

  • 机制:OkHttp 采用责任链模式,多个拦截器形成链式调用,通过Chain.proceed把请求交给下一个拦截器,每个拦截器都可对请求进行处理;
  • 自定义拦截器:可添加应用拦截器(最早执行)或网络拦截器(在连接后执行,监控网络层数据传输);
  • 常见内置拦截器:
    • retryAndFollowUpInterceptor:失败和重定向拦截器。当请求内部抛出异常时,判定是否需要重试,当响应结果是3xx重定向时,构建新的请求并发送请求;
    • BridgeInterceptor:应用层和网络层的桥接拦截器。主要工作是请求添加cookie,添加固定的header,比如Host、Content-Length、Content-Type、User-Agent等等,然后保存响应结果的cookie,如果响应使用gzip压缩过,则还需要解压;
    • CacheInterceptor:缓存拦截器。如果命中缓存,则不发起网络请求;
    • ConnectInterceptor:连接拦截器。内部会维持一个连接池,主要负责TCP连接,包括连接复用、创建连接(三次握手)、释放连接以及创建连接上的socket流;
    • CallServerInterceptor:请求拦截器。在前置准备工作完成后,真正发起网络请求;

记忆:拦截器连成链,proceed 递进,各司其职;应用优先,网络后。


7. 谈谈网络请求中的拦截器(应用拦截器 vs 网络拦截器区别)

类型特点
应用拦截器(addInterceptor最先执行,只执行一次,不关心重定向、重试多次请求;
网络拦截器(addNetworkInterceptor每次重试、重定向都会重复执行,监控网络层数据传输、连接建立的过程

记忆:应用拦截器最早,一次;网络拦截器在连接后,可多次。


8. OkHttp 如何支持 HTTPS?

  • 基本握手与验证
    • 基于SSL/TLS协议建立加密连接(SSL Secure Sockets Layer 安全套接层,TLS Transport Layer Security 传输层安全);
    • 默认信任系统内置的合法 CA 根证书,完成证书链校验;
    • 进行域名校验,防止中间人攻击;
    • 握手完成后使用对称加密传输数据,保证传输安全;
  • 自定义证书配置(如自签名证书)
    • 可自定义SSLSocketFactoryHostnameVerifier,以信任本地自定义证书。
    • 仅测试环境下可忽略证书校验(不推荐生产环境使用)。
  • 证书锁定(Certificate Pinning)
    • 通过CertificatePinner限制只信任特定公钥或证书,进一步防止中间人攻击。

记忆:基于 SSL/TLS 握手加密,默认校验 CA 合法证书链与域名,支持自定义证书、双向认证,加密传输防窃听和中间人攻击。


9. 什么是 HTTP/2 的多路复用?OkHttp 如何利用它?

  • 多路复用

    • 同一个 TCP 连接,可以同时跑多个 HTTP 请求,不用排队,不会阻塞;
    • 请求按帧拆分,交错传输,互不阻塞,解决 HTTP1.1 队头阻塞;
  • OkHttp:OkHttp 默认支持 HTTP/2(如果服务器支持)。它会自动为同一主机使用一个连接进行多路复用,减少连接数,提升性能。

记忆:单连接并发行请求;OkHttp 自动升级 HTTP/2,复用连接。


10. 发生 SocketTimeoutException 时的重试机制?

  • 超时原因:读写超时、网络波动、服务器响应慢;

  • OkHttp:自带默认重试机制,默认自动重试少量次数;

  • 自定义重试:

    • 自定义拦截器捕获 SocketTimeout 异常;
    • 通过循环 + 延迟,手动发起重试;
    • 设置最大重试次数、重试间隔;
  • 只对幂等 GET 请求重试,POST 谨慎避免重复提交;

记忆:Socket 读写超时触发该异常,OkHttp 内置简单重试;可自定义拦截器捕获异常,限制最大次数和间隔,对幂等 GET 做自动重试,POST 慎用。


11. 如何自定义 DNS(Domain Name System 域名系统)?

  • 实现 OkHttp 的Dns接口,重写lookup()方法;
  • 自定义域名解析逻辑:本地静态映射、阿里 / 腾讯公共 DNS、IP 直连;
  • 构建 OkHttpClient 时,通过dns()注入自定义 DNS;
  • 作用:绕过系统 DNS、防 DNS 劫持、域名 IP 直连、智能调度切换 IP。

记忆:实现 OkHttp Dns 接口重写 lookup 方法,自定义域名解析规则,构建 OkHttp 注入,实现 IP 直连、防劫持、自定义公共 DNS。


12. 说说 ViewBinding 和 DataBinding 的区别?DataBinding 是如果实现数据和 UI 的双向绑定?

核心区别:

  • ViewBinding:
    • 只做视图绑定,自动生成布局对应绑定类;
    • 直接引用 XML 控件 ID,无数据绑定、无 MVVM
    • 轻量、编译快、无注解、仅替代 findViewById;
    • 不支持表达式、不支持双向绑定。
  • DataBinding
    • 兼顾视图绑定 + 数据绑定 + MVVM
    • 布局内嵌<layout><data>绑定实体类;
    • 支持表达式、单向 / 双向绑定、事件绑定;
    • 编译慢、复杂度高,适合正式 MVVM 项目;

DataBinding 双向绑定实现原理:

  • 布局中使用@={}语法声明双向绑定;
  • 编译器编译时会为每个布局生成绑定辅助类 XXLayoutBinding
  • 数据实体继承BaseObservable,字段变化时主动通知 UI 刷新;
  • UI 控件编辑变化自动反向同步到数据模型;
  • 底层借助监听器 + 反射 / 注解生成代码,实现 UI ↔ 数据 自动同步。

ViewBinding 可视为 DataBinding 的子集,如果只需视图绑定功能,建议优先使用 ViewBinding。


13. Coil 图片加载库的特点与协程实现

Coil 全称Coroutine Image Loader,是 Kotlin-first 的图片加载库,纯 Kotlin 编写,基于 Kotlin 协程构建。

Coil 核心特点(对比 Glide)

  • 轻量级:API 更简洁优雅,适配 Kotlin 项目,包体积增量远小于 Glide(无多余依赖),但比 Glide 加载慢些;

  • 挂起函数:异步加载通过挂起函数实现,每个图片请求通过suspend fun execute()返回 ImageResult,无需回调。替代 Glide 的线程池 + Handler,更轻量;

    • 协程是 Kotlin 提供的轻量级并发模型,挂起函数是实现协程挂起与恢复的核心机制,二者配合实现非阻塞式异步编程。
  • 生命周期感知:利用CoroutineScope绑定 Activity/Fragment 销毁自动取消请求,避免内存泄漏和无效网络;

  • 缓存机制:与 Glide 一致的双层 LRU(内存缓存存 Bitmap 对象,磁盘缓存存原始数据),但 API 更简化;

  • Jetpack Compose 支持:声明式 API,原生适配 Compose 项目;

  • 线程调度:通过setDecodingDispatchersetFetcherDispatcher分别控制解码和网络/磁盘 IO 的调度器,无需手动切换线程。

  • 流水线处理:缓存查询、网络下载、解码全部在协程中串行排队,线程开销低于传统线程池。

  • 自动暂停/取消:滑动列表时自动暂停未显示的请求,节省网络和 CPU。

  • 内存安全:自动采样策略、Bitmap 复用,降低 OOM 风险。

  • 对比 Glide:若项目大量使用 Kotlin 协程和 OkHttp,Coil 更轻量、更顺手;Glide 加载速度略快,但 Coil 的协程集成和 Compose 支持更好。

记忆口诀:Coil 全称协程加载器,Kotlin-first 轻量级;双层 LRU 内存磁盘,协程 suspend 自动取消;采样降 OOM,Bitmap 复用同 Glide;项目用协程选 Coil,Compose 支持更无敌。


14. Glide 的完整加载流程与缓存机制

三级缓存:Glide 使用ActiveResources(活动资源弱引用)+ MemoryCache(内存缓存 LruCache)+ DiskCache(磁盘缓存 LruCache)

  • ActiveResources:存储当前正在使用的图片(弱引用),界面不展示后移到 MemoryCache。读取优先级最高,活动缓存解决 Lru 缓存时存在的弊端问题。
  • MemoryCache:存储最近释放的图片(LruCache),图片离开界面但还在内存缓存中,下次加载直接复用。
  • DiskCache:分为Resource Cache(形变后缓存,如缩放宽高)和Data Cache(源数据原始缓存),以文件形式存储在应用的私有目录下,采用 Lru 算法。

LRU 全称:最近最少使用算法,优先淘汰长时间未使用的数据

  • 底层:基于 LinkedHashMap,开启访问有序模式。
  • 核心机制:
    • 每次 get/put 访问元素,会将当前元素移至队列头部;
    • 长期不访问的元素会逐步挤到队列尾部;
    • 缓存达到设定阈值时,自动移除尾部最少使用元素;

加载流程(Engine.load 中的缓存读取顺序):内存 → 磁盘 → 网络

  1. 从 ActiveResources 取:弱引用存储正在使用的图片,命中直接返回。
  2. 从 MemoryCache 取:LruCache,命中后移动到 ActiveResources。
  3. 从 DiskCache 取:先查 Resource Cache(已处理图),再查 Data Cache(原始数据)。
  4. 从网络/源头加载:以上都没有,开启 EngineJob 进行网络下载,解码后存入各级缓存[reference:6]。

缓存 Key 生成机制:根据图片 URL、宽高、变换参数等信息唯一标识,确保不同参数的同一图片单独缓存。

记忆口诀:活动引用正在用,Lru 内存暂存放,两级磁盘分形变与原始,BitmapPool 复用降 GC,缓存读取顺序:活动→内存→磁盘→网络。


15. EventBus 的原理、注解反射与线程调度

EventBus 通过发布/订阅事件总线实现组件间通信,广泛用于 Android 模块解耦。

订阅、发布实现:

  • 订阅@Subscribe注解标记订阅方法,方法必须有且仅有一个参数(事件类型)。EventBus 3.x 优先通过APT(Annotation Processing Tool 注解处理器)编译生成订阅者索引,避免反射;未配置或无法索引时回退到反射生成;
    • @Subscribe标记订阅方法,指定线程模式、是否粘性、优先级
  • 发布事件:调用post()发送事件,根据事件类型从映射表找到所有订阅该事件的方法;
  • 遍历回调所有订阅方法,完成事件分发;

事件发布与线程调度

  • post发布事件:根据事件类型在subscriptionsByEventType中找到所有订阅者,遍历调用postToSubscription
  • 线程调度@Subscribe(threadMode = ...)支持 4 种模式:
    • POSTING:当前线程(发布线程)直接回调;
    • MAIN:切到主线程执行(通过MainHandler发送消息)。
    • BACKGROUND:切到后台线程(单线程池)。
    • ASYNC:异步线程池独立执行。
  • 粘性事件:发送后缓存事件,后续注册的订阅者能收到之前已发送的粘性事件
    • postSticky先缓存到stickyEventsMap,后续注册粘性订阅者时立即收到。

记忆口诀:EventBus 发布/订阅模式,@Subscribe 标记订阅。APT 索引编译时生成,反射提速性能优。四种 ThreadMode 调度灵活,post 事件遍历分发,Sticky 存 Map 后注册也能接。


16. LeakCanary 的工作原理与内存泄漏检测

LeakCanary 内存泄漏检测通过弱引用 + 引用队列 + 堆快照分析实现,自动检测并报告内存泄漏。

详细工作原理:

  • 监听组件生命周期:自动监听Activity、Fragment、View、ViewModel销毁时机(Application.registerActivityLifecycleCallbacks());
  • 标记待回收对象:页面销毁后,将本该被 GC 回收的对象加入弱引用队列,关联唯一 Key;
  • 主动触发 GC:延迟一定时间后,手动触发 GC,等待虚拟机回收无用对象;
  • 判断是否泄漏:检查弱引用队列,对象未被回收,说明存在强引用持有,判定内存泄漏
  • 堆快照分析:生成 hprof 堆文件,解析引用链,找出是谁持有导致对象无法回收;
  • 展示泄漏:在桌面弹窗展示泄漏位置、引用链路,方便定位修复;

检测原理:当 JVM 进行垃圾回收时,如果对象只有弱引用存在,该对象会被回收,弱引用进入引用队列;反之若引用队列中没有该弱引用,说明对象仍有其他强引用持有,判定为内存泄漏。

常见配置DebuggerControl(调试模式禁用检测)、watchDurationMillis(5s 等待 GC 窗口)、ReferenceMatcher用于忽略系统已知的泄漏(如某些 Android 版本 View 的 mContext 泄露)。

记忆口诀:弱引用绑定观察对象,GC 后查引用队列判泄漏。堆转储分析引用链,报告分类应用或库。ContentProvider 自动初始化,Activity 泄漏自动检测。


http://www.jsqmd.com/news/786557/

相关文章:

  • Visual Annotator:提升AI编程效率的网页标注工具实战指南
  • SquareBox:声明式本地开发环境管理工具的设计与实践
  • 基于语义搜索的颜文字AI生成器:从NLP原理到工程实践
  • Taotoken模型广场如何帮助开发者根据任务与预算选择合适的模型
  • 脉冲神经网络在医疗边缘计算中的能效革新
  • 移动云怎样保护用户数据的安全?
  • AI伦理测试框架:如何系统性评估算法的公平性
  • sqli-labs通关指南(21-30)
  • 股市赚钱学概论:答疑:资金量的四个阶段
  • AtlasMemory:为AI编程助手构建代码记忆与证据系统
  • AI代码生成革命:开发者效率提升300%实战指南
  • cann/hccl超节点间算法支持
  • 2026年重卡离合器选购指南:如何一眼分辨“好离合器”?
  • 基于智能合约与AgentEscrowProtocol构建AI Agent去中心化支付信任层
  • 如何在Dev-C++中切换编译器为TDM-GCC
  • IDE内嵌AI产品副驾驶:用对话式工作流实现文档即代码
  • 网红酒店|基于java的网红酒店预定系统(源码+数据库+文档)
  • Scrapy-Pinduoduo:构建企业级拼多多电商数据智能采集系统的高效解决方案
  • 基于RTL的双向数据合成训练专用LLM生成硬件断言
  • 光储复合多功能变流器协同控制与电能治理方法【附仿真】
  • 【2026】企业工商照面信息查询:深入了解企业的33项核心数据
  • 基于MCP协议构建AI智能体工具服务器:原理、安全与实践
  • CANN评测MHA算子描述
  • 想快速处理音频文件?2026年音频转文字在线操作方法的5个实测方案
  • 智能眼镜系统架构与PSOC™ Edge MCU技术解析
  • CANN ops-nn GeGluV2算子
  • 衍射层析成像技术:原理、优化与医学应用
  • AI编程助手如何对抗能力错觉?agentic-learning技能包实战指南
  • 告别课堂赴一线,探秘企业知发展 —— 文理基础学院开展名企走访职业启蒙教育
  • Cursor云端智能体HTTP客户端实战:soenneker库配置与优化指南