侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1823 篇文章
  • 累计收到 0 条评论

在Android中实现线程安全且内存安全的Kotlin单例,支持延迟初始化与配置变化恢复

2025-12-14 / 0 评论 / 4 阅读

题目

在Android中实现线程安全且内存安全的Kotlin单例,支持延迟初始化与配置变化恢复

信息

  • 类型:问答
  • 难度:⭐⭐⭐

考点

单例模式实现,线程安全,内存泄漏预防,配置变化处理,Kotlin特性应用

快速回答

实现要点:

  • 使用by lazycompanion object实现延迟初始化
  • 结合@Volatile和双重检查锁定保证线程安全
  • 通过Application上下文避免内存泄漏
  • 处理配置变化时的实例重建问题
  • 利用LazyThreadSafetyMode优化性能
## 解析

核心需求与挑战

在Android中实现单例需解决:1) 线程安全初始化 2) 避免持有Activity上下文导致内存泄漏 3) 配置变化(如屏幕旋转)时的行为一致性。

基础实现方案

class Singleton private constructor(context: Context) {
    companion object {
        @Volatile private var instance: Singleton? = null

        fun getInstance(context: Context): Singleton {
            return instance ?: synchronized(this) {
                instance ?: Singleton(context.applicationContext).also { instance = it }
            }
        }
    }

    // 使用Application Context
    private val appContext = context.applicationContext

    // 示例资源
    private val resources = appContext.resources
}

关键优化点

  • 线程安全:双重检查锁定(DCL)配合@Volatile确保单例创建原子性
  • 内存安全:存储applicationContext而非Activity上下文
  • 延迟初始化:首次调用getInstance()时创建实例

使用Lazy的高级实现

class Singleton private constructor(context: Context) {
    companion object {
        private val lock = Any()

        val instance: Singleton by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
            // 需从外部传入Context,通常通过init函数
            error("Context must be initialized first")
        }

        // 初始化入口
        fun init(context: Context) {
            synchronized(lock) {
                if (instance != null) return
                instance = Singleton(context.applicationContext)
            }
        }
    }
}

配置变化处理方案

问题:屏幕旋转导致Activity重建时可能重复初始化

解决方案

  1. 在Application类中初始化单例
  2. 使用ViewModel保存实例(结合Dagger/Hilt依赖注入更佳)
  3. 添加重建标志位:
    private var initialized = false
    
    fun init(context: Context) {
        if (initialized) return
        synchronized(lock) {
            if (!initialized) {
                instance = Singleton(context.applicationContext)
                initialized = true
            }
        }
    }

最佳实践

  • 上下文管理:始终使用applicationContext
  • 依赖注入:通过Dagger/Hilt管理单例生命周期
  • 异常处理:添加init状态检查避免未初始化调用
  • 性能优化LazyThreadSafetyMode.PUBLICATION允许多线程初始化(当初始化成本低时)

常见错误

  • 内存泄漏:存储Activity上下文 → 解决方案:
    // 错误示例
    class Singleton(private val activity: Activity) 
    
    // 正确做法
    private val appContext: Context = context.applicationContext
  • 空指针异常:未初始化就调用 → 添加状态检查:
    fun getData(): String {
        check(initialized) { "Singleton not initialized!" }
        // ...
    }
  • 线程竞争:未使用同步机制 → 使用synchronized或原子引用

扩展知识

  • Kotlin对象声明object Singleton天然线程安全但无法延迟初始化
  • Dagger作用域@Singleton注解配合Component实现容器管理
  • ViewModel+LiveData:替代单例实现配置无关的数据持有
  • 性能对比by lazy vs 双重检查锁定(DCL在多次访问时性能更优)