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
            // 但官方已不推荐使用多进程 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) {
            }
        }
    }
}

优化亮点

  1. 单例模式:使用双重检查锁实现线程安全的单例
  2. 初始化机制 :需要先调用 init() 方法初始化
  3. 内存安全:使用 Application Context 避免内存泄漏
  4. API 兼容:根据系统版本选择 apply () 或 commit ()
  5. 功能完整:支持所有 SharedPreferences 数据类型
  6. 异常处理:添加 try-catch 防止崩溃
  7. 线程安全: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);
    }
}

四、注意事项(避坑指南)

  1. 初始化顺序 :必须先在 Application 中初始化,再在其他地方调用 getInstance(),否则会抛出 IllegalStateException
  2. 加密 / 解密一致性 :存储时用 ENCRYPT_MODE,读取时必须也用 ENCRYPT_MODE,否则会读取到加密后的乱码。
  3. 密钥安全性 :默认密钥 your-secret-key 不安全,务必替换为自定义密钥(如 MyApp_2024_Secret),且不要硬编码在代码中(可从服务器获取或用设备唯一标识生成)。
  4. 多进程限制MODE_MULTI_PROCESS 仅在 Android 7.0(API 24)以下有效,7.0+ 系统会忽略该模式,多进程场景建议用 ContentProvider 或 MMKV 替代。
  5. 数据类型限制:SharedPreferences 仅支持 String、int、long、float、boolean、Set<String> 类型,复杂对象需先序列化(如 Gson 转 String)再存储。

五、常见问题排查

  1. 报错:SecureSPUtils not initialized → 原因:未在 Application 中初始化,或初始化代码未执行。→ 解决:检查 MyApp 是否在 AndroidManifest.xml 中配置,且 onCreate() 方法是否被调用。

  2. 读取加密数据为乱码→ 原因:存储时用加密模式,读取时用了明文模式;或密钥不一致。→ 解决:确保存储和读取的模式一致,且初始化时的密钥和存储时的密钥相同。

  3. 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);

相关推荐
2501_915909062 小时前
App Store 上架完整流程解析,iOS 应用发布步骤、ipa 文件上传工具、TestFlight 测试与苹果审核经验
android·macos·ios·小程序·uni-app·cocoa·iphone
2501_916008892 小时前
iOS 26 全景揭秘,新界面、功能创新、兼容挑战与各种工具在新版系统中的定位
android·macos·ios·小程序·uni-app·cocoa·iphone
小趴菜822712 小时前
安卓接入Kwai广告源
android·kotlin
2501_9160137412 小时前
iOS 混淆与 App Store 审核兼容性 避免被拒的策略与实战流程(iOS 混淆、ipa 加固、上架合规)
android·ios·小程序·https·uni-app·iphone·webview
程序员江同学13 小时前
Kotlin 技术月报 | 2025 年 9 月
android·kotlin
码农的小菜园14 小时前
探究ContentProvider(一)
android
时光少年15 小时前
Compose AnnotatedString实现Html样式解析
android·前端
hnlgzb16 小时前
安卓中,kotlin如何写app界面?
android·开发语言·kotlin
jzlhll12317 小时前
deepseek kotlin flow快生产者和慢消费者解决策略
android·kotlin