目录
- 概念
- 使用
- 1.获取 SharedPreferences 实例
- 1.1 通过 Context 获取默认的 SharedPreferences 文件
- 1.2 通过 Activity 获取默认的 SharedPreferences 文件
- 1.3 通过 PreferenceManager 获取默认的 SharedPreferences 文件
- 2.存储数据
- 3.读取数据
- 4.删除数据
- 5.监听数据变化
- 使用场景
- 注意事项
- SharedPreferences 为什么是线程安全的
- SharedPreferences线程安全
- 1. 数据存储和读取的线程安全性
- 2. 使用 apply() 方法进行异步提交
- 3. 使用 commit() 方法进行同步提交
- 4. 数据监听的线程安全性
- 内部实现
- SharedPreferences 线程安全总结
- SharedPreferences 工具类示例
SharedPreferences 是 Android 提供的一种轻量级的数据存储方式,主要用于存储简单的键值对(key-value pairs)。它非常适合保存应用的配置信息、用户设置、应用状态等小型数据。
在 Android 中,SharedPreferences 默认情况下是进程内的,即每个进程都有自己独立的 SharedPreferences 实例。
为了在多进程中可靠地共享数据,推荐使用 ContentProvider。ContentProvider 是 Android 提供的用于进程间通信的机制,可以确保数据的一致性和同步性。
MMKV——基于 mmap 的高性能通用 key-value 组件
概念
- 键值对存储:SharedPreferences 使用键值对(key-value)的方式存储数据,键是字符串,值可以是多种基本数据类型。
- 持久化存储:存储在 SharedPreferences 中的数据会被持久化到设备的文件系统中,即使应用被关闭或设备重启,数据仍然存在。
- 轻量级:适合存储少量的简单数据,不适合存储大量数据或复杂对象。
使用
1.获取 SharedPreferences 实例
可以通过以下几种方式获取 SharedPreferences 实例:
1.1 通过 Context 获取默认的 SharedPreferences 文件
SharedPreferences sharedPreferences = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE);
1.2 通过 Activity 获取默认的 SharedPreferences 文件
SharedPreferences sharedPreferences = getPreferences(Context.MODE_PRIVATE);
1.3 通过 PreferenceManager 获取默认的 SharedPreferences 文件
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
2.存储数据
使用 SharedPreferences.Editor 对象来存储数据。Editor 提供了多种 put 方法来存储不同类型的数据。
SharedPreferences sharedPreferences = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();editor.putString("username", "JohnDoe");
editor.putInt("age", 30);
editor.putBoolean("isLoggedIn", true);// 提交更改
editor.apply(); // 或者使用 commit() 方法
- apply():异步提交,不会阻塞主线程,推荐使用(没有返回值)。
- commit():同步提交,返回一个布尔值表示提交是否成功(有返回值)。
3.读取数据
SharedPreferences sharedPreferences = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE);String username = sharedPreferences.getString("username", null);
int age = sharedPreferences.getInt("age", 0);
boolean isLoggedIn = sharedPreferences.getBoolean("isLoggedIn", false);
4.删除数据
可以通过 Editor 的 remove 方法删除特定键的数据,或者使用 clear 方法删除所有数据。
SharedPreferences sharedPreferences = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();// 删除特定键的数据
editor.remove("username");// 删除所有数据
editor.clear();// 提交更改
editor.apply(); // 或者使用 commit() 方法
5.监听数据变化
可以通过 SharedPreferences.OnSharedPreferenceChangeListener 接口监听数据的变化。
SharedPreferences sharedPreferences = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE);SharedPreferences.OnSharedPreferenceChangeListener listener = new SharedPreferences.OnSharedPreferenceChangeListener() {@Overridepublic void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {// 当数据发生变化时的处理逻辑if (key.equals("username")) {String newUsername = sharedPreferences.getString(key, null);// 处理新的用户名}}
};// 注册监听器
sharedPreferences.registerOnSharedPreferenceChangeListener(listener);// 取消注册监听器
sharedPreferences.unregisterOnSharedPreferenceChangeListener(listener);
使用场景
- 用户设置:保存用户的偏好设置,如主题、通知设置等。
- 应用状态:保存应用的状态信息,如上次登录时间、用户会话信息等。
- 简单数据缓存:保存一些简单的临时数据,如搜索历史、用户输入等。
注意事项
- 适用范围:SharedPreferences 适合存储少量的简单数据,不适合存储大量数据或复杂对象。
- 线程安全:SharedPreferences 是线程安全的,可以在多个线程中安全地使用。
- 性能:使用 apply() 方法进行异步提交,避免阻塞主线程。
SharedPreferences 为什么是线程安全的
SharedPreferences线程安全
SharedPreferences 的线程安全性主要体现在以下几个方面:
1. 数据存储和读取的线程安全性
- 读取操作:SharedPreferences 的读取操作是线程安全的。读取数据时不会有并发问题,因为读取操作是无锁的,并且不会修改底层数据结构。
- 写入操作:SharedPreferences 的写入操作通过 SharedPreferences.Editor 类进行。Editor 类使用内部锁机制来确保写入操作的原子性。
2. 使用 apply() 方法进行异步提交
- apply() 方法:apply() 方法在后台线程中异步提交数据,不会阻塞主线程。它通过 SharedPreferencesImpl 类的 enqueueDiskWrite 方法将写操作加入队列,并在后台线程中执行。这种方式避免了多个线程同时写入数据时的冲突。
3. 使用 commit() 方法进行同步提交
- commit() 方法:commit() 方法是同步提交数据,返回一个布尔值表示提交是否成功。它使用内部锁机制确保在提交过程中不会有其他线程进行写操作。虽然 commit() 是线程安全的,但它会阻塞调用它的线程,因此不推荐在主线程中使用。
4. 数据监听的线程安全性
- 数据监听:SharedPreferences 提供了 registerOnSharedPreferenceChangeListener 方法来注册数据变化监听器。当数据发生变化时,所有注册的监听器会被通知。SharedPreferences 内部通过同步机制确保在通知监听器时的线程安全性。
内部实现
SharedPreferences 的线程安全性主要通过以下几个内部实现来保证:
- 锁机制:在写入操作中使用内部锁机制(如 synchronized 关键字)来确保写操作的原子性和线程安全性。
- 异步处理:使用 apply() 方法进行异步提交,将写操作放入队列并在后台线程中执行,避免阻塞主线程。
- 内存缓存:SharedPreferences 使用内存缓存来存储数据,减少频繁的 I/O 操作。读取操作直接从内存缓存中获取数据,保证了高效性和线程安全性。
以下是 SharedPreferences 内部使用锁机制确保线程安全性的简化示例:
public final class SharedPreferencesImpl implements SharedPreferences {private final Object mLock = new Object();private final Map<String, Object> mMap;@Overridepublic Map<String, ?> getAll() {synchronized (mLock) {return new HashMap<>(mMap);}}@Overridepublic Editor edit() {return new EditorImpl();}public final class EditorImpl implements Editor {private final Map<String, Object> mModified;@Overridepublic Editor putString(String key, String value) {synchronized (mLock) {mModified.put(key, value);return this;}}@Overridepublic void apply() {final Runnable writeToDiskRunnable = new Runnable() {@Overridepublic void run() {synchronized (mLock) {// 将 mModified 写入磁盘}}};// 异步执行写操作new Thread(writeToDiskRunnable).start();}@Overridepublic boolean commit() {synchronized (mLock) {// 将 mModified 写入磁盘return true;}}}
}
在这个简化的示例中,mLock 用于同步访问共享资源(如 mMap 和 mModified),以确保线程安全性。
SharedPreferences 线程安全总结
SharedPreferences 的线程安全性是通过内部锁机制、异步处理和内存缓存等方式实现的。这些机制确保了在多线程环境下进行数据存储和读取操作时的安全性和高效性。
SharedPreferences 工具类示例
import android.content.Context;
import android.content.SharedPreferences;public class SharedPrefsHelper {private static final String PREF_NAME = "MyPrefs";private static SharedPrefsHelper instance;private SharedPreferences sharedPreferences;private SharedPreferences.Editor editor;private SharedPrefsHelper(Context context) {sharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);editor = sharedPreferences.edit();}public static synchronized SharedPrefsHelper getInstance(Context context) {if (instance == null) {instance = new SharedPrefsHelper(context.getApplicationContext());}return instance;}// 存储字符串public void putString(String key, String value) {editor.putString(key, value);editor.apply();}// 读取字符串public String getString(String key, String defaultValue) {return sharedPreferences.getString(key, defaultValue);}// 存储整数public void putInt(String key, int value) {editor.putInt(key, value);editor.apply();}// 读取整数public int getInt(String key, int defaultValue) {return sharedPreferences.getInt(key, defaultValue);}// 存储布尔值public void putBoolean(String key, boolean value) {editor.putBoolean(key, value);editor.apply();}// 读取布尔值public boolean getBoolean(String key, boolean defaultValue) {return sharedPreferences.getBoolean(key, defaultValue);}// 存储浮点数public void putFloat(String key, float value) {editor.putFloat(key, value);editor.apply();}// 读取浮点数public float getFloat(String key, float defaultValue) {return sharedPreferences.getFloat(key, defaultValue);}// 存储长整型public void putLong(String key, long value) {editor.putLong(key, value);editor.apply();}// 读取长整型public long getLong(String key, long defaultValue) {return sharedPreferences.getLong(key, defaultValue);}// 删除特定键的数据public void remove(String key) {editor.remove(key);editor.apply();}// 清除所有数据public void clear() {editor.clear();editor.apply();}
}
在应用中使用 SharedPrefsHelper 工具类进行数据存储和读取:
public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 获取 SharedPrefsHelper 实例SharedPrefsHelper sharedPrefsHelper = SharedPrefsHelper.getInstance(this);// 存储数据sharedPrefsHelper.putString("username", "Alan");sharedPrefsHelper.putInt("age", 30);sharedPrefsHelper.putBoolean("isLoggedIn", true);// 读取数据String username = sharedPrefsHelper.getString("username", "defaultName");int age = sharedPrefsHelper.getInt("age", 0);boolean isLoggedIn = sharedPrefsHelper.getBoolean("isLoggedIn", false);// 显示读取的数据Log.d("MainActivity", "Username: " + username);Log.d("MainActivity", "Age: " + age);Log.d("MainActivity", "Is Logged In: " + isLoggedIn);}
}