访问Shared Preferences
你可能知道什么是Android Shared Preferences。可以通过Android框架简单存储的一系列key和value对。这些preferences与SDK的一部分融为一体,使得任务变得更加容易。而且从Android 6.0(Marshmallow),shared preferences可以自动被云存储,所以当一个用户在一个新的设备上面恢复App的时候,它们的preferences也会被恢复。
多亏使用了属性委托,我们可以使用非常简单的方式来处理preferences。我们可以创建一个委托,当get被调用时去查询,当set被调用时去执行保存操作。
因为我们想去保存zip code,它是一个long型,所以让我们创建一个Long属性的委托吧。在DelegatesExtensions.kt中,实现一个新的LongPreference类:
class LongPreference(val context: Context, val name: String, val default: Long)
: ReadWriteProperty<Any?, Long> {
val prefs by lazy {
context.getSharedPreferences("default", Context.MODE_PRIVATE)
}
override fun getValue(thisRef: Any?, property: KProperty<*>): Long {
return prefs.getLong(name, default)
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Long) {
prefs.edit().putLong(name, value).apply()
}
}
首先,我们使用lazy委托的方式创建一个preferences。这样的话,如果我们没有使用这个属性,这个委托就不会请求这个SharedPreferences对象。
当get被调用,它的实现是使用preferences实例去获取一个委托声明中指定名字的long属性,如果没有找到这个属性,则默认使用default。当一个值被set,拿到preferences editor并使用属性名保存。
我们可以在DelegatesExt中定义一个新的委托,这样我们访问时就简单很多:
object DelegatesExt {
....
fun longPreference(context: Context, name: String, default: Long) =
LongPreference(context, name, default)
}
在SettingActivity,现在可以定义一个属性去处理zip code偏好。我创建了两个常量用来作为名字和属性的默认值。这种方式可以在App其他地方使用:
companion object {
val ZIP_CODE = "zipCode"
val DEFAULT_ZIP = 94043L
}
var zipCode: Long by DelegatesExt.longPreference(this, ZIP_CODE, DEFAULT_ZIP)
现在preference工作起来就非常简单了,我们可以从属性中得到并赋值给EditText:
override fun onCreate(savedInstanceState: Bundle?) {
...
cityCode.setText(zipCode.toString())
}
我们不能使用自动生成的属性text,因为EditText在getText中返回的是Editable,所以该属性默认为该值。如果我尝试去分配一个String,编译器会报错,使用setText()就足够了。
现在具备了所有要实现onBackPressed的东西。这里,一个属性的新值会被储存:
override fun onBackPressed() {
super.onBackPressed()
zipCode = cityCode.text.toString().toLong()
}
MainActivity需要一些小的改变。首先,它也需要一个zip code属性。
val zipCode: Long by DelegatesExt.longPreference(this, SettingsActivity.ZIP_CODE,
SettingsActivity.DEFAULT_ZIP)
然后,我把forecast load的代码移动到了onResume,这样每次activity resumed,它都会刷新数据,以防code zip被修改。当然这里有更加复杂一点的方式去做,比如通过在请求forecast之前检查是否zip code真的改变了。但是我像保持这个例子的简单性,而且因为请求的数据已经保存在本地数据库中了,所以这个解决方案也不算太坏:
override fun onResume() {
super.onResume()
loadForecast()
}
private fun loadForecast() = async {
val result = RequestForecastCommand(zipCode).execute()
uiThread {
val adapter = ForecastListAdapter(result) {
startActivity<DetailActivity>(DetailActivity.ID to it.id,
DetailActivity.CITY_NAME to result.city)
}
forecastList.adapter = adapter
toolbarTitle = "${result.city} (${result.country})"
}
}
RequestForecastCommand现在使用zipCode而不是之前的是一个固定值。
这里还有意见我们必须要做的事情:当溢出菜单的settings点击时启动这个setting activity。在ToolbarManager中的initToolbar函数需要有一些小的修改:
when (it.itemId) {
R.id.action_settings -> toolbar.ctx.startActivity<SettingsActivity>()
else -> App.instance.toast("Unknown option")
}