简体   繁体   English

Android SharedPreferences 最佳实践

[英]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是什么:

Best Practice: 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共享首选项

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接口

  • To create 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) { } };

Editor编辑

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

  • Use simple interface to put values in Editor使用简单的界面在Editor放置值
  • Save values synchronous with 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();

Performance & Tips性能和技巧

  • 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 对象越大, getcommitapplyremoveclear操作的时间就越长。 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

Android guide. 安卓指南。

Sample Code示例代码

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 dependency implementation "androidx.core:core-ktx:1.0.2" in appliation level build.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.此外,您可以创建PrefsSingleton对象并在应用程序内的任何位置使用。

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;解决方案依赖两个选项,是否使用;

  1. SharedPreferences Singleton that uses String keys.使用字符串键的 SharedPreferences 单例。

  2. 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM