[英]Android SharedPreferences Best Practices
In an application I have been building we rely on SharedPreferences quite a bit, this got me thinking about what is best practice when it comes to accessing SharedPreferences.在我一直在构建的应用程序中,我们相当依赖 SharedPreferences,这让我思考访问 SharedPreferences 时的最佳实践。 For instance many people say the appropriate way to access it is via this call:
例如,很多人说访问它的适当方式是通过这个调用:
PreferenceManager.getDefaultSharedPreferences(Context context)
However it seems like this could be dangerous.但是,这似乎很危险。 If you have a large application that is relying on SharedPreferences you could have key duplication, especially in the case of using some third party library that relies on SharedPreferences as well.
如果您有一个依赖 SharedPreferences 的大型应用程序,您可能会出现密钥重复,尤其是在使用某些依赖 SharedPreferences 的第三方库的情况下。 It seems to me that the better call to use would be:
在我看来,更好的使用调用是:
Context.getSharedPreferences(String name, int mode)
This way if you have a class that heavily relies on SharedPreferences you can create a preference file that is used only by your class.这样,如果您有一个严重依赖 SharedPreferences 的类,您可以创建一个仅由您的类使用的首选项文件。 You could use the fully qualified name of the class to ensure that the file will most likely not be duplicated by someone else.
您可以使用类的完全限定名称来确保文件很可能不会被其他人复制。
Also based on this SO question: Should accessing SharedPreferences be done off the UI Thread?同样基于这个问题: 是否应该在 UI 线程外访问 SharedPreferences? , it seems that accesses SharedPreferences should be done off the UI thread which makes sense.
,似乎访问 SharedPreferences 应该在有意义的 UI 线程之外完成。
Are there any other best practices Android developers should be aware of when using SharedPreferences in their applications?在应用程序中使用 SharedPreferences 时,Android 开发人员是否应该注意其他最佳实践?
I've wrote a little article that can also be found here .我写了一篇小文章,也可以在这里找到。 It describes what
SharedPreferences
is :它描述了
SharedPreferences
是什么:
Android provides many ways of storing application data. Android 提供了多种存储应用程序数据的方式。 One of those ways leads us to the SharedPreferences object which is used to store private primitive data in key-value pairs.
其中一种方法将我们引向SharedPreferences对象,该对象用于在键值对中存储私有原始数据。
All logic are based only on three simple classes:所有逻辑仅基于三个简单的类:
SharedPreferences
is main of them. SharedPreferences
是其中的主要内容。 It's responsible for getting (parsing) stored data, provides interface for getting Editor
object and interfaces for adding and removing OnSharedPreferenceChangeListener
它负责获取(解析)存储的数据,提供获取
Editor
对象的接口和添加和删除OnSharedPreferenceChangeListener
接口
SharedPreferences
you will need Context
object (can be an application Context
)SharedPreferences
您将需要Context
对象(可以是应用程序Context
)getSharedPreferences
method parses Preference file and creates Map
object for it getSharedPreferences
方法解析 Preference 文件并为其创建Map
对象You can create it in few modes provided by Context.您可以通过 Context 提供的几种模式创建它。 You should always use MODE_PRIVATE, as all the other modes are deprecated since API level 17.
您应该始终使用 MODE_PRIVATE,因为自 API 级别 17 以来所有其他模式都已弃用。
// 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
is an Interface used for modifying values in a SharedPreferences
object. SharedPreferences.Editor
是一个接口,用于修改SharedPreferences
对象中的值。 All changes you make in an editor are batched, and not copied back to the original SharedPreferences
until you call commit() or apply()您在编辑器中所做的所有更改都是批处理的,并且在您调用 commit() 或 apply() 之前不会复制回原始
SharedPreferences
Editor
Editor
放置值commit()
or asynchronous with apply
which is faster.commit()
同步保存值或使用更快的apply
异步保存值。 In fact of using different threads using commit()
is safer.commit()
使用不同的线程更安全。 Thats why I prefer to use commit()
.commit()
。 Remove single value with remove()
or clear all values with clear()
使用
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
is a Singleton object so you can easily get as many references as you want, it opens file only when you call getSharedPreferences
first time, or create only one reference for it. SharedPreferences
是一个Singleton对象,因此您可以轻松获取任意数量的引用,它仅在您第一次调用getSharedPreferences
时打开文件,或者仅为它创建一个引用。
// 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
As SharedPreferences
is a Singleton object you can change any of It's instances and not be scared that their data will be different由于
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
Remember the larger the Preference object is the longer get
, commit
, apply
, remove
and clear
operations will be.请记住, Preference 对象越大,
get
、 commit
、 apply
、 remove
和clear
操作的时间就越长。 So it's highly recommended to separate your data in different small objects.因此强烈建议将您的数据分离到不同的小对象中。
Your Preferences will not be removed after Application update.应用程序更新后,您的首选项不会被删除。 So there are cases when you need to create some migration scheme.
因此,有些情况下您需要创建一些迁移方案。 For example you have Application that parse local JSON in start of application, to do this only after first start you decided to save boolean flag
wasLocalDataLoaded
.例如,您有在应用程序启动时解析本地 JSON 的应用程序,只有在第一次启动后才决定保存布尔标志
wasLocalDataLoaded
。 After some time you updated that JSON and released new application version.一段时间后,您更新了该 JSON 并发布了新的应用程序版本。 Users will update their applications but they will not load new JSON because they already done it in first application version.
用户将更新他们的应用程序,但他们不会加载新的 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
are stored in an xml file in the app data folder 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
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();
}
}
If you have a large application that is relying on SharedPreferences you could have key duplication, especially in the case of using some third party library that relies on SharedPreferences as well.
如果您有一个依赖 SharedPreferences 的大型应用程序,您可能会出现密钥重复,尤其是在使用某些依赖 SharedPreferences 的第三方库的情况下。
Libraries should not use that particular SharedPreferences
.图书馆不应该使用那个特定的
SharedPreferences
。 The default SharedPreferences
should only be used by the application.默认的
SharedPreferences
应该只由应用程序使用。
This way if you have a class that heavily relies on SharedPreferences you can create a preference file that is used only by your class.
这样,如果您有一个严重依赖 SharedPreferences 的类,您可以创建一个仅由您的类使用的首选项文件。
You are certainly welcome to do this.当然欢迎您这样做。 I wouldn't, at the application level, as the primary reason for
SharedPreferences
is to have them be shared among the components in the application.在应用程序级别,我不会,因为
SharedPreferences
主要原因是让它们在应用程序中的组件之间共享。 A development team should have no problem managing this namespace, just as they should have no problem managing names of classes, packages, resources, or other project-level stuff.开发团队管理这个命名空间应该没有问题,就像他们管理类、包、资源或其他项目级别的东西的名称应该没有问题一样。 Moreover, the default
SharedPreferences
are what your PreferenceActivity
will use.此外,默认的
SharedPreferences
是您的PreferenceActivity
将使用的。
However, going back to your libraries point, reusable libraries should use a separate SharedPreferences
for their library only.但是,回到您的库点,可重用库应仅对其库使用单独的
SharedPreferences
。 I would not base it on a class name, because then you are one refactoring away from breaking your app.我不会将它基于类名,因为那样你就可以通过重构来破坏你的应用程序。 Instead, pick a name that is unique (eg, based on the library name, such as
"com.commonsware.cwac.wakeful.WakefulIntentService"
) but stable.相反,选择一个唯一的名称(例如,基于库名称,例如
"com.commonsware.cwac.wakeful.WakefulIntentService"
)但稳定。
it seems that accesses SharedPreferences should be done off the UI thread which makes sense.
似乎访问 SharedPreferences 应该在 UI 线程之外完成,这是有道理的。
Ideally, yes.理想情况下,是的。 I recently released a
SharedPreferencesLoader
that helps with this.我最近发布了一个
SharedPreferencesLoader
来帮助解决这个问题。
Are there any other best practices Android developers should be aware of when using SharedPreferences in their applications?
在应用程序中使用 SharedPreferences 时,Android 开发人员是否应该注意其他最佳实践?
Don't over-rely upon them.不要过分依赖它们。 They are stored in XML files and are not transactional.
它们存储在 XML 文件中并且不是事务性的。 A database should be your primary data store, particularly for data you really don't want to lose.
数据库应该是您的主要数据存储,尤其是对于您真的不想丢失的数据。
In kotlin, use of SharedPreferences
can be simplified in the following way.在 kotlin 中,可以通过以下方式简化
SharedPreferences
使用。
class Prefs(context: Context) {
companion object {
private const val PREFS_FILENAME = "app_prefs"
private const val KEY_MY_STRING = "my_string"
private const val KEY_MY_BOOLEAN = "my_boolean"
private const val KEY_MY_ARRAY = "string_array"
}
private val sharedPrefs: SharedPreferences =
context.getSharedPreferences(PREFS_FILENAME, Context.MODE_PRIVATE)
var myString: String
get() = sharedPrefs.getString(KEY_MY_STRING, "") ?: ""
set(value) = sharedPrefs.edit { putString(KEY_MY_STRING, value) }
var myBoolean: Boolean
get() = sharedPrefs.getBoolean(KEY_MY_BOOLEAN, false)
set(value) = sharedPrefs.edit { putBoolean(KEY_MY_BOOLEAN, value) }
var myStringArray: Array<String>
get() = sharedPrefs.getStringSet(KEY_MY_ARRAY, emptySet())?.toTypedArray()
?: emptyArray()
set(value) = sharedPrefs.edit { putStringSet(KEY_MY_ARRAY, value.toSet()) }
Here,
sharedPrefs.edit{...}
is provided by the android core ktx library and should be implemented by adding dependencyimplementation "androidx.core:core-ktx:1.0.2"
in appliation levelbuild.gradle
.这里,
sharedPrefs.edit{...}
由android core ktx 库提供,应该通过在应用层build.gradle
添加依赖implementation "androidx.core:core-ktx:1.0.2"
来build.gradle
。
You can get the instance of SharedPreferences
by using code:您可以使用代码获取
SharedPreferences
的实例:
val prefs = Prefs(context)
Furthermore, you can create the Singleton
object of Prefs
and use from anywhere within the app.此外,您可以创建
Prefs
的Singleton
对象并在应用程序内的任何位置使用。
val prefs: Prefs by lazy {
Prefs(App.instance)
}
where, App
extends Application
and should be included in AndroidManifest.xml
其中,
App
扩展了Application
并且应该包含在AndroidManifest.xml
App.kt应用程序
class App:Application() {
companion object {
lateinit var instance: App
}
override fun onCreate() {
super.onCreate()
instance = this
}
}
AndroidManifest.xml AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest .....
<application
android:name=".App"
....
Example Usage:示例用法:
// get stored value
val myString = prefs.myString
// store value
prefs.myString = "My String Value"
// get stored array
val myStringArray = prefs.myStringArray
// store array
prefs.myStringArray = arrayOf("String 1","String 2","String 3")
This is my way这是我的方式
for write写
SharedPreferences settings = context.getSharedPreferences("prefs", 0);
SharedPreferences.Editor editore = settings.edit();
editore.putString("key", "some value");
editore.apply();
to read阅读
SharedPreferences settings = getSharedPreferences("prefs", 0);
Strings value = settings.getString("key", "");
Let's assume in a project, with multiple developers working on it, they are defining SharedPreference within an Activity like this:让我们假设在一个项目中,有多个开发人员在处理它,他们在一个 Activity 中定义 SharedPreference,如下所示:
SharedPreferences sharedPref = context.getSharedPreferences("prefName", 0);
At one point or another two developers can define the SharedPreference with the same name or insert equivalent Key - Value pairs, which will lead to problems in using the keys.在某一时刻或另外两个开发人员可以定义同名的 SharedPreference 或插入等效的 Key-Value 对,这将导致使用键时出现问题。
The solution relies on two options, whether to use;解决方案依赖两个选项,是否使用;
SharedPreferences Singleton that uses String keys.使用字符串键的 SharedPreferences 单例。
SharedPreferences Singleton that uses Enum keys.使用枚举键的 SharedPreferences 单例。
Personally and According to this Sharepreference Documentation , I prefer to use Enum keys as it enforces stricter control when there are multiple programmers working on a project.个人和根据这个Sharepreference 文档,我更喜欢使用 Enum 键,因为当有多个程序员在一个项目上工作时,它会强制执行更严格的控制。 A programmer has no choice but to declare a new key in the appropriate enum class and so all the keys are in the same place.
程序员别无选择,只能在适当的枚举类中声明一个新键,因此所有键都在同一个地方。
And to avoid boilerplate code writing create the SharedPreference singleton.为了避免编写样板代码,请创建 SharedPreference 单例。 This SharedPreferences singleton Class help to centralize and simplify reading and writing of SharedPreferences in your Android app.
此SharedPreferences 单例类有助于集中和简化 Android 应用程序中 SharedPreferences 的读取和写入。
The source code for the two provided solutions can be found in GitHub提供的两个解决方案的源代码可以在GitHub中找到
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.