首发于公众号: DSGtalk1989
我们直接使用最最契合的Kotlin
功能点,属性代理来实现
首先我们按照文中所说,先去实现ReadWriteProperty
接口。
其中有一个细节点需要注意,我们在属性委托这一章中没有明确的指出ReadWriteProperty
两个泛型参数的意义。
/**
* Base interface that can be used for implementing property
delegates of read-write properties.
*
* This is provided only for convenience; you don't have to extend this
interface
* as long as your property delegate has methods with the same
signatures.
*
* @param R the type of object which owns the delegated property.
* @param T the type of the property value.
*/
public interface ReadWriteProperty<in R, T>
有句话比较重要
This is provided only for convenience
可见只是为了方便,一旦你在内部有了相同的方法就不一定要去实现这个接口。。
OK,我们来看两个泛型参数的解释,R代表持有代理属性的对象类型,T代表代理属性的类型,我们再结合着两个接口的方法来看就很清晰了。
/**
* Returns the value of the property for the given object.
* @param thisRef the object for which the value is requested.
* @param property the metadata for the property.
* @return the property value.
*/
public operator fun getValue(thisRef: R, property: KProperty<*>): T
/**
* Sets the value of the property for the given object.
* @param thisRef the object for which the value is requested.
* @param property the metadata for the property.
* @param value the value to set.
*/
public operator fun setValue(thisRef: R, property: KProperty<*>, value: T)
总结一下,property
是我们无法改变的, 我们可以修改的就是R和T,那么为了使这个属性代理面向的受众更广泛,我们允许任何的对象去持有这个代理属性,允许代理之后生成任何类型的对象。所以我们看到的代理属性玩法一般是实现接口ReadWriteProperty<Any?,T>
。
有些同学会问,前面的用Any?
不用R
,后面的用T
不用Any?
,这里涉及到泛型的使用体会,什么时候使用泛型什么时候使用Any?
或者java中的Object
。其实就看我们最终是否需要去使用泛型。
也就说我们并不需要直接把R
拿来用,但是我们需要把T
拿来用,试想一下,我们T
改成Any?
,完全没有问题,但是代理的属性瞬间就变成了Any?
,如果我们需要的是Stirng
或者是Int
,就还需要做一层as
强转。
OK,此处不再做展开,相信大家对属性代理的两个方法应该有了更深的见解。
好的,所以我们的SharePreferenceDelegate
变成了如下这样。
class SharePreferenceDelegate<T>(private val key: String, private val defaultValue: T) : ReadWriteProperty<Any?, T>{
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
}
override fun setValue(thisRef: Any?, property: KProperty<*>, defaultValue: T) {
}
}
构造函数的两个参数还是很明显的,跟我们以前玩儿的SharePreference工具类差不多,都是传入一个key
和默认的defaultValue
。
至此属性代理的一步完成了。也就是说接下去我们针对代理的属性,它的每一次的赋值和每一次的取值都会去走代理的方法setValue
和getValue
按照我们以往的理解,在取值时,我们需要从SP中将值取到,然后返回出去。在赋值时,我们需要将值存到SP中。
所以方法应该是
@Suppress("UNCHECKED_CAST")
override operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
val prefs = FrameApplication.instance.getSharedPreferences(SpConst.DEFAULT_PKG, MODE_PRIVATE)
val result : Any = when (defaultValue){
is Long -> prefs.getLong(key, defaultValue)
is String -> prefs.getString(key, defaultValue)
is Int -> prefs.getInt(key, defaultValue)
is Boolean -> prefs.getBoolean(key, defaultValue)
is Float -> prefs.getFloat(key, defaultValue)
else -> throw IllegalArgumentException("Type Error, cannot be got!")
}
return result as T
}
override operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
val prefs = FrameApplication.instance.getSharedPreferences(SpConst.DEFAULT_PKG, MODE_PRIVATE)
when (defaultValue){
is Long -> prefs.edit().putLong(key, defaultValue).apply()
is String -> prefs.edit().putString(key, defaultValue).apply()
is Int -> prefs.edit().putInt(key, defaultValue).apply()
is Boolean -> prefs.edit().putBoolean(key, defaultValue).apply()
is Float -> prefs.edit().putFloat(key, defaultValue).apply()
else -> throw IllegalArgumentException("Type Error, cannot be saved!")
}
}
这里有一些优化的点,比如我们可以把方法进行抽离,显得不那么臃肿;都用的prefs
是否需要每次都去初始化;什么时候用apply
什么时候用commit
。
所以最终我们优化过后的SP
工具类是这样的
class SharePreferenceDelegate<T>(
private val key: String,
private val defaultValue: T,
private val useCommit: Boolean = false
) : ReadWriteProperty<Any?, T> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
return getSharePreferences(key, defaultValue)
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
putSharePreferences(key, value)
}
//延迟属性,只会加载一次
private val prefs: SharedPreferences by lazy {
FrameApplication.instance.getSharedPreferences(
SpConst.DEFAULT_PKG,
Context.MODE_PRIVATE
)
}
@SuppressLint("ApplySharedPref")
private fun putSharePreferences(name: String, value: T) = with(prefs.edit()) {
val editor = when (value) {
is Long -> putLong(name, value)
is String -> putString(name, value)
is Int -> putInt(name, value)
is Boolean -> putBoolean(name, value)
is Float -> putFloat(name, value)
else -> throw IllegalArgumentException("Type Error, cannot be saved!")
}
if (useCommit) editor.commit() else editor.apply()
}
@Suppress("UNCHECKED_CAST")
private fun getSharePreferences(name: String, default: T): T = with(prefs) {
val res: Any = when (default) {
is Long -> getLong(name, default)
is String -> getString(name, default)
is Int -> getInt(name, default)
is Boolean -> getBoolean(name, default)
is Float -> getFloat(name, default)
else -> throw IllegalArgumentException("Type Error, cannot be got!")
}
return res as T
}
}
当我们需要使用SP
的时候,只需要像一般调用一样取值和赋值就可以了。
var a by SharePreferenceDelegate("111", "222")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
a = "2"
}
轻松加愉快!
多唠叨一句,这里有个注意点,我们看到在prefs
的初始化中使用到了application的单例FrameApplication.instance
。来看下具体的代码
class FrameApplication : Application() {
companion object {
var instance: FrameApplication by Delegates.notNull()
}
override fun onCreate() {
super.onCreate()
instance = this
}
}
这里有个Delegates.notNull()
,我们看下注释。
/**
* Returns a property delegate for a read/write property with a non-`null` value that is initialized not during
* object construction time but at a later time. Trying to read the property before the initial value has been
* assigned results in an exception.
*/
做一个属性代理,代理的约束是必须先初始化,才能拿来使用。有点像lateinit var
。从他的代码中也很明显可以看得出来。
private class NotNullVar<T : Any>() : ReadWriteProperty<Any?, T> {
private var value: T? = null
public override fun getValue(thisRef: Any?, property: KProperty<*>): T {
return value ?: throw IllegalStateException("Property ${property.name} should be initialized before get.")
}
public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
this.value = value
}
}
白话一点,我们可以直接把java中的代码
private static FrameApplication instance;
转化成
companion object {
var instance: FrameApplication by Delegates.notNull()
}
以上。