【问题标题】:Android - SharedPreferences - ContextAndroid - SharedPreferences - 上下文
【发布时间】:2018-05-29 10:19:01
【问题描述】:

我想使用 kotlin 在 android 中为我的 SharedPreference 创建一个辅助类。 不幸的是,我需要Context,而且我不想在每次调用首选项时都将其设置为参数。

如果我为上下文使用伴随对象并在应用程序启动时设置它,我会收到以下错误:Do not place Android context classes in static fields; this is a memory leak (and also breaks Instant Run)

那么如何在每次调用首选项时获取上下文而不传递它?

 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)
}

【问题讨论】:

  • 这不能回答你的问题,但你应该每次都传递上下文。正如消息所说,如果活动完成时操作仍在运行,则活动可能会泄漏。根据我的经验,我总是看到 PreferenceHelper 类在进行某些操作时接收上下文。
  • 像我一样使用 Kotlin 扩展 here

标签: android kotlin sharedpreferences android-context


【解决方案1】:

考虑使用对象而不是类。它只有一个实例,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)
        }
}

在应用程序类中:

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}")
    }

查看完整说明here

【讨论】:

  • 如果将参数更改为private inline fun SharedPreferences.edit(operation: SharedPreferences.Editor.() -> Unit),则无需在操作前加上it
【解决方案2】:

您可以创建一个extension function,如下所示:

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()
        }
}

编辑: Here 是此答案的参考。您可以检查如何使用 Kotlin 技巧重构 util classes 并使用它。

编辑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()
            }
    }

在您的应用程序类中:

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

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

【讨论】:

  • 感谢您的回答。但这并不是我真正想要的。在这里我看不出有什么区别,只是用构造函数调用我的辅助类。
  • 我编辑了我的答案并尝试实现您想要做的事情。我更喜欢第一个,但第二个也不错
  • MyApp.prefHelper.defaultPrefs().edit{} 出现错误,无法运行应用程序。
【解决方案3】:

我找到了解决问题的方法。我通过保留 SharedPreferences 变量而不是上下文来解决它。

object PreferenceDao {
    private lateinit var settings: SharedPreferences

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

init 函数在我的 Application 类中被调用。

【讨论】:

  • 你考虑过使用依赖注入框架吗?
  • 那是什么?没听说过。
猜你喜欢
  • 2011-12-14
  • 2017-01-21
  • 2012-07-19
  • 2012-02-24
  • 1970-01-01
  • 2012-07-01
  • 2011-08-22
  • 2015-06-10
  • 2012-12-10
相关资源
最近更新 更多