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
接口
- 要创build
SharedPreferences
您需要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
-
请记住,偏好对象是更长的
get
,commit
,apply
,remove
和clear
操作将越大。 所以强烈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", "");
- jarsigner在哪里?
- 如何避免始终从Google云端硬盘加载caching的应用数据
- Android中的时间码执行
- 在layout / main.xml主布局文件中的TextView中设置textColor不引用colors.xml文件。 (它想要#RRGGBB而不是@ color / text_color)
- APP_PLATFORM,android:minSdkVersion和android:targetSdkVersion之间有什么关系?
- Android:自动selectdebugging/发布Maps api key?
- 对于后退button的按下或触摸,不调用setOnCancelListener和setOnDismissListener
- Android N要求IDE使用Java 1.8或更高版本运行?
- 更新到版本30后缺less“<sdk> / extras / google / google_play_services / libproject”文件夹