Android SharedPreferences最佳实践

在我build立的应用程序中,我们相当依赖SharedPreferences,这让我想到了访问SharedPreferences的最佳实践。 例如许多人说通过这个调用访问它的适当方式:

PreferenceManager.getDefaultSharedPreferences(Context context) 

但是,这似乎是危险的。 如果您有一个依赖于SharedPreferences的大型应用程序,那么您可以进行密钥复制,特别是在使用某些依赖于SharedPreferences的第三方库的情况下。 在我看来,更好的使用方法是:

 Context.getSharedPreferences(String name, int mode) 

这样,如果您有一个严重依赖于SharedPreferences的类,则可以创build仅由您的类使用的首选项文件。 您可以使用该类的完全限定名,以确保该文件很可能不会被其他人复制。

也基于这个SO问题: 应该访问SharedPreferencesclosuresUI线程? ,似乎访问SharedPreferences应该离开UI线程是有道理的。

Android开发人员在应用程序中使用SharedPreferences时应该注意哪些其他最佳实践?

如果您有一个依赖于SharedPreferences的大型应用程序,那么您可以进行密钥复制,特别是在使用某些依赖于SharedPreferences的第三方库的情况下。

库不应该使用特定的SharedPreferences 。 默认的SharedPreferences只能被应用程序使用。

这样,如果您有一个严重依赖于SharedPreferences的类,则可以创build仅由您的类使用的首选项文件。

当然欢迎您这样做。 我不会在应用程序级别作为SharedPreferences主要原因是让它们在应用程序中的组件之间共享。 pipe理这个命名空间的开发团队应该没有问题,就像pipe理类,包,资源或其他项目级别的东西的名字一样。 此外,默认的SharedPreferences是您的PreferenceActivity将使用的。

但是,回到您的库指向, 可重用的库应该为它们的库使用单独的SharedPreferences 。 我不会把它放在一个类名上,因为那样你就重构了一个重新构造你的应用程序。 相反,select一个独特的名称(例如,基于库名称,如"com.commonsware.cwac.wakeful.WakefulIntentService" ),但是稳定。

似乎访问SharedPreferences应该离开UI线程是有道理的。

理想的是,是的。 我最近发布了一个SharedPreferencesLoader ,帮助这个。

Android开发人员在应用程序中使用SharedPreferences时应该注意哪些其他最佳实践?

不要过度依赖他们。 它们存储在XML文件中,不是事务性的。 数据库应该是您的主要数据存储区,特别是对于您真正不想丢失的数据。

我写了一篇文章,也可以在这里find。 它描述了SharedPreferences是什么:

最佳实践:SharedPreferences

Android提供了许多存储应用程序数据的方法。 其中一种方法是将SharedPreferences对象用于将私有原始数据存储在键值对中。

所有的逻辑只基于三个简单的类:

  • SharedPreferences
  • SharedPreferences.Editor
  • SharedPreferences.OnSharedPreferenceChangeListener

SharedPreferences

