遵循唤醒锁最佳实践

使用唤醒锁可能会损害设备性能。如果您需要使用唤醒锁,请务必正确使用。本文档介绍了一些最佳实践,可帮助您避免常见的唤醒锁陷阱。

正确命名唤醒锁

我们建议在唤醒锁标签中包含您的软件包、类或方法名称。这样,如果发生错误,您就可以更轻松地在源代码中找到创建唤醒锁的位置。以下是一些其他提示

  • 请勿在名称中包含任何个人身份信息 (PII),例如电子邮件地址。如果设备在唤醒锁标签中检测到 PII,它将记录 _UNKNOWN 而不是您指定的标签。
  • 不要以编程方式获取类或方法名称,例如通过调用 getName()。如果您尝试以编程方式获取名称,它可能会被 Proguard 等工具混淆。请改用硬编码字符串。
  • 不要向唤醒锁标签添加计数器或唯一标识符。创建唤醒锁的代码应在每次运行时使用相同的标签。这种做法使系统能够聚合每个方法的唤醒锁使用情况。

确保您的应用在前台可见

当唤醒锁处于活动状态时,设备正在耗电。设备用户应该知道这一点。因此,如果您正在使用唤醒锁,则应向用户显示一些通知。实际上,这意味着您应该在前台服务中获取并持有唤醒锁。前台服务必须显示通知。

如果前台服务不是您应用的正确选择,您可能也不应使用唤醒锁。有关在您的应用不在前台时执行工作的其他方法,请参阅《选择正确的 API 以保持设备唤醒》文档。

保持逻辑简单

确保获取和释放唤醒锁的逻辑尽可能简单。当您的唤醒锁逻辑与复杂的状态机、超时、执行器池或回调事件绑定时,该逻辑中的任何细微错误都可能导致唤醒锁被持有时间超出预期。这些错误难以诊断和调试。

检查唤醒锁是否始终被释放

如果您使用唤醒锁,则必须确保您获取的每个唤醒锁都正确释放。这并不总是听起来那么容易。例如,以下代码存在问题

Kotlin

@Throws(MyException::class)
fun doSomethingAndRelease() {
    wakeLock.apply {
        acquire()
        doTheWork() // can potentially throw MyException
        release()   // does not run if an exception is thrown
    }
}

Java

void doSomethingAndRelease() throws MyException {
    wakeLock.acquire();
    doTheWork();         // can potentially throw MyException
    wakeLock.release();  // does not run if an exception is thrown
}

这里的问题是 doTheWork() 方法可能会抛出 MyException 异常。如果抛出异常,doSomethingAndRelease() 方法会将异常向外传播,并且永远不会到达 release() 调用。结果是唤醒锁被获取但未释放,这是非常糟糕的。

在修正后的代码中,doSomethingAndRelease() 确保即使抛出异常也会释放唤醒锁

Kotlin

@Throws(MyException::class)
fun doSomethingAndRelease() {
    wakeLock.apply {
        try {
            acquire()
            doTheWork()
        } finally {
            release()
        }
    }
}

Java

void doSomethingAndRelease() throws MyException {
    try {
        wakeLock.acquire();
        doTheWork();
    } finally {
        wakeLock.release();
    }
}