一句话总结:
单例模式就像「公司财务部」—— 整个系统只此一家,全局共享,Android 里的 InputMethodManager、LayoutInflater 都靠它确保关键服务唯一性!
一、单例模式在 Android 的经典应用
1. 系统服务单例(如 InputMethodManager)
源码实现:
// frameworks/base/core/java/android/view/inputmethod/InputMethodManager.java
public final class InputMethodManager {
// 静态单例实例
static InputMethodManager sInstance;
// 双重检查锁定(线程安全)
public static InputMethodManager getInstance() {
synchronized (InputMethodManager.class) {
if (sInstance == null) {
IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);
sInstance = new InputMethodManager(service, Looper.getMainLooper());
}
}
return sInstance;
}
// 私有构造器
private InputMethodManager(IInputMethodManager service, Looper looper) {
mService = service;
mMainLooper = looper;
}
}
设计亮点:
- 双重检查锁:保证线程安全且高效
- 延迟初始化:首次调用
getInstance()时才创建 - 跨进程调用:通过 Binder 获取系统服务
2. 布局填充器 LayoutInflater
源码实现:
// frameworks/base/core/java/android/view/LayoutInflater.java
public abstract class LayoutInflater {
// 单例缓存(按 Context 隔离)
private static final HashMap<Context, LayoutInflater> sInflaterMap = new HashMap<>();
public static LayoutInflater from(Context context) {
synchronized (sInflaterMap) {
LayoutInflater inflater = sInflaterMap.get(context);
if (inflater == null) {
inflater = new PhoneLayoutInflater(context);
sInflaterMap.put(context, inflater);
}
return inflater;
}
}
}
特殊处理:
- 按 Context 隔离单例:防止不同 Activity 的布局冲突
- 使用 HashMap 缓存:平衡性能与内存占用
二、单例模式的五种实现方式对比
| 实现方式 | 优点 | 缺点 | Android 案例 |
|---|---|---|---|
| 饿汉式 | 简单、线程安全 | 可能提前占用内存 | TextUtils 工具类 |
| 懒汉式(同步锁) | 延迟加载 | 每次获取都同步,性能差 | 较少使用 |
| 双重检查锁定 | 线程安全且高效 | JDK < 5 可能失效 | InputMethodManager |
| 静态内部类 | 懒加载 + 无同步开销 | 无法传参初始化 | 常见于应用层单例 |
| 枚举单例 | 防反射攻击 | 不够灵活 | Android 中较少使用 |
三、源码级设计原理剖析
1. 系统服务的单例管理(ServiceManager)
// frameworks/base/core/java/android/os/ServiceManager.java
public final class ServiceManager {
private static final Map<String, IBinder> sCache = new ArrayMap<>();
// 获取系统服务单例
public static IBinder getService(String name) {
synchronized (sCache) {
IBinder service = sCache.get(name);
if (service != null) return service;
// 通过 Binder 驱动获取服务
return getIServiceManager().getService(name);
}
}
}
核心机制:
- 全局缓存:所有系统服务单例存储在
sCache - 跨进程通信:通过
Binder与system_server进程交互
2. 避免内存泄漏的注意事项
错误示例:
// 错误:单例持有 Activity 引用导致泄漏
public class AppManager {
private static AppManager instance;
private Context context;
private AppManager(Context context) {
this.context = context; // 传入 Activity Context
}
public static AppManager getInstance(Context context) {
if (instance == null) {
instance = new AppManager(context);
}
return instance;
}
}
正确实现:
// 使用 Application Context
private AppManager(Context context) {
this.context = context.getApplicationContext();
}
四、单例模式的最佳实践
1. 使用 Dagger/Hilt 管理单例
// 使用 Hilt 定义单例
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
@Provides
@Singleton // 单例注解
fun provideOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder().build()
}
}
优势:
- 生命周期管理:与组件生命周期绑定
- 依赖解耦:便于测试和替换实现
2. 结合 ContentProvider 初始化
// 利用 ContentProvider 的 onCreate 初始化单例
public class InitProvider extends ContentProvider {
@Override
public boolean onCreate() {
// 在应用启动时初始化
AppConfig.init(getContext());
return true;
}
}
原理:
- ContentProvider 的
onCreate()在Application.onCreate()之前执行
五、总结口诀
「单例模式管全局,系统服务最典型
双重检查锁线程安,静态内部类也推荐
内存泄漏要警惕,Application Context 记心间
使用场景建议:
- 全局配置管理(如
SharedPreferences封装) - 网络请求客户端(如
Retrofit实例) - 数据库访问对象(
Room Database)
注意事项:
- 避免在单例中保存 UI 相关引用
- 多进程应用需使用
@Singleton+@BindsInstance - 单元测试时注意 Mock 单例对象
源码对照表:
| 类名 | 单例实现方式 | 关键方法 |
|---|---|---|
InputMethodManager | 双重检查锁定 | getInstance() |
TextUtils | 饿汉式 | 全部静态方法 |
WindowManager | 系统服务单例 | WindowManagerGlobal |