Hander.sendMessageDelayed()延时消息为什么延时会不准确

一、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中取出并执行。

你可能感兴趣的:(Hander.sendMessageDelayed()延时消息为什么延时会不准确)