Android之SharedPreferences(SP)

目录

  • 概念
  • 使用
    • [1.获取 SharedPreferences 实例](#1.获取 SharedPreferences 实例)
      • [1.1 通过 Context 获取默认的 SharedPreferences 文件](#1.1 通过 Context 获取默认的 SharedPreferences 文件)
      • [1.2 通过 Activity 获取默认的 SharedPreferences 文件](#1.2 通过 Activity 获取默认的 SharedPreferences 文件)
      • [1.3 通过 PreferenceManager 获取默认的 SharedPreferences 文件](#1.3 通过 PreferenceManager 获取默认的 SharedPreferences 文件)
    • 2.存储数据
    • 3.读取数据
    • 4.删除数据
    • 5.监听数据变化
  • 使用场景
  • 注意事项
  • [SharedPreferences 为什么是线程安全的](#SharedPreferences 为什么是线程安全的)
    • SharedPreferences线程安全
      • [1. 数据存储和读取的线程安全性](#1. 数据存储和读取的线程安全性)
      • [2. 使用 apply() 方法进行异步提交](#2. 使用 apply() 方法进行异步提交)
      • [3. 使用 commit() 方法进行同步提交](#3. 使用 commit() 方法进行同步提交)
      • [4. 数据监听的线程安全性](#4. 数据监听的线程安全性)
    • 内部实现
    • [SharedPreferences 线程安全总结](#SharedPreferences 线程安全总结)
  • [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 文件

java 复制代码
SharedPreferences sharedPreferences = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE);

1.2 通过 Activity 获取默认的 SharedPreferences 文件

java 复制代码
SharedPreferences sharedPreferences = getPreferences(Context.MODE_PRIVATE);

1.3 通过 PreferenceManager 获取默认的 SharedPreferences 文件

java 复制代码
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);

2.存储数据

使用 SharedPreferences.Editor 对象来存储数据。Editor 提供了多种 put 方法来存储不同类型的数据。

java 复制代码
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.读取数据

java 复制代码
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 方法删除所有数据。

java 复制代码
SharedPreferences sharedPreferences = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();

// 删除特定键的数据
editor.remove("username");

// 删除所有数据
editor.clear();

// 提交更改
editor.apply(); // 或者使用 commit() 方法

5.监听数据变化

可以通过 SharedPreferences.OnSharedPreferenceChangeListener 接口监听数据的变化。

java 复制代码
SharedPreferences sharedPreferences = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE);

SharedPreferences.OnSharedPreferenceChangeListener listener = new SharedPreferences.OnSharedPreferenceChangeListener() {
    @Override
    public 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 内部使用锁机制确保线程安全性的简化示例:

java 复制代码
public final class SharedPreferencesImpl implements SharedPreferences {
    private final Object mLock = new Object();
    private final Map<String, Object> mMap;

    @Override
    public Map<String, ?> getAll() {
        synchronized (mLock) {
            return new HashMap<>(mMap);
        }
    }

    @Override
    public Editor edit() {
        return new EditorImpl();
    }

    public final class EditorImpl implements Editor {
        private final Map<String, Object> mModified;

        @Override
        public Editor putString(String key, String value) {
            synchronized (mLock) {
                mModified.put(key, value);
                return this;
            }
        }

        @Override
        public void apply() {
            final Runnable writeToDiskRunnable = new Runnable() {
                @Override
                public void run() {
                    synchronized (mLock) {
                        // 将 mModified 写入磁盘
                    }
                }
            };
            // 异步执行写操作
            new Thread(writeToDiskRunnable).start();
        }

        @Override
        public boolean commit() {
            synchronized (mLock) {
                // 将 mModified 写入磁盘
                return true;
            }
        }
    }
}

在这个简化的示例中,mLock 用于同步访问共享资源(如 mMap 和 mModified),以确保线程安全性。

SharedPreferences 线程安全总结

SharedPreferences 的线程安全性是通过内部锁机制、异步处理和内存缓存等方式实现的。这些机制确保了在多线程环境下进行数据存储和读取操作时的安全性和高效性。

SharedPreferences 工具类示例

java 复制代码
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 工具类进行数据存储和读取:

java 复制代码
public class MainActivity extends AppCompatActivity {

    @Override
    protected 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);
    }
}
相关推荐
长亭外的少年2 小时前
Kotlin 编译失败问题及解决方案:从守护进程到 Gradle 配置
android·开发语言·kotlin
建群新人小猿5 小时前
会员等级经验问题
android·开发语言·前端·javascript·php
1024小神6 小时前
tauri2.0版本开发苹果ios和安卓android应用,环境搭建和最后编译为apk
android·ios·tauri
兰琛6 小时前
20241121 android中树结构列表(使用recyclerView实现)
android·gitee
Y多了个想法7 小时前
RK3568 android11 适配敦泰触摸屏 FocalTech-ft5526
android·rk3568·触摸屏·tp·敦泰·focaltech·ft5526
NotesChapter8 小时前
Android吸顶效果,并有着ViewPager左右切换
android
_祝你今天愉快9 小时前
分析android :The binary version of its metadata is 1.8.0, expected version is 1.5.
android
暮志未晚Webgl9 小时前
109. UE5 GAS RPG 实现检查点的存档功能
android·java·ue5
麦田里的守望者江10 小时前
KMP 中的 expect 和 actual 声明
android·ios·kotlin
Dnelic-10 小时前
解决 Android 单元测试 No tests found for given includes:
android·junit·单元测试·问题记录·自学笔记