Android - SharedPreferences - Context

5
我想用Kotlin为我的Android应用程序中的SharedPreference创建一个辅助类。不幸的是,我需要Context,但我不想每次调用首选项时都将其设置为参数。
如果我使用伴生对象来保存Context,并在应用程序启动时设置它,我会收到以下错误:不要将Android上下文类放入静态字段中;这会导致内存泄漏(并且也会破坏Instant Run)。
那么如何在不传递参数的情况下获取Context以调用首选项呢?
 var isWorking: Boolean
    get() = getBoolean(IS_WORKING)
    set(isWorking) = setPreference(IS_WORKING, isWorking)

 private fun setPreference(key: String, value: Boolean) {
    val editor = settings.edit()
    editor.putBoolean(key, value)
    editor.commit()
}

 private val settings: SharedPreferences by lazy {
    context.getSharedPreferences("prefs", Context.MODE_PRIVATE)
}

3
这并不回答你的问题,但你应该每次传递上下文。正如消息所说,如果操作在活动完成时仍在运行,则可能会泄漏活动。根据我的经验,我总是看到当进行某些操作时,PreferenceHelper类会接收一个上下文。 - Iulian Popescu
请像我在这里所做的那样使用Kotlin扩展。链接 - Kosrat D. Ahmad
3个回答

9

不要使用类,考虑使用对象。它只会有一个实例,可以用应用程序的上下文来初始化SharedPreferences,像这样:

object AppPreferences {
    private const val NAME = "SpinKotlin"
    private const val MODE = Context.MODE_PRIVATE
    private lateinit var preferences: SharedPreferences
    // list of app specific preferences
    private val IS_FIRST_RUN_PREF = Pair("is_first_run", false)

    fun init(context: Context) {
        preferences = context.getSharedPreferences(NAME, MODE)
    }

    /**
    * SharedPreferences extension function, so we won't need to call edit() 
        and apply()
    * ourselves on every SharedPreferences operation.
    */
    private inline fun SharedPreferences.edit(operation: 
        (SharedPreferences.Editor) -> Unit) {
        val editor = edit()
        operation(editor)
        editor.apply()
    }

    var firstRun: Boolean
        // custom getter to get a preference of a desired type, with a predefined default value
        get() = preferences.getBoolean(IS_FIRST_RUN_PREF.first, IS_FIRST_RUN_PREF.second)

        // custom setter to save a preference back to preferences file
        set(value) = preferences.edit {
            it.putBoolean(IS_FIRST_RUN_PREF.first, value)
        }
}

在Application类中:

class SpInKotlinApp : Application() {
    override fun onCreate() {
        super.onCreate()
        AppPreferences.init(this)
    }
}

由于使用具有自定义get()和set()方法的属性来简化读写,因此分配值非常容易:

if (!AppPreferences.firstRun) {
        AppPreferences.firstRun = true
        Log.d("SpinKotlin", "The value of our pref is: ${AppPreferences.firstRun}")
    }

Check a whole explanation here.


如果你将参数更改为 private inline fun SharedPreferences.edit(operation: SharedPreferences.Editor.() -> Unit),那么你就不需要在操作前加上 it - Lifes

6
您可以像下面这样创建一个扩展函数:
object PreferenceHelper {

    fun defaultPrefs(context: Context): SharedPreferences
            = PreferenceManager.getDefaultSharedPreferences(context)

    fun customPrefs(context: Context, name: String): SharedPreferences
            = context.getSharedPreferences(name, Context.MODE_PRIVATE)

    inline fun SharedPreferences.edit(operation: (SharedPreferences.Editor) -> Unit) {
            val editor = this.edit()
            operation(editor)
            editor.apply()
        }
}

编辑: 在这里是此答案的参考。您可以查看如何使用Kotlin技巧重构util类并使用它。

编辑2:

您可以将辅助程序更改为类,并在Application中进行初始化。然后您就可以在任何地方使用它。我想这就是您要做的。让我们开始吧。

class PreferenceHelper constructor(context: Context){

        fun defaultPrefs(): SharedPreferences
                = PreferenceManager.getDefaultSharedPreferences(context)

        fun customPrefs(name: String): SharedPreferences
                = context.getSharedPreferences(name, Context.MODE_PRIVATE)

        inline fun SharedPreferences.edit(operation: (SharedPreferences.Editor) -> Unit) {
                val editor = this.edit()
                operation(editor)
                editor.apply()
            }
    }

在您的Application类中:

class YourApp : Application() {

    override fun onCreate() {
        super.onCreate()
        YourApp.prefHelper = PreferenceHelper(this)
    }

    companion object {
        lateinit var prefHelper: PreferenceHelper
            private set
    }
}

您可以像下面这样在任何地方使用:

YourApp.prefHelper.defaultPrefs().edit {
    // Your shared pref operations.
}

我认为第一个更接近最佳实践,但第二个也可以。你可以使用你需要的那个。此外,在我提供的链接内容中还有更多很棒的例子。


谢谢您的回答。但那并不是我想要的。我认为只是使用构造函数调用我的辅助类没有什么区别。 - Thomas Klammer
1
我编辑了我的回答并尝试实现你想要做的事情。我更喜欢第一个,但第二个也可以。 - savepopulation
MyApp.prefHelper.defaultPrefs().edit{} 给我报错了,无法运行应用程序。 - M.R

-1

我已经找到了解决我的问题的方法。我通过持久化SharedPreferences变量而不是上下文来解决它。

object PreferenceDao {
    private lateinit var settings: SharedPreferences

    fun init(context: Context) {
        settings = context.getSharedPreferences("prefs", Context.MODE_PRIVATE)
    }
}

初始化函数在我的Application类中被调用。


你考虑过使用依赖注入框架吗? - tynn
那是什么?从未听说过。 - Thomas Klammer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接