# 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
// DataStore：Jetpack 组件
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年*
