简介:本文深入探讨Android SharedPreferences对象存储机制,分析其原生局限性与优化方案,提供多种对象存储实现路径及代码示例,助力开发者构建高效可靠的数据持久化方案。
SharedPreferences是Android提供的轻量级键值对存储框架,基于XML文件实现数据持久化。其核心设计包含三个关键组件:
<map>标签包裹的键值对集合,支持String、Boolean、Float、Long等基础类型Context.getSharedPreferences()获取实例,支持MODE_PRIVATE(默认)和MODE_WORLD_READABLE/WRITEABLE(已废弃)AtomicFile实现文件级原子操作,通过MemoryCommitResult处理异步提交典型使用场景示例:
// 写入数据SharedPreferences pref = getSharedPreferences("user_data", MODE_PRIVATE);pref.edit().putString("username", "android_dev").putInt("login_count", 5).apply(); // 异步提交// 读取数据String username = pref.getString("username", "default");int count = pref.getInt("login_count", 0);
SharedPreferences原生仅支持6种基础类型,存储自定义对象需序列化。常见错误实践:
// 错误示范:直接存储对象(会抛出ClassCastException)User user = new User("Alice", 25);pref.edit().putObject("user", user); // 不存在此方法
apply()方法虽异步但仍有IO阻塞风险,commit()会阻塞UI线程
// 1. 定义可序列化对象public class User implements Serializable {private String name;private int age;// 构造方法、getter/setter省略}// 2. 序列化工具类public class PrefUtils {public static void saveObject(Context ctx, String key, Serializable object) {SharedPreferences pref = ctx.getSharedPreferences("objects", MODE_PRIVATE);ByteArrayOutputStream bos = new ByteArrayOutputStream();try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {oos.writeObject(object);String encoded = Base64.encodeToString(bos.toByteArray(), Base64.DEFAULT);pref.edit().putString(key, encoded).apply();} catch (IOException e) {e.printStackTrace();}}public static Object getObject(Context ctx, String key) {SharedPreferences pref = ctx.getSharedPreferences("objects", MODE_PRIVATE);String encoded = pref.getString(key, null);if (encoded == null) return null;byte[] data = Base64.decode(encoded, Base64.DEFAULT);try (ByteArrayInputStream bis = new ByteArrayInputStream(data);ObjectInputStream ois = new ObjectInputStream(bis)) {return ois.readObject();} catch (IOException | ClassNotFoundException e) {e.printStackTrace();return null;}}}
适用场景:简单对象存储,数据量较小(<100KB)
// 使用GSON库实现public class JsonPrefHelper {private final Gson gson = new Gson();private final SharedPreferences pref;public JsonPrefHelper(Context context) {pref = context.getSharedPreferences("json_data", MODE_PRIVATE);}public <T> void save(String key, T object) {String json = gson.toJson(object);pref.edit().putString(key, json).apply();}public <T> T get(String key, Class<T> classOfT) {String json = pref.getString(key, null);return json == null ? null : gson.fromJson(json, classOfT);}}// 使用示例JsonPrefHelper helper = new JsonPrefHelper(context);User user = new User("Bob", 30);helper.save("current_user", user);User loaded = helper.get("current_user", User.class);
优势:
针对大数据量场景,可采用分片存储:
public class ShardedPrefManager {private static final int SHARD_SIZE = 50; // 每个分片存储50个键值对private final Context context;public ShardedPrefManager(Context context) {this.context = context;}public void putMulti(Map<String, ?> dataMap) {int shardCount = (int) Math.ceil((double) dataMap.size() / SHARD_SIZE);Iterator<Map.Entry<String, ?>> iterator = dataMap.entrySet().iterator();for (int i = 0; i < shardCount; i++) {SharedPreferences shard = context.getSharedPreferences("shard_" + i, MODE_PRIVATE);Editor editor = shard.edit();for (int j = 0; j < SHARD_SIZE && iterator.hasNext(); j++) {Map.Entry<String, ?> entry = iterator.next();// 根据值类型调用相应put方法if (entry.getValue() instanceof String) {editor.putString(entry.getKey(), (String) entry.getValue());} // 其他类型处理省略...}editor.apply();}}}
public class CachedPreferences {private final SharedPreferences pref;private final ConcurrentHashMap<String, Object> cache = new ConcurrentHashMap<>();public CachedPreferences(Context context) {pref = context.getSharedPreferences("cached", MODE_PRIVATE);// 初始化时加载常用数据到缓存loadFrequentData();}public <T> T get(String key, Class<T> type) {return (T) cache.computeIfAbsent(key, k -> {String json = pref.getString(k, null);return json != null ? new Gson().fromJson(json, type) : null;});}public void put(String key, Object value) {cache.put(key, value);pref.edit().putString(key, new Gson().toJson(value)).apply();}}
public class EncryptedPreferences {private static final String AES = "AES/CBC/PKCS5Padding";private final SharedPreferences pref;private final SecretKeySpec secretKey;private final IvParameterSpec iv;public EncryptedPreferences(Context context, String key) {pref = context.getSharedPreferences("secure", MODE_PRIVATE);// 实际项目中应从安全存储获取密钥this.secretKey = new SecretKeySpec(key.getBytes(), "AES");this.iv = new IvParameterSpec(new byte[16]); // 实际应使用随机IV}public String encrypt(String value) throws Exception {Cipher cipher = Cipher.getInstance(AES);cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);byte[] encrypted = cipher.doFinal(value.getBytes());return Base64.encodeToString(encrypted, Base64.DEFAULT);}public String decrypt(String encrypted) throws Exception {Cipher cipher = Cipher.getInstance(AES);cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);byte[] decoded = Base64.decode(encrypted, Base64.DEFAULT);byte[] decrypted = cipher.doFinal(decoded);return new String(decrypted);}public void saveEncrypted(String key, String value) {try {String encrypted = encrypt(value);pref.edit().putString(key, encrypted).apply();} catch (Exception e) {Log.e("EncryptedPref", "Encryption failed", e);}}}
数据分类存储:
性能优化策略:
Editor.apply()替代多次commit()迁移方案:
public class PrefMigrator {public static void migrateToNewVersion(Context context) {SharedPreferences oldPref = context.getSharedPreferences("old_data", MODE_PRIVATE);SharedPreferences newPref = context.getSharedPreferences("new_data", MODE_PRIVATE);Map<String, ?> allEntries = oldPref.getAll();if (!allEntries.isEmpty()) {Editor editor = newPref.edit();for (Map.Entry<String, ?> entry : allEntries.entrySet()) {// 根据类型进行转换处理if (entry.getValue() instanceof Integer) {editor.putInt(entry.getKey(), (Integer) entry.getValue());} // 其他类型处理...}editor.apply();oldPref.edit().clear().apply(); // 清空旧数据}}}
测试建议:
| 方案 | 适用场景 | 存储限制 | 并发支持 |
|---|---|---|---|
| SharedPreferences | 简单配置 | 单文件限制 | 单进程安全 |
| Room数据库 | 结构化数据 | 无明显限制 | 支持多进程 |
| DataStore | 类型安全 | 无明显限制 | 支持RxJava |
| MMKV | 高性能 | 键值对限制 | 多进程安全 |
决策建议:
SharedPreferences作为Android基础存储方案,通过合理的扩展设计可以满足大多数对象存储需求。开发者应根据实际场景选择最适合的方案:对于简单配置保持原生使用;对于复杂对象采用JSON序列化;对于高性能需求考虑MMKV等替代方案。未来随着Jetpack DataStore的普及,类型安全的存储方案将成为主流,但SharedPreferences在轻量级场景中仍将保持重要地位。