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

相关推荐
踢球的打工仔18 小时前
PHP面向对象(7)
android·开发语言·php
安卓理事人18 小时前
安卓socket
android
安卓理事人1 天前
安卓LinkedBlockingQueue消息队列
android
万能的小裴同学1 天前
Android M3U8视频播放器
android·音视频
q***57741 天前
MySql的慢查询(慢日志)
android·mysql·adb
JavaNoober1 天前
Android 前台服务 "Bad Notification" 崩溃机制分析文档
android
城东米粉儿1 天前
关于ObjectAnimator
android
zhangphil1 天前
Android渲染线程Render Thread的RenderNode与DisplayList,引用Bitmap及Open GL纹理上传GPU
android
火柴就是我1 天前
从头写一个自己的app
android·前端·flutter
lichong9511 天前
XLog debug 开启打印日志,release 关闭打印日志
android·java·前端