SystemClock系统时钟
SystemClock类是一个用于获取时间信息的核心类,为Android开发者提供了多种与时间相关的功能。
- 「类声明和结构」:
SystemClock是一个final类,继承自Object,不能被继承。
不可变类,提供了与时间相关的多种方法。
- 「主要方法」:
SystemClock.uptimeMillis():
返回从系统启动开始到现在的非休眠时间的毫秒数。
当系统进入深度睡眠(CPU休眠、屏幕休眠、设备等待外部输入)时,该时间停止。
SystemClock.elapsedRealtime() 和 SystemClock.elapsedRealtimeNanos():
返回从系统启动到现在的总时间,包括深度睡眠的时间。
两个方法返回的时间都是单调的,即使在省电模式下也会继续计时。
SystemClock.sleep(long):
类似于Thread.sleep(long),忽略了InterruptedException异常。
SystemClock.setCurrentTimeMillis(long):
「注意」:通常用于设置系统时间,需要系统权限。
将系统时间设置为指定的毫秒数,通常只在系统源码环境下使用,并且需要特定的编译和签名过程。
- 「其他注意事项」:
监听时间变化:
通过监听ACTION_TIME_TICK、ACTION_TIME_CHANGED和ACTION_TIMEZONE_CHANGED等广播,可以获取系统时间是否发生改变。
- 「权限要求」:
修改系统时间(如使用SystemClock.setCurrentTimeMillis(long))需要系统权限,应用需要在Android系统源码环境下进行编译和签名。
Hander延时消息延时会不准确
Hanler中发送消息(或者延时消息) 是通过SystemClock.uptimeMillis()为基准计算。
MessageQueue入队列时,计算Message.when以SystemClock.uptimeMillis()时间为基准。
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
从MessageQueue中消费Message,判断Message是否到了执行时间,也是以SystemClock.uptimeMillis()为基准。
//Looper.loop()循环
public static void loop() {
for (;;) {
Message msg = queue.next(); // might block
msg.target.dispatchMessage(msg);
}
}
//MessageQueue类的next方法
//msg.when > now时 才会将Message取出然后执行。
Message next() {
for (;;) {
...
synchronized (this) {
// (1)计算当前时间
final long now = SystemClock.uptimeMillis();
// 返回取到的Message
if (msg != null) {
//msg尚未到达触发时间,则计算新的阻塞超时时间nextPollTimeoutMillis,下次循环触发队列阻塞
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//从链表中移除该消息后,直接返回
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();
return msg;
}
}
...
}
}
SystemClock.uptimeMillis()计算系统从开机到现在的时间,单位是毫秒。但是它不包括系统休眠的时间(cpu休眠、屏幕休眠等)。
当手机灭屏处于休眠状态的时间是不计算进SystemClock.uptimeMillis(),比如发送一个延时20分钟的Message消息,系统息屏后进入了深度睡眠(假设深度睡眠了1个小时),当进程苏醒后,这一个小时的时间是不计入(1)中的now。
final long now = SystemClock.uptimeMillis();
从系统时钟看已经过去1个小时了,但是计算now时因为uptimeMillis不包含休眠时的时间。如果now < msg.when 会判定Messsage还没有到执行时间,就不会从MessageQueue中取出并执行。