logo

Android序列化对象存储到SharedPreferences全攻略

作者:php是最好的2025.09.19 11:52浏览量:0

简介:本文深入探讨Android开发中如何通过序列化技术将复杂对象安全存储到SharedPreferences,涵盖序列化原理、实现方案、性能优化及安全实践,为开发者提供完整解决方案。

一、SharedPreferences存储机制解析

SharedPreferences是Android提供的轻量级键值对存储框架,底层采用XML文件存储数据。其设计初衷是存储简单类型(如String、int、boolean等),但对于复杂对象(如自定义类实例)的直接存储存在天然限制。

1.1 原始存储方式局限性

传统方式通过Editor.putString()存储对象时,开发者常采用以下两种方法:

  • 手动拆解字段:将对象属性逐个拆解为基本类型存储

    1. // 示例:存储User对象(不推荐)
    2. User user = new User("Alice", 25);
    3. SharedPreferences.Editor editor = getSharedPreferences("data", MODE_PRIVATE).edit();
    4. editor.putString("name", user.getName());
    5. editor.putInt("age", user.getAge());
    6. editor.apply();

    问题:代码冗余度高,当对象结构变更时需同步修改所有存储代码。

  • JSON序列化:使用Gson等库将对象转为JSON字符串

    1. // 示例:JSON序列化(存在性能问题)
    2. Gson gson = new Gson();
    3. String json = gson.toJson(user);
    4. editor.putString("user", json);
    5. editor.apply();

    问题:大对象序列化可能引发ANR,且未考虑类型安全

1.2 序列化存储必要性

当需要存储包含嵌套对象、集合等复杂结构的对象时,必须通过序列化技术将对象转换为字节流或结构化字符串。Android平台推荐两种序列化方案:

  • Java原生序列化:实现Serializable接口
  • 高效序列化框架:如Protocol Buffers、FlatBuffers

二、Java原生序列化实现方案

2.1 实现Serializable接口

  1. public class User implements Serializable {
  2. private static final long serialVersionUID = 1L;
  3. private String name;
  4. private int age;
  5. private List<String> hobbies;
  6. // 构造方法、getter/setter省略
  7. }

关键点

  • serialVersionUID用于版本控制,避免反序列化失败
  • 静态变量和transient修饰的字段不会被序列化

2.2 序列化与反序列化工具类

  1. public class SerializationUtils {
  2. public static String serialize(Object obj) throws IOException {
  3. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  4. ObjectOutputStream oos = new ObjectOutputStream(baos);
  5. oos.writeObject(obj);
  6. oos.close();
  7. return Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT);
  8. }
  9. public static Object deserialize(String data) throws IOException, ClassNotFoundException {
  10. byte[] bytes = Base64.decode(data, Base64.DEFAULT);
  11. ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
  12. ObjectInputStream ois = new ObjectInputStream(bais);
  13. return ois.readObject();
  14. }
  15. }

实现细节

  • 使用Base64编码解决字节数组存储问题
  • 必须处理IOExceptionClassNotFoundException

2.3 完整存储流程

  1. // 存储对象
  2. User user = new User("Bob", 30, Arrays.asList("Reading", "Swimming"));
  3. try {
  4. String serializedData = SerializationUtils.serialize(user);
  5. getSharedPreferences("app_data", MODE_PRIVATE)
  6. .edit()
  7. .putString("user_data", serializedData)
  8. .apply();
  9. } catch (IOException e) {
  10. e.printStackTrace();
  11. }
  12. // 读取对象
  13. try {
  14. String serializedData = getSharedPreferences("app_data", MODE_PRIVATE)
  15. .getString("user_data", null);
  16. if (serializedData != null) {
  17. User restoredUser = (User) SerializationUtils.deserialize(serializedData);
  18. }
  19. } catch (Exception e) {
  20. e.printStackTrace();
  21. }

三、性能优化与最佳实践

3.1 序列化框架对比

方案 存储体积 序列化速度 跨平台支持
Java Serializable 仅Java
Gson 多语言
Protocol Buffers 多语言

推荐方案

  • 小型对象:Gson(开发效率高)
  • 大型/高频对象:Protocol Buffers

3.2 异步存储策略

  1. // 使用ExecutorService避免主线程阻塞
  2. ExecutorService executor = Executors.newSingleThreadExecutor();
  3. executor.execute(() -> {
  4. try {
  5. String data = SerializationUtils.serialize(largeObject);
  6. getSharedPreferences("data", MODE_PRIVATE)
  7. .edit()
  8. .putString("key", data)
  9. .apply();
  10. } catch (IOException e) {
  11. Log.e("Serialization", "Error saving data", e);
  12. }
  13. });
  14. executor.shutdown();

