Android组件化之数据共享与生命周期管理
今日目标
- 掌握组件间数据共享的3种核心方式(DataStore、MMKV、全局单例)。
- 精通组件生命周期的管理方法(加载、卸载、生命周期联动)。
- 规避数据共享和生命周期管理的高频踩坑点,完成组件化工程的综合优化与完整测试。
- 复盘组件化知识形成体系。
组件间数据共享
- 需求:组件间需共享全局数据,要求数据可跨组件访问、修改,且保证数据的一致性、安全性,避免组件间直接依赖导致耦合过高。
- 例:用户登录状态、全局配置、主题设置等
围绕数据共享的3种方式展开
- DataStore
- MMKV
- 全局单例
DataStore
基于Google官方DataStore,封装全局工具类,实现跨组件异步数据读写,解决SharedPreferences的ANR隐患。
- 1.版本管理
libs.versions.toml文件中添加DataStore依赖版本
[versions] # 版本
...
datastore = "1.1.2"
coroutines = "1.7.3"
[libraries] # 依赖库
...
androidx-datastore = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "datastore" }
kotlinx-coroutines = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "coroutines" }
...
- 2.base模块的build.gradle.kts,通过api引入DataStore依赖,其他模块间接依赖
...
dependencies {
...
// 协程用于DataStore异步操作
implementation(libs.kotlinx.coroutines)
api(libs.androidx.datastore)
}
- 3.base模块中封装DataStore工具类
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "global_datastore")
/**
* DataStore工具
*/
class DataStoreHelper private constructor() {
// 初始化DataStore,名字与SP一致,便于替换
private val dataStore = AppHelper.mContext.dataStore
// 写入String类型数据,异步避免ANR
suspend fun putString(key: String, value: String) {
dataStore.edit { preferences ->
preferences[stringPreferencesKey(key)] = value
}
}
// 读取String类型数据,返回Flow,可观察数据变化,适合跨组件监听
fun getStringFlow(key: String, def: String = ""): Flow<String> {
return dataStore.data
.catch { e ->
if (e is IOException) {
// 文件损坏
emit(emptyPreferences())
} else {
throw e
}
}
.map { preferences ->
preferences[stringPreferencesKey(key)] ?: def
}
}
// 读取String类型数据,单次读取,无需观察变化
suspend fun getString(key: String, def: String = "") : String {
return getStringFlow(key, def).first()
}
// 写入Boolean类型数据(例:全局状态值)
suspend fun putBoolean(key: String, value: Boolean) {
dataStore.edit { preferences ->
preferences[booleanPreferencesKey(key)] = value
}
}
// 读取Boolean类型数据,返回Flow,可观察数据变化,适合跨组件监听
fun getBooleanFlow(key: String, def: Boolean = false): Flow<Boolean> {
return dataStore.data
.catch { e ->
if (e is IOException) {
// 文件损坏
emit(emptyPreferences())
} else {
throw e
}
}
.map { preferences ->
preferences[booleanPreferencesKey(key)] ?: def
}
}
// 读取Boolean类型数据,单次读取,无需观察变化
suspend fun getBoolean(key: String, def: Boolean = false) : Boolean {
return getBooleanFlow(key, def).first()
}
// 移除指定key数据
suspend fun remove(key: String) {
dataStore.edit { preferences ->
preferences.remove(stringPreferencesKey(key))
preferences.remove(booleanPreferencesKey(key))
}
}
// 清除所有数据
suspend fun clear() {
dataStore.edit { preferences ->
preferences.clear()
}
}
companion object {
@Volatile
private var instance: DataStoreHelper? = null
fun getInstance(): DataStoreHelper =
instance ?: synchronized(this) { instance ?: DataStoreHelper().also { instance = it } }
}
}
- 4.其他组件中使用
// 写入数据
lifecycleScope.launch {
withContext(Dispatchers.IO) {
DataStoreHelper.getInstance().putString("key", "value")
}
}
// 单次读取(适合无需监听数据变化的场景)
lifecycleScope.launch {
withContext(Dispatchers.IO) {
DataStoreHelper.getInstance().getString("key", "value")
}
}
// 监听数据变化(适合数据实时同步的场景,如用户状态变化)
lifecycleScope.launch {
withContext(Dispatchers.IO) {
DataStoreHelper.getInstance().getBooleanFlow("is_login", false).collect { isLogin ->
}
}
}
- 5.确保DataStore相关类不被混淆
MMKV
腾讯主流高效存储框架,替代SharedPreferences,支持复杂数据存储,效率更高、支持多进程。
- 1.版本管理
libs.versions.toml文件中添加MMKV依赖版本
[versions] # 版本
...
mmkv = "2.3.0"
[libraries] # 依赖库
...
mmkv = { group = "com.tencent", name = "mmkv", version.ref = "mmkv" }
...
- 2.base模块的build.gradle.kts,通过api引入DataStore依赖,其他模块间接依赖
...
dependencies {
...
api(libs.mmkv)
}
- 3.Application中初始化MMKV
@HiltAndroidApp // 依赖注入
class App : Application() {
...
override fun onCreate() {
super.onCreate()
val rootDir = MMKV.initialize(this)
}
...
}
- 4.base模块中封装MMKV工具类
/**
* MMKV工具
*/
class MMKVHelper private constructor() {
// 获取全局MMKV实例
val mmkv: MMKV by lazy { MMKV.defaultMMKV() }
// 写入String类型数据
fun putString(key: String, value: String) {
mmkv.encode(key, value)
}
// 读取String类型数据
fun getString(key: String, def: String = ""): String = mmkv.decodeString(key, def) ?: ""
// 写入Boolean类型数据
fun putBoolean(key: String, value: Boolean) {
mmkv.encode(key, value)
}
// 读取Boolean类型数据
fun getBoolean(key: String, def: Boolean = false): Boolean = mmkv.decodeBool(key, def) ?: false
// 写入实体类数据,支持序列化
fun <T : Parcelable> putParcelable(key: String, value: T) {
mmkv.encode(key, value)
}
// 读取实体类数据
fun <T : Parcelable> getParcelable(key: String, def: Class<T>): T? = mmkv.decodeParcelable(key, def)
// 移除指定key数据
fun remove(key: String) {
mmkv.remove(key)
}
// 清除所有数据
fun clear() {
mmkv.clearAll()
}
companion object {
@Volatile
private var instance: MMKVHelper? = null
fun getInstance(): MMKVHelper =
instance ?: synchronized(this) { instance ?: MMKVHelper().also { instance = it } }
}
}
- 5.其他组件中使用
// 写入数据
MMKVHelper.getInstance().putBoolean("is_login", false)
// 读取数据
MMKVHelper.getInstance().getBoolean("is_login", false)
// 读取实体类数据,实体类需实现序列化
MMKVHelper.getInstance().getParcelable("is_login", T::class.java)
- 6.确保MMKV相关类不被混淆
全局单例
全局单例适合存储临时全局数据(无需持久化),结合Hilt依赖注入实现单例管理,避免手动创建实例,降低耦合
- 1.管理版本
libs.versions.toml中添加hilt依赖版本
[versions] # 版本
...
hilt = "2.57.2" # Hilt插件版本
ksp = "2.2.20-2.0.4" # ksp注解插件版本
[libraries] # 依赖库
...
hilt = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }
hilt-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" }
[plugins] # 插件
...
hilt-android-gradle = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
- 2 base模块的build.gradle.kts,引入Hilt依赖
plugins {
...
// 依赖注入
id("dagger.hilt.android.plugin")
// 启用KSP,用于Hilt生成代码
id("com.google.devtools.ksp")
}
...
dependencies {
...
// 依赖注入
implementation(Lib.hilt)
ksp(Lib.hilt_compiler)
}
- 3.创建全局单例类,存储临时全局数据,进程杀死后丢失,无需持久化
/**
* 创建全局单例类
* 存储临时全局数据,进程杀死后丢失,无需持久化
* 例:当前页面状态
*
* 结合Hilt实现单例,替代手动单例
*/
@Singleton // Hilt单例注解,与Application生命周期一致
class GlobalDataManager @Inject constructor() {
var currPage: String = "main" // 当前显示页面
var isAppForeground: Boolean = false // App是否在前台
}
- 4.在Hilt Module中提供单例实例
/**
* Hilt Module提供单例实例
*/
@Module
@InstallIn(SingletonComponent::class)
class BaseModule {
// 提供GlobalDataManager单例(Hilt自动注入,无需手动创建)
@Singleton
@Provides
fun provideGlobalDataManager(): GlobalDataManager {
return GlobalDataManager()
}
}
- 5.其他组件中使用,Hilt注入,无需手动获取
/**
* @AndroidEntryPoint 依赖注入
*/
@AndroidEntryPoint
class MainActivity: AppCompatActivity() {
...
// Hilt注入全局单例
@Inject
lateinit var globalDataManager: GlobalDataManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_main)
// 写入
globalDataManager.currPage = "main"
// 换成其他组件读取
val currPage = globalDataManager.currPage
}
}
- 6.确保相关类不被混淆
3种数据共享方式对比
- ① DataStore:Google官方推荐,替代SharedPreferences,基于Flow实现,支持异步操作,避免ANR,适合存储轻量键值对(例:用户ID、登录状态等)。
- ② MMKV:腾讯开源高效存储框架,基于mmap,效率远超SharedPreferences,支持多进程,适合存储中等体量数据(例:用户信息、配置参数等),高性能场景首选。
- ③ 全局单例:适合存储临时全局数据(例:当前页面状态、临时缓存等),内存中存储,进程杀死后丢失,无需持久化场景首选。
组件生命周期管理
组件化项目的加载、卸载、生命周期联动直接影响项目的稳定性,重点掌握组件生命周期与Application、Activity的关联,实现组件生命周期的统一管理,避免内存泄漏。
核心认知
- 组件生命周期分类
- ① 组件加载(Application初始化时加载)
- ② 组件卸载(APP退出或内存不足时)
- ③ 组件联动(组件间生命周期同步)
- 原则:组件生命周期需与Application、Activity生命周期联动,避免组件单独初始化导致内存泄漏、资源浪费
- 实战方案:在base组件中创建组件生命周期管理类,统一管理所有组件的加载和卸载
实战
- 1.创建组件生命周期接口(base组件中,供各组件实现)
interface IComponentLifecycle {
// 组件加载(初始化组件资源)
fun onLoad()
// 组件卸载(释放组件资源,避免内存泄漏)
fun onUnload()
}
- 2.user组件实现生命周期接口,管理自身资源
class UserComponentLifecycle @Inject constructor() : IComponentLifecycle {
// 组件加载:初始化user组件资源(如初始化工具类、注册监听)
override fun onLoad() {
Log.d("UserComponent", "user组件加载,初始化资源")
// 示例:初始化user组件的工具类
UserUtil.init()
}
// 组件卸载:释放user组件资源(如取消监听、清空缓存)
override fun onUnload() {
Log.d("UserComponent", "user组件卸载,释放资源")
// 示例:取消热流监听、清空临时缓存
FlowBusManager.unregisterAll()
MMKVUtil.getInstance().remove("user_temp_cache")
}
}
- 3.在base组件中,创建组件生命周期管理类
@Singleton
class ComponentLifecycleManager @Inject constructor() {
// 存储所有组件的生命周期回调
private val componentCallbacks = mutableListOf<IComponentLifecycle>()
// 注册组件生命周期回调(各组件初始化时注册)
fun registerComponentCallback(callback: IComponentLifecycle) {
if (!componentCallbacks.contains(callback)) {
componentCallbacks.add(callback)
}
}
// 卸载组件(APP退出时调用)
fun unregisterAllComponents() {
componentCallbacks.forEach { it.onUnload() }
componentCallbacks.clear()
}
// 组件加载(Application初始化时调用)
fun loadAllComponents() {
componentCallbacks.forEach { it.onLoad() }
}
}
- 4.在Application中,统一管理组件加载和卸载
@HiltAndroidApp
class AppApplication : Application() {
@Inject
lateinit var componentLifecycleManager: ComponentLifecycleManager
@Inject
lateinit var userComponentLifecycle: UserComponentLifecycle
override fun onCreate() {
super.onCreate()
AppContext.init(this)
MMKV.initialize(this)
// 1. 注册组件生命周期回调
componentLifecycleManager.registerComponentCallback(userComponentLifecycle)
// 2. 加载所有组件
componentLifecycleManager.loadAllComponents()
// 初始化ARouter(原有代码不变)
if (BuildConfig.DEBUG) {
ARouter.openLog()
ARouter.openDebug()
}
ARouter.init(this)
}
override fun onTerminate() {
super.onTerminate()
// APP退出时,卸载所有组件,释放资源
componentLifecycleManager.unregisterAllComponents()
// 销毁ARouter
ARouter.getInstance().destroy()
}
}
- 5.验证组件生命周期管理(核心)
- 同步工程,检查无报错,组件生命周期接口可正常实现
- 运行壳工程,查看日志,确认user组件正常加载(打印"user组件加载,初始化资源")
- 退出APP,查看日志,确认user组件正常卸载(打印"user组件卸载,释放资源")
- 验证要点:组件加载、卸载正常,资源释放彻底,无内存泄漏隐患
踩坑点
坑点1:DataStore跨组件读取失败/数据不一致
- 原因
- 未使用协程异步操作
- DataStore实例不唯一
- 键名不一致
- 修复
- 所有DataStore读写操作均在协程中执行
- 封装全局唯一DataStore工具类,确保所有组件调用同一实例
- 统一键名规范
坑点2:MMKV存储实体类失败
- 原因
- 实体类未实现Parcelable接口
- MMKV未初始化
- 混淆导致MMKV相关类失效
- 修复
- 给实体类添加@Parcelize注解实现Parcelable
- 在Application中初始化MMKV
- 添加MMKV专属混淆规则
坑点3:全局单例注入为null
- 原因
- 未给单例类添加@Singleton注解
- 未在Hilt Module中提供实例
- Activity未添加@AndroidEntryPoint注解
- 修复
- 添加@Singleton注解
- 在Module中提供单例实例
- 给Activity添加@AndroidEntryPoint注解,开启Hilt注入
坑点4:组件生命周期未联动,导致内存泄漏
- 原因
- 组件未注册生命周期回调
- 卸载时未释放资源(例:热流监听、缓存等)
- 修复
- 所有组件实现IComponentLifecycle接口
- 在Application中统一注册和卸载,卸载时释放所有资源
坑点5:release模式下数据共享失效
- 原因:存储相关类(DataStore、MMKV、实体类)被混淆
- 修复:在混淆规则中保留相关类,确保序列化接口、单例类、DataStore相关类不被混淆,打包后测试数据共享功能
坑点6:DataStore报ANR/异常
- 原因
- 在主线程中同步调用DataStore读写方法
- 未处理异常(如文件损坏)
- 修复
- 所有读写操作均在协程IO调度器中执行
- 添加异常捕获逻辑,避免主线程阻塞
组件化复盘
通过几节组件化的学习,形成“项目搭建→组件通信→路由框架→依赖注入→依赖管理→混淆配置→数据共享→生命周期管理”的完整体系为组件化进阶学习铺垫。
复盘1:组件化工程搭建
掌握壳工程+base基础组件+user业务组件的搭建流程,理解组件化与模块化的区别,掌握集成模式与组件模式的切换方法,核心是“高内聚、低耦合”的工程架构。
复盘2:组件间基础通信
掌握热流(Flow)通信、接口通信(ServiceManager)的实战用法,理解不同通信方式的适用场景,解决组件间数据传递和接口调用的核心痛点。
复盘3:ARouter+Hilt实战
掌握ARouter路由框架的页面跳转、参数传递、拦截器用法,理解Hilt依赖注入的核心原理和基础配置,用ARouter替代Intent、用Hilt替代ServiceManager,进一步降低耦合。
复盘4:依赖管理+组件混淆
掌握依赖版本统一、依赖隔离、冲突解决的方法,完成组件化工程的完整混淆配置,确保工程稳定性和安全性,解决职场中常见的依赖和混淆问题。
复盘5:数据共享+生命周期管理
掌握3种组件间数据共享方式(DataStore、MMKV、全局单例)的实战用法,精通组件生命周期的管理方法,完成项目综合测试。
全周体系梳理
组件化开发的核心流程是“项目搭建→实现通信→优化路由和依赖→管理依赖→配置混淆→实现数据共享→管理生命周期→综合测试”,每一步都围绕“低耦合、高内聚”的原则,确保项目可维护、可扩展、可上线。