Kotlin for Android (四)

Application单例化和属性的Delegated

需要有一个更简单的访问application context的方式

Application单例化
1
2
3
4
5
6
7
8
9
10
class App : Application() {
companion object {
private var instance: Application? = null
fun instance() = instance!!
}
override fun onCreate() {
super.onCreate()
instance = this
}
}

Android有一个问题,就是我们不能去控制很多类的构造函数。比如,我们不能初
始化一个非null属性,因为它的值需要在构造函数中去定义。所以我们需要一个可
null的变量,和一个返回非null值的函数。我们知道我们一直都有一个 App 实例,
但是在它调用 onCreate 之前我们不能去操作任何事情,所以我们为了安全性,我
们假设 instance() 函数将会总是返回一个非null的 app 实例。但是这个方案看起来有点不自然。我们需要定义个一个属性(已经有了getter和setter),然后通过一个函数来返回那个属性。我们有其他方法去达到相似的效果么?是的,我们可以通过委托这个属性的值给另外一个类。这个就是我们知道的委托属性

委托属性
  • 属性委托的结构如下:这个T是委托属性的类型。 getValue 函数接收一个类的引用和一个属性的元数据。 setValue 函数又接收了一个被设置的值。如果这个属性是不可修改(val),就会只有一个 getValue 函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Delegate<T> : ReadWriteProperty<Any?, T> {
    fun getValue(thisRef: Any?, property: KProperty<*>): T {
    return ...
    }
    fun setValue(thisRef: Any?, property: KProperty<*>, value: T)
    {
    ...
    }
    }
  • 示例属性委托怎么设置,by 这个关键字来指定一个委托对象

    1
    2
    3
    class Example {
    var p: String by Delegate()
    }
标准委托
  • Lazy
    它包含一个lambda,当第一次执行 getValue 的时候这个lambda会被调用,所以
    这个属性可以被延迟初始化。之后的调用都只会返回同一个值,当我们在它们第一次真正调用之前不是必须需要它们的时候。我们可以节省内存,在这些属性真正需要前不进行初始化,database并没有被真正初始化,直到第一次调用 onCreate 时。在那之后,我们才确保applicationContext存在,并且已经准备好可以被使用了。 lazy 操作符是线程安全的。如果你不担心多线程问题或者想提高更多的性能,你也可以使用lazy(LazyThreadSafeMode.NONE){ … }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class App : Application() {
    val database: SQLiteOpenHelper by lazy {
    MyDatabaseHelper(applicationContext)
    }
    override fun onCreate() {
    super.onCreate()
    val db = database.writableDatabase
    }
    }
  • Observable这个委托会帮我们监测我们希望观察的属性的变化。当被观察属性的 set 方法被调用的时候,它就会自动执行我们指定的lambda表达式。所以一旦该属性被赋了新的值,我们就会接收到被委托的属性、旧值和新值,下面的示例是一些我们需要关心的ViewMode,每次值被修改了,就会保存它们到数据库

    1
    2
    3
    4
    5
    6
    class ViewModel(val db: MyDatabase) {
    var myProperty by Delegates.observable("") {
    d, old, new ->
    db.saveChanges(this, new)
    }
    }
  • Vetoable 这是一个特殊的 observable ,它让你决定是否这个值需要被保存。它可以被用于在真正保存之前进行一些条件判断,下面的示例,允许在新的值是正数的时候执行保存。在lambda中,最后一行表示返回值。你不需要使用return关键字(实质上不能被编译)

    1
    2
    3
    4
    var positiveNumber = Delegates.vetoable(0) {
    d, old, new ->
    new >= 0
    }
  • NotNull 含有一个可null的变量并会在我们设置这个属性的时候分配一个真实的值。如果这个值在被获取之前没有被分配,它就会抛出一个异常

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class App : Application() {
    companion object {
    var instance: App by Delegates.notNull()
    }
    override fun onCreate() {
    super.onCreate()
    instance = this
    }
    }
  • 从Map中映射值 属性的值会从一个map中获取value,属性的名字对应这个map中的key,如果我们importkotlin.properties.getValue ,我们可以从构造函数映射到 val 属性来得到一个不可修改的map。如果我们想去修改map和属性,我们也可以import kotlin.properties.setValue 。类需要一个 MutableMap 作为构造函数的参数

  • 想象我们从一个Json中加载了一个配置类,然后分配它们的key和value到一个map中。我们可以仅仅通过传入一个map的构造函数来创建一个实例:

    1
    2
    3
    4
    5
    6
    7
    import kotlin.properties.getValue
    class Configuration(map: Map<String, Any?>) {
    val width: Int by map
    val height: Int by map
    val dp: Int by map
    val deviceName: String by map
    }
  • 创建一个map

    1
    2
    3
    4
    5
    6
    conf = Configuration(mapOf(
    "width" to 1080,
    "height" to 720,
    "dp" to 240,
    "deviceName" to "mydevice"
    ))
  • 自定义委托 创建一个类然后继承 ReadWriteProperty :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    private class NotNullSingleValueVar<T>() : ReadWriteProperty<Any
    ?, T> {
    override fun getValue(thisRef: Any?, property: KProperty
    <*>): T {
    throw UnsupportedOperationException()
    }
    override fun setValue(thisRef: Any?, property: KProperty
    <*>, value: T) {
    }
    }
  • 这个委托可以作用在任何非null的类型。它接收任何类型的引用,然后像getter和setter那样使用T。现在我们需要去实现这些函数

    Getter函数 如果已经被初始化,则会返回一个值,否则会抛异常
    Setter函数 如果仍然是null,则赋值,否则会抛异常

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    private class NotNullSingleValueVar<T>() : ReadWriteProperty<Any
    ?, T> {
    private var value: T? = null
    override fun getValue(thisRef: Any?, property: KProperty<*>)
    : T {
    return value ?: throw IllegalStateException("${desc.name
    } " +
    "not initialized")
    }
    override fun setValue(thisRef: Any?, property: KProperty<*>,
    value: T) {
    this.value = if (this.value == null) value
    else throw IllegalStateException("${desc.name} already i
    nitialized")
    }
    }

    使用上述委托示例

    1
    2
    3
    4
    object DelegatesExt {
    fun notNullSingleValue<T>():
    ReadWriteProperty<Any?, T> = NotNullSingleValueVar()
    }
  • 重新实现Application单例化,可以在app的任何地方去修改这个值,因为如果我们使用 Delegates.notNull() ,属性必须是var的

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class App : Application() {
    companion object {
    var instance: App by Delegates.notNull()
    }
    override fun onCreate() {
    super.onCreate()
    instance = this
    }
    }

    上述可以任何地方修改问题,解决办法使用下面的委托,只能修改一次
    companion object {
    var instance: App by DelegatesExt.notNullSingleValue()
    }
willkernel wechat
关注微信公众号
帅哥美女们,请赐予我力量吧!