3.3 存储空间管理

  1. // 清理过期数据的工具方法
  2. public static void cleanOldData(SharedPreferences prefs, long expireMillis) {
  3. Map<String, ?> allData = prefs.getAll();
  4. long currentTime = System.currentTimeMillis();
  5. for (Map.Entry<String, ?> entry : allData.entrySet()) {
  6. if (entry.getKey().endsWith("_timestamp")) {
  7. long timestamp = Long.parseLong((String) entry.getValue());
  8. if (currentTime - timestamp > expireMillis) {
  9. String dataKey = entry.getKey().replace("_timestamp", "");
  10. prefs.edit().remove(dataKey).remove(entry.getKey()).apply();
  11. }
  12. }
  13. }
  14. }

四、安全增强方案

4.1 数据加密实现

  1. public class SecureSerializationUtils {
  2. private static final String ALGORITHM = "AES/CBC/PKCS5Padding";
  3. private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding";
  4. public static String encryptAndSerialize(Object obj, SecretKey key, byte[] iv)
  5. throws Exception {
  6. String serialized = SerializationUtils.serialize(obj);
  7. Cipher cipher = Cipher.getInstance(ALGORITHM);
  8. cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
  9. byte[] encrypted = cipher.doFinal(serialized.getBytes());
  10. return Base64.encodeToString(encrypted, Base64.DEFAULT);
  11. }
  12. // 解密方法对称实现
  13. }

4.2 密钥管理方案

  • Android Keystore系统:存储加密密钥
    ```java
    KeyStore keyStore = KeyStore.getInstance(“AndroidKeyStore”);
    keyStore.load(null);

KeyGenerator keyGenerator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES, “AndroidKeyStore”);
keyGenerator.init(new KeyGenParameterSpec.Builder(
“my_key_alias”,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.build());
SecretKey secretKey = keyGenerator.generateKey();

  1. # 五、常见问题解决方案
  2. ## 5.1 序列化版本不一致问题
  3. **现象**:`InvalidClassException: local class incompatible`
  4. **解决方案**:
  5. 1. 显式定义`serialVersionUID`
  6. 2. 确保所有使用该类的模块版本一致
  7. ## 5.2 大对象存储限制
  8. **现象**:`TransactionTooLargeException`
  9. **解决方案**:
  10. 1. 分割大对象为多个小对象存储
  11. 2. 使用Room数据库替代SharedPreferences存储大对象
  12. ## 5.3 跨进程访问问题
  13. **现象**:多进程访问SharedPreferences导致数据丢失
  14. **解决方案**:
  15. 1. 使用`Context.MODE_MULTI_PROCESS`模式(已废弃)
  16. 2. 改用ContentProviderMMKV等支持多进程的存储方案
  17. # 六、替代方案对比
  18. ## 6.1 MMKV框架
  19. **优势**:
  20. - 基于mmap内存映射
  21. - 支持多进程
  22. - 性能优于SharedPreferences
  23. **集成示例**:
  24. ```gradle
  25. implementation 'com.tencent:mmkv:1.3.0'
  1. // 初始化
  2. MMKV.initialize(this);
  3. MMKV mmkv = MMKV.defaultMMKV();
  4. // 存储对象
  5. User user = new User(...);
  6. String json = new Gson().toJson(user);
  7. mmkv.putString("user", json);
  8. // 读取对象
  9. String json = mmkv.getString("user", null);
  10. User user = new Gson().fromJson(json, User.class);

6.2 Room数据库方案

适用场景

  • 需要复杂查询的关系型数据
  • 对象结构频繁变更

实现要点

  1. 定义Entity类
  2. 创建DAO接口
  3. 初始化数据库

七、总结与建议

  1. 简单对象存储:Gson + SharedPreferences组合(开发效率最高)
  2. 高性能需求:Protocol Buffers + MMKV(内存占用小,速度快)
  3. 安全敏感数据:加密序列化 + Android Keystore(防止数据泄露)
  4. 大型应用架构:考虑分层存储策略(SharedPreferences存配置,Room存业务数据)

最佳实践建议

  • 为所有序列化对象添加版本控制字段
  • 实现数据迁移机制处理对象结构变更
  • 对超过10KB的对象考虑使用替代存储方案
  • 定期清理过期数据防止SharedPreferences文件膨胀

通过合理选择序列化方案和存储策略,可以在保证开发效率的同时,实现安全、高效的对象持久化存储。

相关文章推荐

发表评论