android SharedPreferences 工具类 * 兼容 Android 16+ (API 16)
下面是优化后的完整代码,兼容 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// 但官方已不推荐使用多进程 SharedPreferencesmode = 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 如果未初始化*/@NonNullpublic 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 值,如果不存在则返回默认值*/@Nullablepublic String getString(@NonNull String key) {return getString(key, "");}/*** 获取 String 类型数据* @param key 键* @param defaultValue 默认值* @return 值,如果不存在则返回默认值*/@Nullablepublic 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 值,如果不存在则返回默认值*/@Nullablepublic Set<String> getStringSet(@NonNull String key) {return getStringSet(key, null);}/*** 获取 Set<String> 类型数据* @param key 键* @param defaultValue 默认值* @return 值,如果不存在则返回默认值*/@Nullablepublic 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 所有键值对*/@NonNullpublic 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 {@Overridepublic 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);}}}}@NonNullpublic 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();}}@Nullablepublic String getString(@NonNull String key) {return getString(key, "", mDefaultMode);}@Nullablepublic 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();}}/*** 获取所有数据(自动根据默认模式解密)*/@NonNullpublic Map<String, ?> getAll() {return getAll(mDefaultMode);}/*** 获取所有数据(可选择解密模式)*/@NonNullpublic 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 {@Overridepublic 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 类:
<applicationandroid: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); // 输出:25Log.d("SP_TEST", "首次启动:" + isFirstLaunch); // 输出:falseLog.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); // 输出:13800138000Log.d("SP_TEST", "用户 ID:" + userId); // 输出:10086
}
2.3 3. 其他常用操作(删除、清空、判断是否包含)
java
运行
private void otherOperations() {SecureSPUtils sp = SecureSPUtils.getInstance();// 1. 判断是否包含某个 keyboolean 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);