下面是优化后的完整代码,兼容 Android 16+:
package com.nyw.wanglitiao.util;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.nyw.wanglitiao.config.Constant;
import java.util.Map;
import java.util.Set;
/**
* SharedPreferences 工具类
* 兼容 Android 16+ (API 16)
*/
public final class SPUtils {
// 默认 SharedPreferences 名称
private static final String DEFAULT_SP_NAME = Constant.SPKEY;
// 单例实例
private static volatile SPUtils sInstance;
// SharedPreferences 实例
private final SharedPreferences mSp;
/**
* 私有构造函数
* @param context 上下文
* @param spName SharedPreferences 名称
*/
private SPUtils(@NonNull Context context, @NonNull String spName) {
// 使用 Application Context 避免内存泄漏
Context appContext = context.getApplicationContext();
// 根据系统版本选择合适的模式
int mode = Context.MODE_PRIVATE;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
// Android 7.0+ 支持多进程模式
// 如果需要多进程支持,可改为 MODE_MULTI_PROCESS
// 但官方已不推荐使用多进程 SharedPreferences
mode = Context.MODE_PRIVATE;
}
mSp = appContext.getSharedPreferences(spName, mode);
}
/**
* 初始化 SPUtils
* @param context 上下文
*/
public static void init(@NonNull Context context) {
if (sInstance == null) {
synchronized (SPUtils.class) {
if (sInstance == null) {
sInstance = new SPUtils(context, DEFAULT_SP_NAME);
}
}
}
}
/**
* 获取单例实例
* @return SPUtils 实例
* @throws IllegalStateException 如果未初始化
*/
@NonNull
public static SPUtils getInstance() {
if (sInstance == null) {
throw new IllegalStateException("SPUtils not initialized. Call init() first.");
}
return sInstance;
}
/**
* 保存 String 类型数据
* @param key 键
* @param value 值
*/
public void putString(@NonNull String key, @Nullable String value) {
SharedPreferences.Editor editor = mSp.edit();
editor.putString(key, value);
apply(editor);
}
/**
* 获取 String 类型数据
* @param key 键
* @return 值,如果不存在则返回默认值
*/
@Nullable
public String getString(@NonNull String key) {
return getString(key, "");
}
/**
* 获取 String 类型数据
* @param key 键
* @param defaultValue 默认值
* @return 值,如果不存在则返回默认值
*/
@Nullable
public String getString(@NonNull String key, @Nullable String defaultValue) {
try {
return mSp.getString(key, defaultValue);
} catch (Exception e) {
return defaultValue;
}
}
/**
* 保存 int 类型数据
* @param key 键
* @param value 值
*/
public void putInt(@NonNull String key, int value) {
SharedPreferences.Editor editor = mSp.edit();
editor.putInt(key, value);
apply(editor);
}
/**
* 获取 int 类型数据
* @param key 键
* @return 值,如果不存在则返回默认值
*/
public int getInt(@NonNull String key) {
return getInt(key, 0);
}
/**
* 获取 int 类型数据
* @param key 键
* @param defaultValue 默认值
* @return 值,如果不存在则返回默认值
*/
public int getInt(@NonNull String key, int defaultValue) {
try {
return mSp.getInt(key, defaultValue);
} catch (Exception e) {
return defaultValue;
}
}
/**
* 保存 long 类型数据
* @param key 键
* @param value 值
*/
public void putLong(@NonNull String key, long value) {
SharedPreferences.Editor editor = mSp.edit();
editor.putLong(key, value);
apply(editor);
}
/**
* 获取 long 类型数据
* @param key 键
* @return 值,如果不存在则返回默认值
*/
public long getLong(@NonNull String key) {
return getLong(key, 0L);
}
/**
* 获取 long 类型数据
* @param key 键
* @param defaultValue 默认值
* @return 值,如果不存在则返回默认值
*/
public long getLong(@NonNull String key, long defaultValue) {
try {
return mSp.getLong(key, defaultValue);
} catch (Exception e) {
return defaultValue;
}
}
/**
* 保存 float 类型数据
* @param key 键
* @param value 值
*/
public void putFloat(@NonNull String key, float value) {
SharedPreferences.Editor editor = mSp.edit();
editor.putFloat(key, value);
apply(editor);
}
/**
* 获取 float 类型数据
* @param key 键
* @return 值,如果不存在则返回默认值
*/
public float getFloat(@NonNull String key) {
return getFloat(key, 0f);
}
/**
* 获取 float 类型数据
* @param key 键
* @param defaultValue 默认值
* @return 值,如果不存在则返回默认值
*/
public float getFloat(@NonNull String key, float defaultValue) {
try {
return mSp.getFloat(key, defaultValue);
} catch (Exception e) {
return defaultValue;
}
}
/**
* 保存 boolean 类型数据
* @param key 键
* @param value 值
*/
public void putBoolean(@NonNull String key, boolean value) {
SharedPreferences.Editor editor = mSp.edit();
editor.putBoolean(key, value);
apply(editor);
}
/**
* 获取 boolean 类型数据
* @param key 键
* @return 值,如果不存在则返回默认值
*/
public boolean getBoolean(@NonNull String key) {
return getBoolean(key, false);
}
/**
* 获取 boolean 类型数据
* @param key 键
* @param defaultValue 默认值
* @return 值,如果不存在则返回默认值
*/
public boolean getBoolean(@NonNull String key, boolean defaultValue) {
try {
return mSp.getBoolean(key, defaultValue);
} catch (Exception e) {
return defaultValue;
}
}
/**
* 保存 Set<String> 类型数据
* @param key 键
* @param values 值
*/
public void putStringSet(@NonNull String key, @Nullable Set<String> values) {
SharedPreferences.Editor editor = mSp.edit();
editor.putStringSet(key, values);
apply(editor);
}
/**
* 获取 Set<String> 类型数据
* @param key 键
* @return 值,如果不存在则返回默认值
*/
@Nullable
public Set<String> getStringSet(@NonNull String key) {
return getStringSet(key, null);
}
/**
* 获取 Set<String> 类型数据
* @param key 键
* @param defaultValue 默认值
* @return 值,如果不存在则返回默认值
*/
@Nullable
public Set<String> getStringSet(@NonNull String key, @Nullable Set<String> defaultValue) {
try {
return mSp.getStringSet(key, defaultValue);
} catch (Exception e) {
return defaultValue;
}
}
/**
* 删除指定 key 的数据
* @param key 键
*/
public void remove(@NonNull String key) {
SharedPreferences.Editor editor = mSp.edit();
editor.remove(key);
apply(editor);
}
/**
* 检查是否包含指定 key
* @param key 键
* @return 是否包含
*/
public boolean contains(@NonNull String key) {
try {
return mSp.contains(key);
} catch (Exception e) {
return false;
}
}
/**
* 清除所有数据
*/
public void clear() {
SharedPreferences.Editor editor = mSp.edit();
editor.clear();
apply(editor);
}
/**
* 获取所有数据
* @return 所有键值对
*/
@NonNull
public Map<String, ?> getAll() {
try {
return mSp.getAll();
} catch (Exception e) {
return java.util.Collections.emptyMap();
}
}
/**
* 根据系统版本选择合适的提交方式
* @param editor Editor 实例
*/
private void apply(@NonNull SharedPreferences.Editor editor) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
// Android 2.3+ 支持 apply()
editor.apply();
} else {
// Android 2.2 及以下使用 commit()
editor.commit();
}
} catch (Exception e) {
// 防止在某些特殊情况下崩溃
try {
editor.commit();
} catch (Exception ignored) {
}
}
}
}
优化亮点
- 单例模式:使用双重检查锁实现线程安全的单例
- 初始化机制 :需要先调用
init()
方法初始化 - 内存安全:使用 Application Context 避免内存泄漏
- API 兼容:根据系统版本选择 apply () 或 commit ()
- 功能完整:支持所有 SharedPreferences 数据类型
- 异常处理:添加 try-catch 防止崩溃
- 线程安全:apply () 是异步的,commit () 是同步的,都已处理
使用方法
初始化(建议在 Application 中)
java
运行
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
SPUtils.init(this);
}
}
保存数据
java
运行
SPUtils.getInstance().putString("username", "test");
SPUtils.getInstance().putInt("age", 25);
SPUtils.getInstance().putBoolean("isLogin", true);
获取数据
java
运行
String username = SPUtils.getInstance().getString("username");
int age = SPUtils.getInstance().getInt("age", 0);
boolean isLogin = SPUtils.getInstance().getBoolean("isLogin", false);
删除数据
java
运行
SPUtils.getInstance().remove("username");
清除所有数据
java
运行
SPUtils.getInstance().clear();
注意事项
- 必须先调用
SPUtils.init(context)
初始化,否则会抛出异常 - 所有方法都已添加异常处理,避免因 SharedPreferences 问题导致崩溃
- 支持 Android 16+ 所有版本
- 不建议在多进程中使用 SharedPreferences,可能导致数据同步问题
SharedPreferences 工具类(多进程 + 可选加密版)
package com.nyw.mvvmmode.utils;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.text.TextUtils;
import android.util.Base64;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
/**
* SharedPreferences 工具类(多进程 + 可选加密版)
* 默认:不加密
* 兼容 Android 16+ (API 16)
*/
public final class SecureSPUtils {
// 默认 SharedPreferences 名称
private static final String DEFAULT_SP_NAME = Constant.SPKEY;
private static final String DEFAULT_CRYPTO_KEY = "your-secret-key"; // 建议替换为自己的密钥
// 模式常量
public static final int ENCRYPT_MODE = 0; // 加密模式
public static final int PLAIN_MODE = 1; // 明文模式(默认)
// 单例
private static volatile SecureSPUtils sInstance;
private final SharedPreferences mSp;
private final SecretKeySpec mSecretKey;
private int mDefaultMode = PLAIN_MODE; // 默认不加密
private SecureSPUtils(@NonNull Context context, @NonNull String spName,
boolean enableMultiProcess, @NonNull String secretKey) {
Context appContext = context.getApplicationContext();
int mode = Context.MODE_PRIVATE;
if (enableMultiProcess && Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
mode = Context.MODE_MULTI_PROCESS;
}
mSp = appContext.getSharedPreferences(spName, mode);
try {
mSecretKey = generateKey(secretKey);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 初始化(默认不加密)
*/
public static void init(@NonNull Context context) {
init(context, DEFAULT_SP_NAME, false, DEFAULT_CRYPTO_KEY);
}
public static void init(@NonNull Context context, boolean enableMultiProcess) {
init(context, DEFAULT_SP_NAME, enableMultiProcess, DEFAULT_CRYPTO_KEY);
}
public static void init(@NonNull Context context, @NonNull String spName,
boolean enableMultiProcess, @NonNull String secretKey) {
if (sInstance == null) {
synchronized (SecureSPUtils.class) {
if (sInstance == null) {
sInstance = new SecureSPUtils(context, spName, enableMultiProcess, secretKey);
}
}
}
}
@NonNull
public static SecureSPUtils getInstance() {
if (sInstance == null) {
throw new IllegalStateException("SecureSPUtils not initialized. Call init() first.");
}
return sInstance;
}
/**
* 设置全局默认模式
*/
public void setDefaultMode(int mode) {
if (mode == ENCRYPT_MODE || mode == PLAIN_MODE) {
mDefaultMode = mode;
}
}
// ===== String =====
public void putString(@NonNull String key, @Nullable String value) {
putString(key, value, mDefaultMode);
}
public void putString(@NonNull String key, @Nullable String value, int mode) {
try {
SharedPreferences.Editor editor = mSp.edit();
if (value == null) {
editor.putString(key, null);
} else {
String saveValue = (mode == ENCRYPT_MODE) ? encrypt(value) : value;
editor.putString(key, saveValue);
}
apply(editor);
} catch (Exception e) {
e.printStackTrace();
}
}
@Nullable
public String getString(@NonNull String key) {
return getString(key, "", mDefaultMode);
}
@Nullable
public String getString(@NonNull String key, @Nullable String defaultValue, int mode) {
try {
String value = mSp.getString(key, null);
if (value == null) {
return defaultValue;
}
return (mode == ENCRYPT_MODE) ? decrypt(value) : value;
} catch (Exception e) {
e.printStackTrace();
return defaultValue;
}
}
// ===== int =====
public void putInt(@NonNull String key, int value) {
putInt(key, value, mDefaultMode);
}
public void putInt(@NonNull String key, int value, int mode) {
putString(key, String.valueOf(value), mode);
}
public int getInt(@NonNull String key) {
return getInt(key, 0, mDefaultMode);
}
public int getInt(@NonNull String key, int defaultValue, int mode) {
try {
String value = getString(key, null, mode);
if (TextUtils.isEmpty(value)) {
return defaultValue;
}
return Integer.parseInt(value);
} catch (Exception e) {
return defaultValue;
}
}
// ===== long =====
public void putLong(@NonNull String key, long value) {
putLong(key, value, mDefaultMode);
}
public void putLong(@NonNull String key, long value, int mode) {
putString(key, String.valueOf(value), mode);
}
public long getLong(@NonNull String key) {
return getLong(key, 0L, mDefaultMode);
}
public long getLong(@NonNull String key, long defaultValue, int mode) {
try {
String value = getString(key, null, mode);
if (TextUtils.isEmpty(value)) {
return defaultValue;
}
return Long.parseLong(value);
} catch (Exception e) {
return defaultValue;
}
}
// ===== float =====
public void putFloat(@NonNull String key, float value) {
putFloat(key, value, mDefaultMode);
}
public void putFloat(@NonNull String key, float value, int mode) {
putString(key, String.valueOf(value), mode);
}
public float getFloat(@NonNull String key) {
return getFloat(key, 0f, mDefaultMode);
}
public float getFloat(@NonNull String key, float defaultValue, int mode) {
try {
String value = getString(key, null, mode);
if (TextUtils.isEmpty(value)) {
return defaultValue;
}
return Float.parseFloat(value);
} catch (Exception e) {
return defaultValue;
}
}
// ===== boolean =====
public void putBoolean(@NonNull String key, boolean value) {
putBoolean(key, value, mDefaultMode);
}
public void putBoolean(@NonNull String key, boolean value, int mode) {
putString(key, String.valueOf(value), mode);
}
public boolean getBoolean(@NonNull String key) {
return getBoolean(key, false, mDefaultMode);
}
public boolean getBoolean(@NonNull String key, boolean defaultValue, int mode) {
try {
String value = getString(key, null, mode);
if (TextUtils.isEmpty(value)) {
return defaultValue;
}
return Boolean.parseBoolean(value);
} catch (Exception e) {
return defaultValue;
}
}
// ===== 其他方法 =====
public void remove(@NonNull String key) {
try {
SharedPreferences.Editor editor = mSp.edit();
editor.remove(key);
apply(editor);
} catch (Exception e) {
e.printStackTrace();
}
}
public boolean contains(@NonNull String key) {
try {
return mSp.contains(key);
} catch (Exception e) {
return false;
}
}
public void clear() {
try {
SharedPreferences.Editor editor = mSp.edit();
editor.clear();
apply(editor);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取所有数据(自动根据默认模式解密)
*/
@NonNull
public Map<String, ?> getAll() {
return getAll(mDefaultMode);
}
/**
* 获取所有数据(可选择解密模式)
*/
@NonNull
public Map<String, ?> getAll(int mode) {
try {
Map<String, ?> all = mSp.getAll();
Map<String, Object> result = new HashMap<>();
for (Map.Entry<String, ?> entry : all.entrySet()) {
if (entry.getValue() instanceof String) {
try {
String value = (String) entry.getValue();
result.put(entry.getKey(), (mode == ENCRYPT_MODE) ? decrypt(value) : value);
} catch (Exception e) {
result.put(entry.getKey(), entry.getValue());
}
} else {
result.put(entry.getKey(), entry.getValue());
}
}
return result;
} catch (Exception e) {
e.printStackTrace();
return new HashMap<>();
}
}
/**
* 根据系统版本选择 apply 或 commit
*/
private void apply(@NonNull SharedPreferences.Editor editor) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
editor.apply();
} else {
editor.commit();
}
} catch (Exception e) {
try {
editor.commit();
} catch (Exception ignored) {
}
}
}
/**
* 生成 AES 密钥
*/
private SecretKeySpec generateKey(String seed) throws Exception {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(seed.getBytes("UTF-8"));
keyGen.init(128, sr);
SecretKey secretKey = keyGen.generateKey();
return new SecretKeySpec(secretKey.getEncoded(), "AES");
}
/**
* AES 加密
*/
private String encrypt(String data) throws Exception {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, mSecretKey);
byte[] encryptedData = cipher.doFinal(data.getBytes("UTF-8"));
return Base64.encodeToString(encryptedData, Base64.NO_WRAP);
}
/**
* AES 解密
*/
private String decrypt(String encryptedData) throws Exception {
byte[] decodedData = Base64.decode(encryptedData, Base64.NO_WRAP);
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, mSecretKey);
byte[] decryptedData = cipher.doFinal(decodedData);
return new String(decryptedData, "UTF-8");
}
}
SecureSPUtils 使用案例详解
下面提供 完整的使用流程案例,包括初始化、基础数据存储(明文 / 加密)、数据读取、特殊场景使用等,覆盖日常开发中 90% 以上的使用场景。
一、前置准备:初始化(关键!)
必须在 Application 类 中初始化(全局只初始化一次),避免重复创建实例或内存泄漏。
1.1 创建 / 修改 Application 类
package com.nyw.mvvmmode;
import android.app.Application;
import com.nyw.mvvmmode.utils.SecureSPUtils;
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
// 1. 基础初始化(默认不加密,使用默认 SP 名称和密钥)
SecureSPUtils.init(this);
// 2. 进阶初始化(按需选择)
// 2.1 启用多进程模式(仅 Android 7.0 以下有效)
// SecureSPUtils.init(this, true);
// 2.2 自定义 SP 名称、多进程、密钥(推荐!替换默认密钥更安全)
// SecureSPUtils.init(
// this, // 上下文
// "MyApp_SP", // 自定义 SP 文件名(默认是 Constant.SPKEY)
// false, // 是否启用多进程
// "MySecretKey_123456" // 自定义加密密钥(替换默认的 "your-secret-key")
// );
}
}
1.2 配置 AndroidManifest.xml
在 AndroidManifest.xml
的 <application>
标签中添加 android:name
,关联上面的 MyApp 类:
<application
android:name=".MyApp" <!-- 关键:关联自定义 Application -->
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
...>
<!-- 其他配置 -->
</application>
二、基础使用案例(Activity/Fragment 中)
在任何需要操作 SharedPreferences 的地方(如 Activity、Fragment、ViewModel),通过 SecureSPUtils.getInstance()
获取实例后使用。
2.1 1. 存储 / 读取 明文数据(默认模式)
适合存储 非敏感数据(如用户昵称、主题设置、是否首次启动等):
// 1. 存储明文数据
private void savePlainData() {
SecureSPUtils sp = SecureSPUtils.getInstance();
// 存储 String(默认不加密)
sp.putString("user_nickname", "张三");
// 存储 int(默认不加密)
sp.putInt("user_age", 25);
// 存储 boolean(默认不加密)
sp.putBoolean("is_first_launch", false);
// 存储 long(默认不加密)
sp.putLong("last_login_time", System.currentTimeMillis());
// 存储 float(默认不加密)
sp.putFloat("user_score", 98.5f);
}
// 2. 读取明文数据
private void readPlainData() {
SecureSPUtils sp = SecureSPUtils.getInstance();
// 读取 String(默认不解密,无数据时返回空字符串)
String nickname = sp.getString("user_nickname");
// 读取 int(无数据时返回默认值 0)
int age = sp.getInt("user_age");
// 读取 boolean(无数据时返回默认值 true)
boolean isFirstLaunch = sp.getBoolean("is_first_launch", true);
// 读取 long(无数据时返回默认值 0L)
long lastLoginTime = sp.getLong("last_login_time", 0L);
// 读取 float(无数据时返回默认值 0.0f)
float score = sp.getFloat("user_score", 0.0f);
// 打印结果(实际开发中根据需求使用)
Log.d("SP_TEST", "昵称:" + nickname); // 输出:张三
Log.d("SP_TEST", "年龄:" + age); // 输出:25
Log.d("SP_TEST", "首次启动:" + isFirstLaunch); // 输出:false
Log.d("SP_TEST", "上次登录时间:" + new Date(lastLoginTime));
Log.d("SP_TEST", "分数:" + score); // 输出:98.5
}
2.2 存储 / 读取 加密数据(手动指定加密模式)
适合存储 敏感数据 (如用户 Token、手机号、身份证号等),需手动传入 SecureSPUtils.ENCRYPT_MODE
:
// 1. 存储加密数据(敏感信息)
private void saveEncryptedData() {
SecureSPUtils sp = SecureSPUtils.getInstance();
// 存储 Token(加密模式)
sp.putString("user_token", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", SecureSPUtils.ENCRYPT_MODE);
// 存储手机号(加密模式)
sp.putString("user_phone", "13800138000", SecureSPUtils.ENCRYPT_MODE);
// 存储用户 ID(加密模式,int 类型)
sp.putInt("user_id", 10086, SecureSPUtils.ENCRYPT_MODE);
}
// 2. 读取加密数据(必须和存储时的模式一致,否则解密失败)
private void readEncryptedData() {
SecureSPUtils sp = SecureSPUtils.getInstance();
// 读取 Token(解密模式,无数据时返回默认值 "null")
String token = sp.getString("user_token", "null", SecureSPUtils.ENCRYPT_MODE);
// 读取手机号(解密模式,无数据时返回默认值 "未知")
String phone = sp.getString("user_phone", "未知", SecureSPUtils.ENCRYPT_MODE);
// 读取用户 ID(解密模式,无数据时返回默认值 -1)
int userId = sp.getInt("user_id", -1, SecureSPUtils.ENCRYPT_MODE);
// 打印结果(加密数据读取后会自动解密为原始值)
Log.d("SP_TEST", "Token:" + token); // 输出:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Log.d("SP_TEST", "手机号:" + phone); // 输出:13800138000
Log.d("SP_TEST", "用户 ID:" + userId); // 输出:10086
}
2.3 3. 其他常用操作(删除、清空、判断是否包含)
java
运行
private void otherOperations() {
SecureSPUtils sp = SecureSPUtils.getInstance();
// 1. 判断是否包含某个 key
boolean hasNickname = sp.contains("user_nickname");
Log.d("SP_TEST", "是否有昵称:" + hasNickname); // 输出:true
// 2. 删除指定 key 的数据(无论明文/加密,直接删除)
sp.remove("user_age"); // 删除之前存储的 "user_age"
// 3. 清空所有数据(谨慎使用!会删除 SP 中的所有键值对)
// sp.clear();
}
三、进阶使用案例
3.1 全局修改默认模式(批量加密)
如果需要 全局默认加密 (如整个模块的所有数据都需要加密),可以修改默认模式,无需每次传 ENCRYPT_MODE
:
java
运行
private void setGlobalEncryptMode() {
SecureSPUtils sp = SecureSPUtils.getInstance();
// 1. 设置全局默认模式为加密(后续所有无模式参数的方法都会加密)
sp.setDefaultMode(SecureSPUtils.ENCRYPT_MODE);
// 2. 存储数据(默认加密,无需传模式参数)
sp.putString("order_id", "20240520123456"); // 自动加密
sp.putInt("pay_amount", 99); // 自动加密
// 3. 读取数据(默认解密,无需传模式参数)
String orderId = sp.getString("order_id"); // 自动解密
int payAmount = sp.getInt("pay_amount"); // 自动解密
// 4. 恢复默认模式为明文(按需恢复)
sp.setDefaultMode(SecureSPUtils.PLAIN_MODE);
}
3.2 批量获取所有数据(getAll)
适合需要一次性获取 SP 中所有键值对的场景(如数据备份、调试):
java
运行
private void getAllData() {
SecureSPUtils sp = SecureSPUtils.getInstance();
// 1. 获取所有明文数据(默认模式)
Map<String, ?> allPlainData = sp.getAll();
Log.d("SP_TEST", "所有明文数据:" + allPlainData);
// 2. 获取所有加密数据(需指定解密模式)
Map<String, ?> allEncryptedData = sp.getAll(SecureSPUtils.ENCRYPT_MODE);
Log.d("SP_TEST", "所有加密数据(已解密):" + allEncryptedData);
// 3. 遍历数据(示例)
for (Map.Entry<String, ?> entry : allPlainData.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
Log.d("SP_TEST", "key:" + key + ",value:" + value);
}
}
四、注意事项(避坑指南)
- 初始化顺序 :必须先在 Application 中初始化,再在其他地方调用
getInstance()
,否则会抛出IllegalStateException
。 - 加密 / 解密一致性 :存储时用
ENCRYPT_MODE
,读取时必须也用ENCRYPT_MODE
,否则会读取到加密后的乱码。 - 密钥安全性 :默认密钥
your-secret-key
不安全,务必替换为自定义密钥(如MyApp_2024_Secret
),且不要硬编码在代码中(可从服务器获取或用设备唯一标识生成)。 - 多进程限制 :
MODE_MULTI_PROCESS
仅在 Android 7.0(API 24)以下有效,7.0+ 系统会忽略该模式,多进程场景建议用 ContentProvider 或 MMKV 替代。 - 数据类型限制:SharedPreferences 仅支持 String、int、long、float、boolean、Set<String> 类型,复杂对象需先序列化(如 Gson 转 String)再存储。
五、常见问题排查
-
报错:SecureSPUtils not initialized → 原因:未在 Application 中初始化,或初始化代码未执行。→ 解决:检查 MyApp 是否在 AndroidManifest.xml 中配置,且
onCreate()
方法是否被调用。 -
读取加密数据为乱码→ 原因:存储时用加密模式,读取时用了明文模式;或密钥不一致。→ 解决:确保存储和读取的模式一致,且初始化时的密钥和存储时的密钥相同。
-
generateKey 相关报错→ 原因:密钥为空或格式非法,或设备不支持 AES 算法(极少情况)。→ 解决:确保密钥不为空,且长度符合 AES 要求(16/24/32 字节)。
// Application 初始化
SecureSPUtils.init(this);// 默认不加密
SecureSPUtils.getInstance().putString("user_name", "张三");
String name = SecureSPUtils.getInstance().getString("user_name");// 需要加密的地方
SecureSPUtils.getInstance().putString("token", "abcd1234", SecureSPUtils.ENCRYPT_MODE);
String token = SecureSPUtils.getInstance().getString("token", "", SecureSPUtils.ENCRYPT_MODE);