Android 单例模式浅析

21 阅读5分钟

一、基本概念

定义

确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。

核心思想

  • 私有构造器:防止外部通过new创建实例
  • 静态实例引用:类自身持有唯一实例
  • 全局访问点:提供静态方法获取唯一实例

二、实现方式详解

1. 饿汉式 (Eager Initialization)

// Kotlin实现
object Singleton {
    fun doSomething() {
        println("饿汉式单例")
    }
}

// 使用
Singleton.doSomething()

// Java实现
public class EagerSingleton {
    // 类加载时就初始化
    private static final EagerSingleton INSTANCE = new EagerSingleton();
    
    private EagerSingleton() {
        // 防止反射攻击
        if (INSTANCE != null) {
            throw new RuntimeException("单例模式禁止反射创建");
        }
    }
    
    public static EagerSingleton getInstance() {
        return INSTANCE;
    }
}

特点

  • 线程安全(类加载时初始化)
  • 不支持延迟加载
  • 可能浪费内存(即使不用也会创建)

2. 懒汉式 (Lazy Initialization)

// 线程不安全版本
class UnsafeLazySingleton private constructor() {
    companion object {
        private var instance: UnsafeLazySingleton? = null
        
        fun getInstance(): UnsafeLazySingleton {
            if (instance == null) {
                instance = UnsafeLazySingleton() // 多线程下可能创建多个实例
            }
            return instance!!
        }
    }
}

// 线程安全版本 - 同步方法
class SynchronizedLazySingleton private constructor() {
    companion object {
        private var instance: SynchronizedLazySingleton? = null
        
        @Synchronized
        fun getInstance(): SynchronizedLazySingleton {
            if (instance == null) {
                instance = SynchronizedLazySingleton()
            }
            return instance!!
        }
    }
}

3. 双重检查锁定 (Double-Checked Locking)

// Kotlin实现
class DoubleCheckedSingleton private constructor() {
    companion object {
        @Volatile // 确保可见性,防止指令重排序
        private var instance: DoubleCheckedSingleton? = null
        
        fun getInstance(): DoubleCheckedSingleton {
            // 第一次检查:不加锁,提高性能
            return instance ?: synchronized(this) {
                // 第二次检查:加锁,确保线程安全
                instance ?: DoubleCheckedSingleton().also {
                    instance = it
                }
            }
        }
    }
}

// Java实现
public class DoubleCheckedSingleton {
    // volatile关键字防止指令重排序
    private static volatile DoubleCheckedSingleton instance;
    
    private DoubleCheckedSingleton() {}
    
    public static DoubleCheckedSingleton getInstance() {
        if (instance == null) {  // 第一次检查
            synchronized (DoubleCheckedSingleton.class) {
                if (instance == null) {  // 第二次检查
                    instance = new DoubleCheckedSingleton();
                }
            }
        }
        return instance;
    }
}

原理分析

  • 第一次检查:避免不必要的同步
  • 同步块:确保只有一个线程进入
  • 第二次检查:防止多个线程通过第一次检查
  • volatile:防止指令重排序,确保可见性

4. 静态内部类 (Static Inner Class)

class InnerClassSingleton private constructor() {
    companion object {
        fun getInstance() = Holder.INSTANCE
    }
    
    private object Holder {
        val INSTANCE = InnerClassSingleton()
    }
}

// Java实现
public class StaticInnerClassSingleton {
    private StaticInnerClassSingleton() {}
    
    private static class SingletonHolder {
        private static final StaticInnerClassSingleton INSTANCE = 
            new StaticInnerClassSingleton();
    }
    
    public static StaticInnerClassSingleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

优点

  • 懒加载(只有调用getInstance时才加载内部类)
  • 线程安全(类加载机制保证)
  • 实现简单,性能好

5. 枚举实现 (Enum Singleton) - 《Effective Java》推荐

// Kotlin
enum class EnumSingleton {
    INSTANCE;
    
    fun doSomething() {
        println("枚举单例")
    }
}

// 使用
EnumSingleton.INSTANCE.doSomething()

// Java
public enum EnumSingleton {
    INSTANCE;
    
    public void doSomething() {
        System.out.println("枚举单例");
    }
}

优势

  • 绝对防止反射攻击
  • 防止反序列化创建新实例
  • 线程安全
  • 实现简单

三、单例模式的高级应用

1. 参数化单例

class ConfigManager private constructor(
    private val configName: String
) {
    private val properties = mutableMapOf<String, Any>()
    
    companion object {
        private val instances = mutableMapOf<String, ConfigManager>()
        
        fun getInstance(configName: String): ConfigManager {
            return instances[configName] ?: synchronized(this) {
                instances[configName] ?: ConfigManager(configName).also {
                    instances[configName] = it
                }
            }
        }
    }
    
    fun setProperty(key: String, value: Any) {
        properties[key] = value
    }
    
    fun getProperty(key: String): Any? {
        return properties[key]
    }
}

// 使用不同的配置实例
val userConfig = ConfigManager.getInstance("user")
val systemConfig = ConfigManager.getInstance("system")

2. 单例工厂

abstract class SingletonFactory<T> {
    private val instances = mutableMapOf<String, T>()
    
    protected abstract fun create(key: String): T
    
