【问题标题】:Android - SharedPreferences with serializable objectAndroid - 具有可序列化对象的 SharedPreferences
【发布时间】:2011-08-14 13:45:52
【问题描述】:

我知道 SharedPreferences 有 putString()putFloat()putLong()putInt()putBoolean()。但是我需要在SharedPreferences 中存储一个Serializable 类型的对象。我怎样才能做到这一点?

【问题讨论】:

    标签: android serialization sharedpreferences


    【解决方案1】:

    简而言之,您不能尝试将您的对象序列化为私有文件,这相当于同一件事。下面的示例类:

    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    
    import android.app.Activity;
    import android.content.Context;
    
    /**
     *
     * Writes/reads an object to/from a private local file
     * 
     *
     */
    public class LocalPersistence {
    
    
        /**
         * 
         * @param context
         * @param object
         * @param filename
         */
        public static void witeObjectToFile(Context context, Object object, String filename) {
    
            ObjectOutputStream objectOut = null;
            try {
    
                FileOutputStream fileOut = context.openFileOutput(filename, Activity.MODE_PRIVATE);
                objectOut = new ObjectOutputStream(fileOut);
                objectOut.writeObject(object);
                fileOut.getFD().sync();
    
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (objectOut != null) {
                    try {
                        objectOut.close();
                    } catch (IOException e) {
                        // do nowt
                    }
                }
            }
        }
    
    
        /**
         * 
         * @param context
         * @param filename
         * @return
         */
        public static Object readObjectFromFile(Context context, String filename) {
    
            ObjectInputStream objectIn = null;
            Object object = null;
            try {
    
                FileInputStream fileIn = context.getApplicationContext().openFileInput(filename);
                objectIn = new ObjectInputStream(fileIn);
                object = objectIn.readObject();
    
            } catch (FileNotFoundException e) {
                // Do nothing
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } finally {
                if (objectIn != null) {
                    try {
                        objectIn.close();
                    } catch (IOException e) {
                        // do nowt
                    }
                }
            }
    
            return object;
        }
    
    }
    

    【讨论】:

    • 代码中的filename 应该是绝对路径(例如:“/data/com.example.myapp/foobar.dat”),还是应该是相对路径(例如:“foobar.dat” ")?
    • 只是一个文件名,不允许使用路径分隔符,如 API 文档中所述 developer.android.com/reference/android/content/…, int)
    • 如何删除上面你的方法保存的文件?
    • 我认为它只是 context.getFileStreamPath(filename).delete();
    • 只是出于好奇 - 您的 FileInputStream 和 FileOutputStream 是否应该在您的“finally”块以及 ObjectInputStream 和 ObjectOutputStream 中关闭?
    【解决方案2】:

    公认的答案具有误导性,我们可以使用 GSON 将可序列化对象存储到 SharedPreferences 中。在google-gson 阅读有关它的更多信息。

    您可以在 Gradle 文件中添加 GSON 依赖项:

    compile 'com.google.code.gson:gson:2.7'
    

    这里是sn-p:

    首先,创建您常用的 sharedPreferences:

    //Creating a shared preference
    SharedPreferences  mPrefs = getPreferences(MODE_PRIVATE);
    

    从可序列化对象保存到首选项:

     Editor prefsEditor = mPrefs.edit();
     Gson gson = new Gson();
     String json = gson.toJson(YourSerializableObject);
     prefsEditor.putString("SerializableObject", json);
     prefsEditor.commit();
    

    从首选项中获取可序列化对象:

    Gson gson = new Gson();
    String json = mPrefs.getString("SerializableObject", "");
    yourSerializableObject = gson.fromJson(json, YourSerializableObject.class);
    

    【讨论】:

    • 使用 Json 很危险。如果我们改变数据结构,反序列化的数据将不匹配。
    • @Elye 存储在 SharedPreferences 中的任何信息都与运行时更改不同步,就像在任何其他数据库中一样。作为程序员,您有责任在数据发生变化时照顾和存储数据。某些数据库(例如 Realm 和 Room)可能会通知您有关数据库中的更改,但如果加载数据,则只有您负责将其放回数据库。
    【解决方案3】:

    如果您的对象是简单的 POJO,您可以将对象转换为 JSON 字符串并使用 putString() 将其保存在共享首选项中。

    【讨论】:

      【解决方案4】:

      没有文件也可以。

      我正在将信息序列化为 base64,这样我就可以将其作为字符串保存在首选项中。

      以下代码将可序列化的对象序列化为 base64 字符串,反之亦然: 导入android.util.Base64;

      import java.io.ByteArrayInputStream;
      import java.io.ByteArrayOutputStream;
      import java.io.IOException;
      import java.io.ObjectInputStream;
      import java.io.ObjectOutputStream;
      import java.io.Serializable;
      
      
      public class ObjectSerializerHelper {
          static public String objectToString(Serializable object) {
              String encoded = null;
              try {
                  ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                  ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
                  objectOutputStream.writeObject(object);
                  objectOutputStream.close();
                  encoded = new String(Base64.encodeToString(byteArrayOutputStream.toByteArray(),0));
              } catch (IOException e) {
                  e.printStackTrace();
              }
              return encoded;
          }
      
          @SuppressWarnings("unchecked")
          static public Serializable stringToObject(String string){
              byte[] bytes = Base64.decode(string,0);
              Serializable object = null;
              try {
                  ObjectInputStream objectInputStream = new ObjectInputStream( new ByteArrayInputStream(bytes) );
                  object = (Serializable)objectInputStream.readObject();
              } catch (IOException e) {
                  e.printStackTrace();
              } catch (ClassNotFoundException e) {
                  e.printStackTrace();
              } catch (ClassCastException e) {
                  e.printStackTrace();
              }
              return object;
          }
      
      }
      

      【讨论】:

      • 上面的类可以粘贴到你的POJO类中,你可以很容易地实现public YourPojo getFromPreferences(Context context)public void saveToPreferences(Context context),使用上面的两种方法,以及PreferenceManager.getDefaultSharedPreferences(context)
      【解决方案5】:

      我们可以使用 Kotlin 创建一个易于使用的语法。

      @Throws(JsonIOException::class)
      fun Serializable.toJson(): String {
         return Gson().toJson(this)
      }
      
      @Throws(JsonSyntaxException::class)
       fun <T> String.to(type: Class<T>): T where T : Serializable {
       return Gson().fromJson(this, type)
      }
      
      @Throws(JsonIOException::class)
      fun SharedPreferences.Editor.putSerializable(key: String, o: Serializable?) = apply {
         putString(key, o?.toJson())
      }
      
      @Throws(JsonSyntaxException::class)
         fun <T> SharedPreferences.getSerializable(key: String, type: Class<T>): T? where T : Serializable {
          return getString(key, null)?.to(type)
      }
      

      然后使用类似的get/put() 将任何Serializable 保存到SharedPreferences

      这里有完整的要点Save Serializables in Shared Preferences with Kotlin and GSON

      正如其他答案中提到的,当数据类的结构发生变化时,您可能必须考虑迁移。或者至少必须更改您用于存储的密钥。

      【讨论】:

      • Kotlin 是要走的路;)
      【解决方案6】:

      2020:如果要保存对象,那么最好使用 Proto DataStore 而不是 SharedPreferences。

      与“旧”的 SharedPreferences 相比,它带来了很大的好处,即:

      • 用于存储和读取数据的异步 API (Flow)
      • 开箱即用的类型安全(协议缓冲区)
      • 从 UI 线程调用安全(Dispatchers.IO 下面)

      还有许多其他好处,例如保证一致性的事务 API。

      查看我的博文以查看how to implement Proto DataStore easily

      【讨论】:

        【解决方案7】:

        如果您的对象与嵌套对象很复杂,并且您有一天可能需要删除嵌套对象,那么这是一种不好的机制,因为您需要实施自己的迁移策略(除非丢弃所有数据都可以)。

        如果是大量数据,这种机制缺乏事务支持。那你自己写吗?还是使用更合适的存储机制?我强烈建议使用更合适的存储机制,例如 SQLite DB 的文件系统。

        【讨论】:

          猜你喜欢
          • 2013-02-05
          • 2013-01-20
          • 1970-01-01
          • 1970-01-01
          • 2017-10-17
          • 1970-01-01
          • 2023-04-03
          • 1970-01-01
          • 2011-11-10
          相关资源
          最近更新 更多