Kotlin get set函数

拆解下面这个set函数

   @Volatile
    private var mScreenActionIsWakeUp = true
        set(value) {
            val lastWakeup = field
            field = value
            mScreenFadeAnimator.animateTo(lastWakeup, value)
        }

逐层拆解:


  1. @Volatile — 可见性保证

等价于 Java 的 volatile 关键字:

  • 保证多线程间对该变量的读写可见性(禁止 CPU 缓存/指令重排优化)
  • 这里因为主线程写、ScreenFade 线程读,需要跨线程可见
  1. private var mScreenActionIsWakeUp = true — 属性声明

  1. set(value) { … } — 自定义 setter

这是 Kotlin 属性访问器(Property Accessor) 语法。Kotlin 的 var 属性本质上会生成:

  • 一个 backing field(幕后字段)
  • 一个 get() 方法(这里用默认的)
  • 一个 set() 方法(这里自定义了)

任何对该属性的赋值操作都会走这个 setter,而不是直接写字段。

  1. field — 幕后字段关键字

field 是 Kotlin 特有的关键字,只能在 getter/setter 内部使用,代表属性的实际存储值。

 set(value) {
      val lastWakeup = field   // ① 读取当前旧值(赋值前的值)
      field = value            // ② 将新值写入幕后字段
      mScreenFadeAnimator.animateTo(lastWakeup, value)  // ③ 用旧值和新值触发动画
  }

为什么不能写 this.mScreenActionIsWakeUp 代替 field?因为那会递归调用 setter 自身,导致栈溢出。field 是直接操作底层字段的唯一方式。

  1. 等价的 Java 代码

 @Volatile
  private volatile boolean mScreenActionIsWakeUp = true;
 
  // 编译器不会真的生成这个方法,但语义等价:
  private void setMScreenActionIsWakeUp(boolean value) {
      boolean lastWakeup = this.mScreenActionIsWakeUp;  // field → 直接读字段
      this.mScreenActionIsWakeUp = value;                // field = value → 直接写字段
      mScreenFadeAnimator.animateTo(lastWakeup, value);
  }
  1. 触发时机

在 updateScreenFadeState 中:

  private fun updateScreenFadeState(wakeup: Boolean) {
      mScreenFadeHandler?.post {
          mScreenActionIsWakeUp = wakeup  // ← 赋值就会触发自定义 setter
      }
  }

这行赋值不是简单的写字段,而是执行了:

  1. 保存旧状态(上一次是亮屏还是灭屏)
  2. 更新为新状态
  3. 调用 animateTo(旧状态, 新状态) 启动弹簧动画

设计意图

把状态变更和动画触发绑定在一起,确保只要状态改变就一定会启动动画,不可能出现”改了状态忘了触发动画”的情况。这 是一种观察者模式的轻量实现——属性自身就是被观察的对象。