客户端面试八股文-Android第三方库
2026/7/26 8:54:57 网站建设 项目流程

okhttp拦截器

okhttp拦截器分为两大类:应用拦截器,网络拦截器,以及内部封装的系统拦截器,通过责任链模式串联执行

执行链路顺序:

1、自定义应用拦截器:最先执行,最早拿到原始请求,最晚拿到响应

2、RetryAndFollowUpInterceptor:重试重定向拦截器,处理IO异常重试,301/302重定向,401鉴权等,限制最大重定向次数默认20次

3、BridgeInterceptor:桥拦截器(补全请求头,解析响应编码),上层业务与底层网络的桥梁,自动补全Content-Type,User-Agent,Accept-Encoding,响应自动解压,封装字符编码

4、CacheInterceptor:缓存拦截器,处理本地缓存逻辑

5、ConnectInterceptor 建立TCP/SSL链接,从连接池RealConnectionPool获取可用TCP链接,无空闲链接则新建Socket,完成SSL握手

6、自定义网络拦截器 只走真实网络请求,缓存命中不走这里

7、CallServerInterceptor 真正发请求

责任链RealInterceptorChain原理:

核心:

1、每一层拦截器调用chain.proceed()才会放行给下一个拦截器

2、执行是递归链式:先从上倒下处理请求,全部放行后,再从下到上回传响应

3、每一层都可以:修改请求,直接返回自定义Response,拿到上层返回的响应再加工

Retrofit 动态代理

Retrofit 是基于 OkHttp 封装的 RESTful HTTP 请求框架,由 Square 公司开源;它本身不直接做网络 IO,只是一套请求声明 + 解析转换的上层封装,底层完全依赖 OkHttp 完成真实网络请求。

Android优化方面:

内存优化:

内存泄漏优化:

1、Handler使用静态内部类+弱引用WeakReference包裹activity

2、所有注册类(广播,定位,传感器)onDestroy统一unregister

3、单例统一传入getApplicationContext 禁止传Activity

4、Webview独立进程,销毁时webview.destroy 移除父布局

5、页面退出清空集合,置空webview,取消网络请求call.cancel()

6、异步线程不要持有页面引用,避免页面关闭线程仍在运行

图片内存优化:

1、大图压缩:

2、图片格式选择:webp<png,

3、图片缓存三级缓存:内存LRU + 磁盘缓存,内存缓存设置合理阈值

4、Glide/Coil自动复用Bitmap池,禁止自己new Bitmap

5、大图下载加@Streaming,分段式读取,不一次性加载进内存

其他内存优化:

避免内存抖动:

减少匿名临时对象,字符串拼接用StringBuider

布局复用 include,merge,减少view对象数量

大数组按需创建,用完及时置空

使用LeakCanary检测泄漏,Profiler查看堆内存,对象实例数量

冷启动优化

Application瘦身

1、不要再主线程做初始化

2、延迟加载库

3、子线程初始化SDK,禁止阻塞主线程

4、区分渠道,调试环境,Debug只加载必要组件

页面布局优化:

首页布局扁平化,减少层级,移除冗余嵌套

延迟加载非关键UI

避免onCreate,onResume做大量耗时计算

业务逻辑延迟:

首页网络请求延迟到页面渲染完成

预加载,缓存本地数据,启动先展示缓存,再刷新网络

数据库操作放子线程

卡顿优化

主线程耗时操作转移至子线程

网络请求,数据库读写,文件IO,大图解码,JSON解析等

布局绘制优化

减少布局层级,使用ConstraintLayout代替LinearLayout多层嵌套

减少过度绘制,

减少自定义View onDraw内复杂逻辑,循环,频繁new对象

图片避免clipPath,模糊渲染等昂贵渲染操作

内存抖动

复用对象池

列表滑动卡顿

1、复用ViewHolder,禁止在onBindViewHolder创建对象

2、图片加载异步,滑动时暂停加载

3、减少Item布局层级

4、使用局部刷新

5、滑动时不做耗时操作

包体积优化:

资源压缩

图片:png转webp,大图压缩,删除无用图片

矢量图替代多张分辨率png

无用资源清理:开启shrinkResource,自动删除未引用资源

音视频压缩

代码优化

1、开启混淆

2、第三方SDK裁剪,按需引入模块

3、移除重复依赖,统一版本,implementation改为api按需调整

4、业务废弃类,工具类及时删除

原生so库转化

只保留目标ABI架构

压缩so,去除调试符号

拆分功能so,动态下发

打包配置

开启资源压缩,代码混淆

动态功能模块,非核心业务分包,按需下载

弱网优化:

请求层okhttp优化:

1、调整超时时间,弱网延长

2、失败重试机制

3、请求防抖:重复请求合并,短时间多次相同接口只发一次

4、拦截器统一捕获IO异常,超时异常,友好提示用户

缓存策略

1、本地持久缓存接口数据,弱网优先展示缓存

2、区分实时接口和非实时接口,非实时开启HTTP缓存

3、数据库缓存首页列表,基础配置

业务层策略

1、大文件断点续传,分块上传/下载

2、表单数据本地持久化,弱网下离线缓存,网络恢复自动重发

3、图片分步加载:先缩略图

4、网络状态监听:无网/弱网弹窗提示

流量节省

弱网下关闭预加载,轮询,长连接心跳拉长间隔;图片自动降质

kotlin语法糖

kotlin协程 withContext,launch,async, await dispatchers

kotlin顶级函数,扩展函数, koltin内联函数,kotlin by lazy 单例

