0%

安卓设计模式之单例模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
一、单例模式的核心思想
定义:
确保一个类 只有一个实例,并提供一个全局访问点。

生活类比:

公司只有一个CEO(所有人找CEO办事都找同一个人)

电脑只有一个任务管理器(无论打开多少次都是同一个窗口)

二、单例模式的5种Java实现
我们从基础到高级逐步优化:

1️⃣ 饿汉式(线程安全)
java
public class CEO {
// 类加载时就初始化实例
private static final CEO instance = new CEO();

// 私有构造方法
private CEO() {}

// 全局访问点
public static CEO getInstance() {
return instance;
}

public void manageCompany() {
System.out.println("CEO在管理公司");
}
}

// 使用
CEO ceo1 = CEO.getInstance();
CEO ceo2 = CEO.getInstance();
System.out.println(ceo1 == ceo2); // 输出true
特点:

线程安全(因为类加载时初始化)

可能造成资源浪费(如果从未使用过这个实例)

2️⃣ 懒汉式(非线程安全)
java
public class TaskManager {
private static TaskManager instance;

private TaskManager() {}

// 需要时再创建实例(懒加载)
public static TaskManager getInstance() {
if (instance == null) {
instance = new TaskManager();
}
return instance;
}

public void showProcesses() {
System.out.println("显示运行中的进程");
}
}
问题:多线程环境下可能创建多个实例(❌ 不安全)

3️⃣ 懒汉式(线程安全版)
java
public synchronized static TaskManager getInstance() {
if (instance == null) {
instance = new TaskManager();
}
return instance;
}
缺点:每次获取实例都要同步,性能差

4️⃣ 双重检查锁(DCL)
java
public class Database {
private static volatile Database instance;

private Database() {}

public static Database getInstance() {
if (instance == null) { // 第一次检查
synchronized (Database.class) { // 加锁
if (instance == null) { // 第二次检查
instance = new Database();
}
}
}
return instance;
}
}
关键点:

volatile 防止指令重排序

两次判空确保线程安全

只在第一次创建时同步

这是Android中最常用的单例实现方式

5️⃣ 静态内部类(推荐)
java
public class SettingsManager {
private SettingsManager() {}

private static class Holder {
static final SettingsManager INSTANCE = new SettingsManager();
}

public static SettingsManager getInstance() {
return Holder.INSTANCE;
}
}
优势:

线程安全(JVM保证类加载的线程安全)

懒加载(只有调用getInstance()时才会加载Holder类)

代码简洁

三、Android中的实际应用
案例1:全局工具类
java
// 网络工具单例
public class NetworkUtils {
private static volatile NetworkUtils instance;

public static NetworkUtils getInstance() {
if (instance == null) {
synchronized (NetworkUtils.class) {
if (instance == null) {
instance = new NetworkUtils();
}
}
}
return instance;
}

private NetworkUtils() {
// 初始化网络相关配置
}

public boolean isNetworkAvailable() {
// 实现网络检查
}
}
案例2:SharedPreferences管理
java
public class PrefsManager {
private static PrefsManager instance;
private SharedPreferences prefs;

private PrefsManager(Context context) {
prefs = context.getSharedPreferences("my_prefs", Context.MODE_PRIVATE);
}

public static synchronized PrefsManager getInstance(Context context) {
if (instance == null) {
instance = new PrefsManager(context.getApplicationContext());
}
return instance;
}

public void saveString(String key, String value) {
prefs.edit().putString(key, value).apply();
}
}
四、单例模式的注意事项
内存泄漏风险:

在Android中避免持有Activity/Fragment的引用

推荐使用Application Context

单元测试困难:

单例难以被Mock或替换

解决方案:考虑依赖注入(如Dagger)

多进程问题:

每个进程会有自己的单例实例

解决方案:使用文件锁或ContentProvider

五、常见问题
Q1:为什么要用双重检查锁?
👉 既保证线程安全,又避免每次获取实例都同步,提升性能。

Q2:volatile关键字的作用?
👉 1. 保证可见性 2. 防止指令重排序(避免返回未初始化完成的对象)

Q3:单例模式违反单一职责原则吗?
👉 确实可能同时承担"创建对象"和"业务逻辑"两个职责,这是它的缺点之一。

Q4:如何破坏单例?如何防御?
👉 破坏方式:

反射:通过反射调用私有构造方法
防御:在构造方法中加判断

java
private Singleton() {
if (instance != null) {
throw new RuntimeException("请使用getInstance()方法");
}
}
反序列化:序列化后再反序列化会创建新对象
防御:实现readResolve()方法

java
protected Object readResolve() {
return getInstance();
}
六、代码模板(直接可用)
java
public class AppManager {
private static volatile AppManager instance;
private Context appContext;

private AppManager(Context context) {
this.appContext = context.getApplicationContext();
}

public static AppManager getInstance(Context context) {
if (instance == null) {
synchronized (AppManager.class) {
if (instance == null) {
instance = new AppManager(context);
}
}
}
return instance;
}

// 示例方法
public void doSomething() {
Toast.makeText(appContext, "单例方法被调用", Toast.LENGTH_SHORT).show();
}
}
掌握单例模式后,可以在这些场景中使用它:

全局配置管理

日志工具类

数据库访问

文件系统操作

网络请求客户端