    fun getInstance(key: String): T {
        return instances[key] ?: synchronized(this) {
            instances[key] ?: create(key).also {
                instances[key] = it
            }
        }
    }
}

// 具体实现
class DatabaseFactory : SingletonFactory<Database>() {
    override fun create(key: String): Database {
        return when (key) {
            "user" -> UserDatabase()
            "product" -> ProductDatabase()
            else -> DefaultDatabase()
        }
    }
}

四、单例模式的问题与解决方案

1. 内存泄漏问题

// 错误示例:单例持有Activity引用
class LeakySingleton private constructor(private val activity: Activity) {
    companion object {
        private var instance: LeakySingleton? = null
        
        fun init(activity: Activity) {
            if (instance == null) {
                instance = LeakySingleton(activity) // 持有Activity引用!
            }
        }
        
        fun getInstance(): LeakySingleton? = instance
    }
}

// 正确做法:使用Application Context
class SafeSingleton private constructor(context: Context) {
    private val appContext = context.applicationContext
    
    companion object {
        @Volatile
        private var instance: SafeSingleton? = null
        
        fun getInstance(context: Context): SafeSingleton {
            return instance ?: synchronized(this) {
                instance ?: SafeSingleton(context.applicationContext).also {
                    instance = it
                }
            }
        }
    }
}

2. 测试困难 - 使用依赖注入解决

// 传统单例 - 难以测试
class AnalyticsManager private constructor() {
    companion object {
        private var instance: AnalyticsManager? = null
        
        fun getInstance(): AnalyticsManager {
            return instance ?: synchronized(this) {
                instance ?: AnalyticsManager().also {
                    instance = it
                }
            }
        }
    }
    
    fun trackEvent(event: String) {
        // 发送到服务器 - 测试时难以模拟
    }
}

// 使用接口和依赖注入
interface Analytics {
    fun trackEvent(event: String)
}

class AnalyticsImpl : Analytics {
    override fun trackEvent(event: String) {
        // 真实实现
    }
}

class MockAnalytics : Analytics {
    override fun trackEvent(event: String) {
        // 测试实现
    }
}

// 使用Dagger/Hilt依赖注入
@Module
@InstallIn(SingletonComponent::class)
object AnalyticsModule {
    @Provides
    @Singleton // Dagger会处理单例
    fun provideAnalytics(): Analytics = AnalyticsImpl()
}

// ViewModel中使用
class MyViewModel @Inject constructor(
    private val analytics: Analytics  // 由Dagger注入单例
) : ViewModel() {
    fun onButtonClick() {
        analytics.trackEvent("button_click")
    }
}

五、Kotlin特有的单例特性

1. object声明

object NetworkManager {
    private const val TIMEOUT = 5000L
    
    fun makeRequest(url: String) {
        // 网络请求
    }
    
    // object可以有init块
    init {
        println("NetworkManager初始化")
    }
}

// 反编译为Java看本质
/*
public final class NetworkManager {
    public static final NetworkManager INSTANCE;
    
    static {
        NetworkManager var0 = new NetworkManager();
        INSTANCE = var0;
        System.out.println("NetworkManager初始化");
    }
    
    private NetworkManager() {}
}
*/

2. by lazy实现懒加载

class LazySingleton private constructor() {
    companion object {
        val instance: LazySingleton by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
            println("初始化LazySingleton")
            LazySingleton()
        }
    }
}

// 不同的线程安全模式
val unsafeInstance by lazy(LazyThreadSafetyMode.NONE) { /* 线程不安全 */ }
val synchronizedInstance by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { /* 线程安全 */ }
val publicationInstance by lazy(LazyThreadSafetyMode.PUBLICATION) { /* 允许重复初始化 */ }

六、最佳实践指南

何时使用单例模式?

✅ 适用场景

  1. 全局配置管理(如AppConfig)
  2. 日志记录器(如Logger)
  3. 数据库访问对象
  4. 网络请求客户端
  5. 缓存管理器
  6. 工具类(如DateFormatter)

❌ 避免使用场景

  1. 需要大量测试的类
  2. 可能有多态需求的类
  3. 需要参数化配置的类
  4. 状态频繁变化的类

Android开发建议

  • 优先使用依赖注入(Dagger/Hilt)管理单例
  • 避免持有Context引用,必须使用时用Application Context
  • 注意生命周期,及时清理资源
  • 使用Kotlin object简化单例实现

代码示例:完整的Android单例

// 使用Kotlin object + 依赖注入思想
object ImageLoader {
    private const val MEMORY_CACHE_SIZE = 10 * 1024 * 1024 // 10MB
    
    private val memoryCache: LruCache<String, Bitmap>
    private val executor: ExecutorService
    
    init {
        memoryCache = object : LruCache<String, Bitmap>(MEMORY_CACHE_SIZE) {
            override fun sizeOf(key: String, value: Bitmap): Int {
                return value.byteCount
            }
        }
        
        executor = Executors.newFixedThreadPool(4)
    }
    
    fun loadImage(url: String, imageView: ImageView) {
        // 检查内存缓存
        val cachedBitmap = memoryCache.get(url)
        if (cachedBitmap != null) {
            imageView.setImageBitmap(cachedBitmap)
            return
        }
        
        // 异步加载
        executor.submit {
            val bitmap = downloadImage(url)
            bitmap?.let {
                memoryCache.put(url, it)
                
                // 切回主线程更新UI
                Handler(Looper.getMainLooper()).post {
                    imageView.setImageBitmap(it)
                }
            }
        }
    }
    
    fun clearCache() {
        memoryCache.evictAll()
    }
}

// 使用
ImageLoader.loadImage("https://example.com/image.jpg", imageView)

七、总结

单例模式是Android开发中最常用的设计模式之一,但也是最容易被滥用的模式。正确使用单例模式需要注意:

  1. 线程安全:根据需求选择合适的线程安全方案
  2. 内存管理:避免内存泄漏,特别是Context引用
  3. 测试友好:考虑使用依赖注入提高可测试性
  4. Kotlin特性:充分利用object、by lazy等语言特性
  5. 架构考虑:在MVVM等架构中合理使用单例