Files
mkdocs/docs/android面试/数据存储/SharedPreferences.md
2026-01-15 11:53:37 +08:00

279 lines
5.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SharedPreferences
## 目录
- [SharedPreferences原理](#sharedpreferences原理)
- [SharedPreferences使用](#sharedpreferences使用)
- [SharedPreferences性能](#sharedpreferences性能)
- [SharedPreferences线程安全](#sharedpreferences线程安全)
- [SharedPreferences替代方案](#sharedpreferences替代方案)
- [SharedPreferences最佳实践](#sharedpreferences最佳实践)
- [面试常见问题](#面试常见问题)
---
## SharedPreferences原理
### 存储位置
```java
// SharedPreferences 存储在 XML 文件中
// 路径:/data/data/<package_name>/shared_prefs/<name>.xml
// 文件内容示例
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name="username">John</string>
<int name="age" value="25" />
<boolean name="isVip" value="true" />
</map>
```
### 存储机制
```java
// SharedPreferences 使用内存缓存 + 文件存储
// 1. 首次读取:从文件加载到内存
// 2. 后续读取:从内存读取(快速)
// 3. 写入:先写入内存,异步写入文件
```
---
## SharedPreferences使用
### 基本使用
```java
// 获取 SharedPreferences
SharedPreferences prefs = getSharedPreferences("my_prefs", Context.MODE_PRIVATE);
// 写入数据
SharedPreferences.Editor editor = prefs.edit();
editor.putString("username", "John");
editor.putInt("age", 25);
editor.putBoolean("isVip", true);
editor.apply(); // 异步提交
// 或
editor.commit(); // 同步提交
// 读取数据
String username = prefs.getString("username", "default");
int age = prefs.getInt("age", 0);
boolean isVip = prefs.getBoolean("isVip", false);
```
### apply vs commit
```java
// apply():异步提交
editor.apply();
// 优点:不阻塞主线程
// 缺点:无法知道是否成功
// commit():同步提交
boolean success = editor.commit();
// 优点:可以知道是否成功
// 缺点:可能阻塞主线程
```
### 监听数据变化
```java
// 注册监听器
SharedPreferences.OnSharedPreferenceChangeListener listener =
new SharedPreferences.OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if ("username".equals(key)) {
String username = sharedPreferences.getString(key, "");
// 处理变化
}
}
};
prefs.registerOnSharedPreferenceChangeListener(listener);
// 注销监听器
prefs.unregisterOnSharedPreferenceChangeListener(listener);
```
---
## SharedPreferences性能
### 性能问题
```java
// ❌ 问题:频繁读写
for (int i = 0; i < 1000; i++) {
SharedPreferences prefs = getSharedPreferences("prefs", Context.MODE_PRIVATE);
prefs.edit().putString("key" + i, "value" + i).apply();
}
// ✅ 解决:批量写入
SharedPreferences prefs = getSharedPreferences("prefs", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
for (int i = 0; i < 1000; i++) {
editor.putString("key" + i, "value" + i);
}
editor.apply(); // 一次性提交
```
### 性能优化
```java
// 1. 使用 apply() 而不是 commit()
editor.apply();
// 2. 批量写入
editor.putString("key1", "value1");
editor.putString("key2", "value2");
editor.apply();
// 3. 避免频繁读取
// 缓存到内存变量
```
---
## SharedPreferences线程安全
### 线程安全说明
```java
// SharedPreferences 是线程安全的
// 多个线程可以同时读取
// 写入操作会加锁
// 读取:线程安全
String value = prefs.getString("key", "default");
// 写入:线程安全
prefs.edit().putString("key", "value").apply();
```
### 并发写入
```java
// 多个线程同时写入
// SharedPreferences 内部会加锁,保证线程安全
// 但可能导致性能问题
// 建议:避免多线程频繁写入
```
---
## SharedPreferences替代方案
### 1. MMKV
```java
// MMKV高性能键值存储
MMKV mmkv = MMKV.defaultMMKV();
mmkv.encode("username", "John");
String username = mmkv.decodeString("username");
```
### 2. DataStore
```kotlin
// DataStoreJetpack 组件
val dataStore: DataStore<Preferences> = context.createDataStore(name = "settings")
// 写入
suspend fun saveUsername(username: String) {
dataStore.edit { preferences ->
preferences[stringPreferencesKey("username")] = username
}
}
// 读取
val usernameFlow: Flow<String> = dataStore.data
.map { preferences ->
preferences[stringPreferencesKey("username")] ?: ""
}
```
### 3. Room
```java
// Room适合复杂数据
@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
}
```
---
## SharedPreferences最佳实践
### 1. 使用 apply()
```java
// ✅ 推荐:使用 apply()
editor.apply();
// ❌ 不推荐:使用 commit()(除非需要知道结果)
editor.commit();
```
### 2. 批量写入
```java
// ✅ 批量写入
editor.putString("key1", "value1");
editor.putString("key2", "value2");
editor.apply();
// ❌ 多次写入
editor.putString("key1", "value1").apply();
editor.putString("key2", "value2").apply();
```
### 3. 避免存储大量数据
```java
// ❌ 不推荐:存储大量数据
editor.putString("largeData", largeString);
// ✅ 推荐:使用文件存储
saveToFile(largeData);
```
---
## 面试常见问题
### Q1: SharedPreferences 的原理?
**答案:**
- 存储在 XML 文件中
- 使用内存缓存 + 文件存储
- 首次读取从文件加载,后续从内存读取
### Q2: apply() 和 commit() 的区别?
**答案:**
- **apply()**:异步提交,不阻塞主线程
- **commit()**:同步提交,返回结果,可能阻塞主线程
### Q3: SharedPreferences 是否线程安全?
**答案:**
- 是线程安全的
- 多个线程可以同时读取
- 写入操作会加锁
### Q4: SharedPreferences 的替代方案?
**答案:**
1. **MMKV**:高性能键值存储
2. **DataStore**Jetpack 组件
3. **Room**:适合复杂数据
---
*最后更新2024年*