SharedPreferences是他们的主要。 它负责获取(parsing)存储的数据,提供获取Editor对象的接口以及添加和删除OnSharedPreferenceChangeListener接口

  • 要创buildSharedPreferences您需要Context对象(可以是应用程序Context
  • getSharedPreferences方法parsing首选项文件并为其创buildMap对象
  • 您可以使用Context提供的几种模式创build它,强烈build议使用MODE_PRIVATE,因为创build世界可读/可写文件是非常危险的,并且可能会在应用程序中导致安全漏洞

     // parse Preference file SharedPreferences preferences = context.getSharedPreferences("com.example.app", Context.MODE_PRIVATE); // get values from Map preferences.getBoolean("key", defaultValue) preferences.get..("key", defaultValue) // you can get all Map but be careful you must not modify the collection returned by this // method, or alter any of its contents. Map<String, ?> all = preferences.getAll(); // get Editor object SharedPreferences.Editor editor = preferences.edit(); //add on Change Listener preferences.registerOnSharedPreferenceChangeListener(mListener); //remove on Change Listener preferences.unregisterOnSharedPreferenceChangeListener(mListener); // listener example SharedPreferences.OnSharedPreferenceChangeListener mOnSharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() { @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { } }; 

编辑

SharedPreferences.Editor是用于修改SharedPreferences对象中的值的接口。 在编辑器中进行的所有更改都是批处理的,除非您调用commit()或apply()方法,否则不会将其复制回原始的SharedPreferences

  • 使用简单的界面将值放入Editor
  • 保存与commit()同步的值或与更快的apply同步。 事实上,使用commit()使用不同的线程更安全。 这就是为什么我更喜欢使用commit()
  • 使用remove()移除单个值或使用clear()清除所有值

     // get Editor object SharedPreferences.Editor editor = preferences.edit(); // put values in editor editor.putBoolean("key", value); editor.put..("key", value); // remove single value by key editor.remove("key"); // remove all values editor.clear(); // commit your putted values to the SharedPreferences object synchronously // returns true if success boolean result = editor.commit(); // do the same as commit() but asynchronously (faster but not safely) // returns nothing editor.apply(); 

性能和提示

  • SharedPreferences是一个Singleton对象,所以你可以很容易地得到任意数量的引用,只有当你第一次调用getSharedPreferences或者只为它创build一个引用时,它才会打开文件。

     // There are 1000 String values in preferences SharedPreferences first = context.getSharedPreferences("com.example.app", Context.MODE_PRIVATE); // call time = 4 milliseconds SharedPreferences second = context.getSharedPreferences("com.example.app", Context.MODE_PRIVATE); // call time = 0 milliseconds SharedPreferences third = context.getSharedPreferences("com.example.app", Context.MODE_PRIVATE); // call time = 0 milliseconds 
  • 由于SharedPreferences是一个Singleton对象,您可以更改它的任何实例,而不会害怕他们的数据会有所不同

     first.edit().putInt("key",15).commit(); int firstValue = first.getInt("key",0)); // firstValue is 15 int secondValue = second.getInt("key",0)); // secondValue is also 15 
  • 当你第一次调用get方法时,它通过键分析值,并将这个值添加到地图。 所以对于第二个调用它只是从地图得到它,而不parsing。

     first.getString("key", null) // call time = 147 milliseconds first.getString("key", null) // call time = 0 milliseconds second.getString("key", null) // call time = 0 milliseconds third.getString("key", null) // call time = 0 milliseconds 
  • 请记住,偏好对象是更长的getcommitapplyremoveclear操作将越大。 所以强烈build议将数据分成不同的小对象。

  • 应用程序更新后,您的首选项将不会被删除 。 所以有时候你需要创build一些迁移scheme。 例如,你有应用程序开始parsing本地JSON的应用程序,要做到这一点,只有在第一次启动后,你决定保存布尔标志wasLocalDataLoaded 。 过了一段时间,你更新了JSON并发布了新的应用程序版本。 用户将更新他们的应用程序,但他们不会加载新的JSON,因为他们已经在第一个应用程序版本中完成了。

     public class MigrationManager { private final static String KEY_PREFERENCES_VERSION = "key_preferences_version"; private final static int PREFERENCES_VERSION = 2; public static void migrate(Context context) { SharedPreferences preferences = context.getSharedPreferences("pref", Context.MODE_PRIVATE); checkPreferences(preferences); } private static void checkPreferences(SharedPreferences thePreferences) { final double oldVersion = thePreferences.getInt(KEY_PREFERENCES_VERSION, 1); if (oldVersion < PREFERENCES_VERSION) { final SharedPreferences.Editor edit = thePreferences.edit(); edit.clear(); edit.putInt(KEY_PREFERENCES_VERSION, currentVersion); edit.commit(); } } } 
  • SharedPreferences存储在应用程序数据文件夹中的xml文件中

     // yours preferences /data/data/YOUR_PACKAGE_NAME/shared_prefs/YOUR_PREFS_NAME.xml // default preferences /data/data/YOUR_PACKAGE_NAME/shared_prefs/YOUR_PACKAGE_NAME_preferences.xml 

Android指南。

示例代码

 public class PreferencesManager { private static final String PREF_NAME = "com.example.app.PREF_NAME"; private static final String KEY_VALUE = "com.example.app.KEY_VALUE"; private static PreferencesManager sInstance; private final SharedPreferences mPref; private PreferencesManager(Context context) { mPref = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); } public static synchronized void initializeInstance(Context context) { if (sInstance == null) { sInstance = new PreferencesManager(context); } } public static synchronized PreferencesManager getInstance() { if (sInstance == null) { throw new IllegalStateException(PreferencesManager.class.getSimpleName() + " is not initialized, call initializeInstance(..) method first."); } return sInstance; } public void setValue(long value) { mPref.edit() .putLong(KEY_VALUE, value) .commit(); } public long getValue() { return mPref.getLong(KEY_VALUE, 0); } public void remove(String key) { mPref.edit() .remove(key) .commit(); } public boolean clear() { return mPref.edit() .clear() .commit(); } } 

有几个库可以帮助您处理SharedPreferences。 我最近发现了Hawk( https://github.com/orhanobut/hawk )和StoreBox( https://github.com/martino2k6/StoreBox )。

这是我的方式

 SharedPreferences settings = context.getSharedPreferences("prefs", 0); SharedPreferences.Editor editore = settings.edit(); editore.putString("key", "some value"); editore.apply(); 

读书

 SharedPreferences settings = getSharedPreferences("prefs", 0); Strings value = settings.getString("key", "");