Jetpack 组件按职责天然分族:数据与状态、UI 绑定、生命周期、依赖注入。同族组件往往配合使用——ViewModel 暴露 LiveData、ROOM 查询能返回 LiveData 或 Flow、DataStore 和 ROOM 按数据形态分工。
一、数据与状态:ViewModel 管状态、LiveData 管观察、ROOM 与 DataStore 分管存储
ViewModel:状态从组件生命周期里抽出来
ViewModel 把界面状态从 Activity/Fragment 生命周期里抽出来,配置变更后由 ViewModelStore 按 key 复用同一实例。onCleared 里取消协程与解绑。AndroidViewModel 可拿 Application,不要持有 Activity 或 View。SavedStateHandle 能在进程被杀后恢复。配合 LiveData/StateFlow 向外暴露不可变状态。Factory 与 CreationExtras 处理有参构造;Hilt 则用 @HiltViewModel。共享 ViewModel 用 activityViewModels(),注意作用域过大导致泄漏。测试可用 ViewModel 纯单元测,不必起 Activity。viewModelScope 默认 Main.immediate,切 IO 时记得把结果切回 UI 线程。
LiveData:带生命周期的可观察数据
LiveData 是带生命周期的可观察数据:STARTED 才分发,DESTROYED 自动移除。observe 绑 LifecycleOwner,observeForever 必须手动 remove。postValue 可在后台线程,setValue 必须主线程。粘性/倒灌:新订阅者会立刻收到最后一条,页面重建可能重复消费事件;单次事件可用 SingleLiveEvent 或封装 Event 包装类。MediatorLiveData 适合组合多个源。LiveDataBus 用总线解耦,但隐式依赖难追踪,优先 ViewModel 共享。不要在 observe 里再改同一 LiveData 造成循环。测试用 InstantTaskExecutorRule。
ROOM:把 SQLite 类型安全化
Room 用 Entity、Dao、Database 三件套把 SQLite 类型安全化。查询可返回 LiveData 或 Flow,在 ViewModel 里收集。版本升级写 Migration,测试用 MigrationTestHelper;失败策略有 fallbackToDestructiveMigration,会丢数据。exportSchema 便于审差异。预填充可用 createFromAsset。加密常用 SQLCipher 封装。主线程查询默认禁止,必须指定 allowMainThreadQueries 仅调试。索引与外键写在 Entity 上。TypeConverter 处理枚举与日期。多表关系用 @Relation 小心 N+1。和 DataStore 分工:结构化数据走 Room,简单配置走 DataStore。
DataStore:SharedPreferences 的继任者
DataStore 是 Jetpack 对 SharedPreferences 的继任:Preferences DataStore 用键值,Proto DataStore 用强类型 protobuf。读写都走协程/Flow,避免主线程卡顿与 apply 队列导致的 ANR。迁移 SP 可用 SharedPreferencesMigration。多进程要用 MultiProcessDataStore 或换 MMKV。更新用 updateData 保证原子读写,不要先 read 再 write 产生竞态。序列化异常要提供 corruptionHandler。文件落在 files/datastore。测试可用测试调度器注入。和 Room 的边界:少量配置用 DataStore,关系数据仍走数据库。
三者按数据形态分层:界面状态用 ViewModel + LiveData/StateFlow 暴露;结构化、可查询的数据用 ROOM;仅键值/强类型配置用 DataStore,不是替代关系。
二、UI 绑定:DataBinding 与 ViewBinding 怎么选
DataBinding:在 XML 里写表达式
DataBinding 在 XML 里写表达式,编译期生成 Binding 类,把 View 与数据对象连起来。事件绑定可用 lambda 或方法引用;include 布局会生成子 Binding。自定义属性靠 @BindingAdapter,图片加载、本地资源可用重载区分。双向绑定用 @={},数据侧用 BaseObservable 或 ObservableField,记得 notify。RecyclerView 把 item Binding 放进 ViewHolder。和 ViewModel+LiveData 搭配时,lifecycleOwner 要设对,否则观察不到。相比 ViewBinding,它更重、构建更慢。新项目能不用就不用。注意循环绑定与主线程刷新。BindingAdapter 要做成静态且无副作用。
ViewBinding:编译期生成的类型安全绑定
ViewBinding 在编译期为每个布局生成绑定类,字段对应带 id 的控件,类型安全且空安全。Activity 用 inflate,Fragment 要在 onDestroyView 把 _binding 置空,避免持有旧视图。include 会嵌套生成子 Binding。与 DataBinding 不同,它不做表达式和双向绑定,APT 开销更小。merge 根布局要用 bind(parent) 而不是 inflate。RecyclerView 的 ViewHolder 持有 item Binding 即可,不必再 findViewById。模块开启 viewBinding true 后注意资源混淆与 flavor 下布局同名。自定义 View 内部也可 inflate Binding。迁移 ButterKnife 时先替换注解字段,再删掉 unbinder。
只想要类型安全、空安全的视图引用用 ViewBinding(APT 开销小、构建快);确实需要在 XML 里写表达式、做双向绑定时才用 DataBinding,且注意构建成本。
三、生命周期:LifeCycle 与 AppStartUp
Lifecycle:把生命周期建模成状态机
Lifecycle 把 ON_CREATE 到 ON_DESTROY 建模成状态机,Observer 只在至少 STARTED 时活跃。LifecycleOwner 由组件实现,LifecycleRegistry 派发事件。DefaultLifecycleObserver 比注解更清晰。LifecycleService 让 Service 也能被观察;ProcessLifecycleOwner 表示整个应用前后台,适合埋点与连接管理,不要拿它当单 Activity 生命周期。repeatOnLifecycle 是协程里收集 Flow 的推荐写法。自定义 Owner 时要按序 dispatch,避免在 DESTROYED 后再加观察者。与 ViewTreeLifecycleOwner 结合,Compose/自定义 View 才能正确感知窗口销毁。
App Startup:把初始化收拢成拓扑序
App Startup 把 ContentProvider 散落的初始化收拢到 InitializationProvider,用 Initializer 声明依赖并按拓扑序执行。可在 manifest 里 disable 自动发现,改由 AppInitializer 按需触发,避免冷启动拖死 Application.onCreate。实现 Initializer<T> 时要声明 dependencies(),WorkManager、EmojiCompat 都是这套模型。注意主线程与后台线程:库初始化若不依赖 UI,尽量推到后台并用跟踪测耗时。多模块工程里让每个 feature 提供自己的 Initializer,主工程只调用图的入口。若与 Hilt 搭配,先保证 Application 已创建再去取 EntryPoint,否则容易遇到过早注入。
需要随组件生命周期启停的逻辑(协程、连接、观察)都基于 Lifecycle;多库冷启动初始化拖慢 Application.onCreate 时,用 App Startup 收敛并按拓扑序执行。
四、依赖注入:Hilt 在标准组件上套一层
Hilt 在 Dagger 上套标准组件:SingletonComponent、ActivityRetained、ViewModel、Activity、Fragment。@HiltAndroidApp 生成入口,@AndroidEntryPoint 让页面可 @Inject。模块用 @InstallIn 声明作用域。ViewModel 用 @HiltViewModel + @Inject 构造,AssistedInject 处理运行时参数。多绑定 @IntoSet/@IntoMap 适合插件化初始化。测试用 @HiltAndroidTest 与 @UninstallModules 替换假实现。常见坑是作用域不匹配:把 Activity 依赖放进 Singleton。Hilt 不直接支持跨模块可选依赖,需要用入口聚合。编译慢时可开 incremental 并减少不必要的 @InstallIn 扫包。
需要统一、可测试的依赖装配时上 Hilt;配合 ViewModel 用 @HiltViewModel,配合 App Startup 时注意 Application 创建顺序。
附:新老替代与分工对照表
| 维度 | 老 / 另一种 | 新 / 继任 | 判断与边界 |
|---|---|---|---|
| 键值配置存储 | SharedPreferences(apply 队列、主线程风险) |
DataStore(Preferences/Proto) |
DataStore 是 SP 的继任,读写走协程/Flow,避免主线程卡顿与 ANR |
| 视图绑定 | findViewById / ButterKnife |
ViewBinding |
类型安全、空安全,APT 开销小;ButterKnife 迁移先换注解字段再删 unbinder |
| XML 表达式绑定 | 无(手写 setText 等) |
DataBinding |
更重、构建更慢,新项目能不用就不用;仅需绑定用 ViewBinding |
| 结构化数据 | 裸 SQLite / 不该塞 DataStore |
ROOM |
关系数据走 ROOM,少量配置才走 DataStore |
| 数据流观察 | LiveData(界面状态) |
Flow(协程数据流,ROOM/DataStore 均可返回) |
LiveData 配 ViewModel 暴露界面状态;Flow 用 repeatOnLifecycle 收集,二者按场景分工而非简单替换 |
| 散落初始化 | 各库 ContentProvider |
App Startup |
收拢成初始化拓扑序,避免冷启动拖死 Application.onCreate |
Jetpack 组件速查:按族归类,而不是逐个记 API
https://lautung.com/archives/jetpack-components
评论