Android开发[7]:组件化之数据共享与生命周期管理

4 阅读10分钟

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.验证组件生命周期管理(核心)
  1. 同步工程,检查无报错,组件生命周期接口可正常实现
  2. 运行壳工程,查看日志,确认user组件正常加载(打印"user组件加载,初始化资源")
  3. 退出APP,查看日志,确认user组件正常卸载(打印"user组件卸载,释放资源")
  4. 验证要点:组件加载、卸载正常,资源释放彻底,无内存泄漏隐患

踩坑点

坑点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、全局单例)的实战用法,精通组件生命周期的管理方法,完成项目综合测试。

全周体系梳理

组件化开发的核心流程是“项目搭建→实现通信→优化路由和依赖→管理依赖→配置混淆→实现数据共享→管理生命周期→综合测试”,每一步都围绕“低耦合、高内聚”的原则,确保项目可维护、可扩展、可上线。