一、SystemClock 系统时钟类:
SystemClock提供了几种不同的获取时间的方法:
1.1、SystemClock.uptimeMillis 它获取的是系统从开机到现在的时间,单位是毫秒。但是它不包括系统休眠的时间(cpu休眠、屏幕休眠等)
- System.currentTimeMillis()对应取出的是, 标准挂钟时间(日期+时间),从1970年1月1日 UTC到现在的毫秒数 属于绝对时间。
- 使用System.currentTimeMillis存在一定的风险,因为它是以系统时间为基准的,而我们可以通过SystemClock.setCurrentTimeMilis来设置系统时间。因此推荐使用SystemClock.uptimeMillis()
SystemClock.setCurrentTimeMillis()可以修系统时钟.
调用setCurrentTimeMillis 需要申请对应的权限。
/**
* Sets the current wall time, in milliseconds. Requires the calling
* process to have appropriate permissions.
*
* @return if the clock was successfully set to the specified time.
*/
public static boolean setCurrentTimeMillis(long millis) {
final IAlarmManager mgr = IAlarmManager.Stub
.asInterface(ServiceManager.getService(Context.ALARM_SERVICE));
if (mgr == null) {
Slog.e(TAG, "Unable to set RTC: mgr == null");
return false;
}
try {
return mgr.setTime(millis);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to set RTC", e);
} catch (SecurityException e) {
Slog.e(TAG, "Unable to set RTC", e);
}
return false;
}
1.2、SystemClock.elapsedRealtime 它获取的是从开机到现在的时间,单位是毫秒。它包括了系统休眠的时间。
/**
* Returns milliseconds since boot, including time spent in sleep.
*
* @return elapsed milliseconds since boot.
*/
@CriticalNative
native public static long elapsedRealtime();
1.3、SystemClock.elapsedRealtimeNanos 它获取的是开机到现在的时间,单位是纳秒。它包括了系统休眠的时间。
1.4、SystemClock.currentThreadTimeMilis
它获取的是在当前线程中运行的时间,单位是毫秒。
二、SystemClock引发的问题:
Hanler中发送消息(或者延时消息) 是通过SystemClock.uptimeMillis()为基准计算的。
2.1、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);
}
2.2、从MessageQueue中消费Message,判断Message是否到了执行时间,也是以uptimeMillis()为基准
Looper.loop()循环
public static void loop() {
for (;;) {
Message msg = queue.next(); // might block
msg.target.dispatchMessage(msg);
}
}
从MessageQueue中取Message,执行时间也是以SystemClock.uptimeMillis()为基准进行比较的。
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;
}
}
...
}
}
2.3、System.uptimeMillis()计算系统从开机到现在的时间,单位是毫秒。但是它不包括系统休眠的时间(cpu休眠、屏幕休眠等)。
当手机灭屏处于休眠状态的时间是不计算进System.uptimeMillis()
比如发送一个延时20分钟的Message消息,系统灭屏后进入了深度睡眠(假设深度睡眠了1个小时),当进程苏醒后,这一个小时的时间是不计入(1)中的now
final long now = SystemClock.uptimeMillis();
从系统时钟看 已经过去1个小时了,但是计算now时,因为uptimeMillis 不包含休眠时的时间。
如果now< msg.when,会判定messsage还没有到执行时间,就不会从MessageQueue中取出并执行。