尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

深挖Kotter Knife设计精髓:自研Lazy懒加载类如何优雅缓存View引用

深挖Kotter Knife设计精髓:自研Lazy懒加载类如何优雅缓存View引用 深挖Kotter Knife设计精髓自研Lazy懒加载类如何优雅缓存View引用【免费下载链接】kotterknifeView injection library for Android.项目地址: https://gitcode.com/gh_mirrors/ko/kotterknifeKotter Knife 是一个 Kotlin 编写的 Android View 绑定库核心思路是属性声明时用by bindView(R.id.xxx)声明 View首次访问时自动查找并缓存结果从此不再重复调用findViewById。全文带读它的核心源码 ButterKnife.kt重点拆解它自研的 Lazy 懒加载类是如何又懒又稳地缓存 View 引用的。一、一句话理解Kotter Knife 解决什么问题 传统写法每次取控件都要写findViewById繁琐且易错。Kotter Knife 用 Kotlin 的属性委托语法把它变成一行声明class PersonView(context: Context) : LinearLayout(context) { val firstName: TextView by bindView(R.id.first_name) val details: TextView? by bindOptionalView(R.id.details) }之后代码里直接用firstName.text ...底层查找由库自动完成。它支持 Activity、Dialog、Fragment、RecyclerView 的 ViewHolder 等 8 类宿主见 ButterKnife.kt 中成对出现的扩展函数。二、四种绑定姿势从必选到列表委托方法行为找不到时bindView绑定单个控件抛异常bindOptionalView绑定单个控件可空返回 nullbindViews批量绑定为List抛异常bindOptionalViews批量绑定缺失项静默跳过返回 null 并过滤四种能力都由两个私有工厂函数生成逻辑一目了然required() 必选查找找不到就抛出IllegalStateException提示View ID x for 属性名 not found便于定位错误optional() 可选查找找不到返回 nullbindOptionalViews还会filterNotNull()自动过滤缺失项。三、核心设计自研 Lazy 类如何缓存 View 引用 Kotter Knife 没有直接用 Kotlin 标准库的lazy因为它需要初始化时拿到宿主对象和属性元数据于是自研了一个仅 12 行的类private class LazyT, V(private val initializer: (T, KProperty*) - V) : ReadOnlyPropertyT, V { private object EMPTY private var value: Any? EMPTY override fun getValue(thisRef: T, property: KProperty*): V { if (value EMPTY) { value initializer(thisRef, property) } return value as V } }完整实现见 Lazy 类定义。拆开看三个设计点都很讲究3.1 哨兵对象 EMPTY区分还没算过和算出来是 null普通懒加载有个经典坑初始化结果可能为null可选绑定的场景若用value null判断是否已初始化每次都会重新查找缓存形同虚设。Kotter Knife 用private object EMPTY作为哨兵占位只有真正算过之后才替换掉它——null 也是合法的缓存值。3.2 只读委托ReadOnlyProperty 天然防篡改Lazy只实现了ReadOnlyProperty属性用val声明无法从外部重新赋值。这与View 引用只应来自布局的语义完全契合。3.3 不加锁的懒加载为 UI 线程量身定制这个getValue没有加synchronized。这是刻意为之Android 的 View 访问本身就要求在主线程为不可能发生的并发加锁纯属浪费。简单、无锁、零额外开销——典型的按场景做减法设计。四、缓存行为验证测试代码是最有说服力的说明单元测试 ViewTest.kt 把缓存行为验证得很直白比如 testFindCached先绑定一次取到控件 ✅调removeAllViews()把子视图全部清空再次访问属性——仍然拿到旧引用✅这正体现了缓存的是首次查找结果一旦初始化值就定格后续即使视图树发生变化也不会重新查找。列表绑定的 testListCaches 同理验证。五、宿主适配Finder typealias 的小技巧 不同宿主取控件的方式各不相同Dialog 可能挂在dialog上Fragment 要在view里找Kotter Knife 用一个函数类型把差异收敛typealias FinderT T.(Int) - View?各宿主只需提供自己的viewFinder扩展属性见 viewFinder 定义比如 DialogFragment 会先试dialog、再兜底view。新增宿主类型时只需按此模式复制粘贴两段代码主流程零改动。六、客观提醒为什么作者后来放弃了它 ⚠️项目 READMEREADME.md中作者明确标注了Deprecated为每个 View 引用都分配一个 Lazy 对象这是一个糟糕的点子并建议改用官方View Binding。这恰是本库最大的教学价值它展示了运行时查找 委托缓存方案的完整面貌也让你看清它的代价——每个绑定属性都会在运行期创建一个对象而 View Binding 是编译期生成代码零反射、零额外对象缓存的是引用视图树变化后不会自动感知。一句话总结Kotter Knife 不适合新项目投产但 这 132 行源码 是学习 Kotlin 属性委托、哨兵对象模式、懒加载缓存的绝佳小样本——读懂它你就理解了优雅背后的每一处权衡。七、快速上手路径 核心源码全部功能ButterKnife.kt行为测试缓存/可选/列表/异常ViewTest.kt版本与发布信息gradle.properties开源协议LICENSE.txtApache 2.0【免费下载链接】kotterknifeView injection library for Android.项目地址: https://gitcode.com/gh_mirrors/ko/kotterknife创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表