# SharedPreferences ## 目录 - [SharedPreferences原理](#sharedpreferences原理) - [SharedPreferences使用](#sharedpreferences使用) - [SharedPreferences性能](#sharedpreferences性能) - [SharedPreferences线程安全](#sharedpreferences线程安全) - [SharedPreferences替代方案](#sharedpreferences替代方案) - [SharedPreferences最佳实践](#sharedpreferences最佳实践) - [面试常见问题](#面试常见问题) --- ## SharedPreferences原理 ### 存储位置 ```java // SharedPreferences 存储在 XML 文件中 // 路径:/data/data//shared_prefs/.xml // 文件内容示例 John ``` ### 存储机制 ```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 = context.createDataStore(name = "settings") // 写入 suspend fun saveUsername(username: String) { dataStore.edit { preferences -> preferences[stringPreferencesKey("username")] = username } } // 读取 val usernameFlow: Flow = 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年*