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 视图时,执行顺序:
- 先查 AttachedScrap布局阶段优先从 Scrap 寻找匹配 viewType、position 的 VH;找到直接复用,不 bind。
- 找不到 → 查询mCachedViews(Cache 缓存)匹配 viewType,找到直接复用,不 bind。
- 找不到 → 查询ViewCacheExtension(自定义缓存)
- 找不到 → 查询RecycledViewPool找到 ViewHolder,需要调用
onBindViewHolder绑定数据 - Pool 也没有 →LayoutInflater.inflate 创建新 View + 新建 ViewHolder
三、回收流程
LayoutManager 滚动时回收滑出屏幕的 ViewHolder:
- 先判断能不能放进
mCachedViews:- Cache 没满 → 存入 Cache;
- Cache 已满 → 将队列头部旧 VH 踢出,丢进 RecycledViewPool;
- 进入 Pool 前,会清空 ViewHolder 内部的数据、位置信息;
注意:Scrap 里的条目不会直接进入 Pool,布局结束后优先尝试进入 Cache。