顶级函数

不包裹在class/object/interface内部,直接写在kt文件最外层的函数,属于文件级全局函数,

原理是:Kotlin编译后会生成文件名+kt的静态工具类,函数全部变成public static方法

扩展函数

作用:在不修改原有类源码,不继承的前提下,给已有类新增方法

底层:不是真正给类新增成员方法,编译后是静态工具方法,接受对象作为第一个参数

内联函数 inline

需要的原因:普通高阶函数(lambda),会生成匿名内部类对象, 频繁调用造成内存抖动,gc频繁

inline会在编译时把函数体+lambda代码直接拷贝到调用处,不生成额外class,不创建对象,消除开销

1、inline:整个函数内联,lambda直接复制到调用点,支持lambda内return(非局部返回,直接退出外层函数)

2、noinline:多个lambda参数时,不想某一个被内联,加noinline,该lambda保留对象引用,可当做参数传递

3、crossinline:禁止lambda内部使用非局部return,只能return@label;多用于lambda会被嵌套运行(如Runnable,协程调度)

缺点:代码膨胀,多处调用会复制大量代码,方法体超大的不建议inline

by lazy 延迟初始化 & Kotlin 四种单例

by lazy延迟初始化

懒加载:声明时不执行,第一次使用才创建;只初始化一次,后续直接取缓存值;只能修饰val,不能修饰var;默认同步锁,多线程安全

kotlin object class data class伴生对象

data class 数据类

专门用来承载数据,编译期自动生成样板代码,约束:

1、主构造必须至少一个参数

2、参数建议用val

3、不能继承其他类,可实现接口

伴生对象 companion object

写在普通类内部的companion object,一个类只有一个伴生对象

1、基础作用:替代 java static静态方法/静态变量

kotlin没有static关键词,全靠伴生对象实现

2、底层原理:companion object会生成一个静态内部类,并在外部类持有 public static final Companion INSTANCE

RecyclerView缓存详解

目标:减少inflate()创建view开销,减少onBindViewHolder()数据绑定开销,提升滑动流畅度

缓存优先级 Scrap --- Cache --- ViewCacheExtension --- RecycledViewPool

概念:

1、ViewHolder:承载View实例 + viewType + position + 绑定的数据状态

2、Scrap / Cache 保存完整绑定好的ViewHolder (持有效数据)

3、RecycledViewPool 只缓存裸View,数据全部清空,取出必须重新bind

4、触发回收:列表滚动,Item滑出屏幕,LayoutManager负责回收,复用调度

一、缓存拆解:

1、Scrap(临时一级缓存,Scrap Heap)

存储位置:RecyclerView.mAttachedScrap

容量:无固定上限,存放当前屏幕上正在布局的Item

生命周期:仅在layout()布局流程内临时生效,布局完成就清空

放入:布局开始前,把屏幕上所有可见的ViewHolder先丢进Scrap缓存

取出:布局过程中,优先从Scrap拿ViewHolder复用

取出后:不需要重新inflate,不需要重新onBindViewHolder

使用场景:调用notifyItemChanged,局部刷新,滚动重新布局时,屏幕上还在显示的条目直接复用Scrap中的ViewHolder,避免重复绑定

特点:临时缓存,不能跨布局周期复用;布局结束时所有Scrap条目会被转移到Cache或者Pool

2、Cache(二级缓存,mCachedViews)

容量:默认=2,可以手动设置

存放:刚滑出屏幕,相邻的少量ViewHolder

核心特性:从cache取出,无需inflate,无需执行onBindViewHolder(数据保留)

这是滑动体验最优的缓存

淘汰规则:Cache是先进先出队列,缓存满了后,最先进入的Cache的ViewHolder会被移出Cache,进入RecycledViewPool

(Cache区分ViewType,不同类型互不干扰,Cache里的ViewHolder仍然记录原有Position,适合邻近来回滑动的场景)

3、ViewCacheExtension(三级扩展缓存)

系统默认空实现,开发者业务实现

4、RecycledViewPool(四级缓存,回收池)

关键区分:Cache保存(带数据的ViewHolder)

RecycledViewPool只缓存View视图,清空原有绑定数据

1、按viewType分组缓存,不同viewType条目分开存放

2、默认每个viewType最多缓存5个ViewHolder,可手动调整容量

3、取出规则:拿到view实例,必须重新执行onBindViewHolder

二、复用查找流程

当 RecyclerView 需要一个新 Item 视图时,执行顺序:

  1. 先查 AttachedScrap布局阶段优先从 Scrap 寻找匹配 viewType、position 的 VH;找到直接复用,不 bind。
  2. 找不到 → 查询mCachedViews(Cache 缓存)匹配 viewType,找到直接复用,不 bind。
  3. 找不到 → 查询ViewCacheExtension(自定义缓存)
  4. 找不到 → 查询RecycledViewPool找到 ViewHolder,需要调用onBindViewHolder绑定数据
  5. Pool 也没有 →LayoutInflater.inflate 创建新 View + 新建 ViewHolder

三、回收流程

LayoutManager 滚动时回收滑出屏幕的 ViewHolder:

  1. 先判断能不能放进mCachedViews
    • Cache 没满 → 存入 Cache;
    • Cache 已满 → 将队列头部旧 VH 踢出,丢进 RecycledViewPool;
  2. 进入 Pool 前,会清空 ViewHolder 内部的数据、位置信息;

注意:Scrap 里的条目不会直接进入 Pool,布局结束后优先尝试进入 Cache。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询