Android Handler机制3之SystemClock类

Android Handler机制系列文章整体内容如下:

  • Android Handler机制1之Thread
  • Android Handler机制2之ThreadLocal
  • Android Handler机制3之SystemClock类
  • Android Handler机制4之Looper与Handler简介
  • Android Handler机制5之Message简介与消息对象对象池
  • Android Handler机制6之MessageQueue简介
  • Android Handler机制7之消息发送
  • Android Handler机制8之消息的取出与消息的其他操作
  • Android Handler机制9之Handler的Native实现前奏之Linux IO多路复用
  • Android Handler机制10之Handdler的Native实现Native的实现
  • Android Handler机制11之Handler机制总结
  • Android Handler机制12之Callable、Future和FutureTask
  • Android Handler机制13之AsyncTask源码解析

本片文章的主要内容如下:

  • 1 类注释
  • 2 源码解析
  • 3 方法解析
  • 4 JNI和Native对应的代码

官网位置在https://developer.android.com/reference/android/os/SystemClock.html

平时看源码的是SystemClock.java

老规矩先看下类的注释

一、类注释

/**
 * Core timekeeping facilities.
 *
 * <p> Three different clocks are available, and they should not be confused:
 *
 * <ul>
 *     <li> <p> {@link System#currentTimeMillis System.currentTimeMillis()}
 *     is the standard "wall" clock (time and date) expressing milliseconds
 *     since the epoch.  The wall clock can be set by the user or the phone
 *     network (see {@link #setCurrentTimeMillis}), so the time may jump
 *     backwards or forwards unpredictably.  This clock should only be used
 *     when correspondence with real-world dates and times is important, such
 *     as in a calendar or alarm clock application.  Interval or elapsed
 *     time measurements should use a different clock.  If you are using
 *     System.currentTimeMillis(), consider listening to the
 *     {@link android.content.Intent#ACTION_TIME_TICK ACTION_TIME_TICK},
 *     {@link android.content.Intent#ACTION_TIME_CHANGED ACTION_TIME_CHANGED}
 *     and {@link android.content.Intent#ACTION_TIMEZONE_CHANGED
 *     ACTION_TIMEZONE_CHANGED} {@link android.content.Intent Intent}
 *     broadcasts to find out when the time changes.
 *
 *     <li> <p> {@link #uptimeMillis} is counted in milliseconds since the
 *     system was booted.  This clock stops when the system enters deep
 *     sleep (CPU off, display dark, device waiting for external input),
 *     but is not affected by clock scaling, idle, or other power saving
 *     mechanisms.  This is the basis for most interval timing
 *     such as {@link Thread#sleep(long) Thread.sleep(millls)},
 *     {@link Object#wait(long) Object.wait(millis)}, and
 *     {@link System#nanoTime System.nanoTime()}.  This clock is guaranteed
 *     to be monotonic, and is suitable for interval timing when the
 *     interval does not span device sleep.  Most methods that accept a
 *     timestamp value currently expect the {@link #uptimeMillis} clock.
 *
 *     <li> <p> {@link #elapsedRealtime} and {@link #elapsedRealtimeNanos}
 *     return the time since the system was booted, and include deep sleep.
 *     This clock is guaranteed to be monotonic, and continues to tick even
 *     when the CPU is in power saving modes, so is the recommend basis
 *     for general purpose interval timing.
 *
 * </ul>
 *
 * There are several mechanisms for controlling the timing of events:
 *
 * <ul>
 *     <li> <p> Standard functions like {@link Thread#sleep(long)
 *     Thread.sleep(millis)} and {@link Object#wait(long) Object.wait(millis)}
 *     are always available.  These functions use the {@link #uptimeMillis}
 *     clock; if the device enters sleep, the remainder of the time will be
 *     postponed until the device wakes up.  These synchronous functions may
 *     be interrupted with {@link Thread#interrupt Thread.interrupt()}, and
 *     you must handle {@link InterruptedException}.
 *
 *     <li> <p> {@link #sleep SystemClock.sleep(millis)} is a utility function
 *     very similar to {@link Thread#sleep(long) Thread.sleep(millis)}, but it
 *     ignores {@link InterruptedException}.  Use this function for delays if
 *     you do not use {@link Thread#interrupt Thread.interrupt()}, as it will
 *     preserve the interrupted state of the thread.
 *
 *     <li> <p> The {@link android.os.Handler} class can schedule asynchronous
 *     callbacks at an absolute or relative time.  Handler objects also use the
 *     {@link #uptimeMillis} clock, and require an {@link android.os.Looper
 *     event loop} (normally present in any GUI application).
 *
 *     <li> <p> The {@link android.app.AlarmManager} can trigger one-time or
 *     recurring events which occur even when the device is in deep sleep
 *     or your application is not running.  Events may be scheduled with your
 *     choice of {@link java.lang.System#currentTimeMillis} (RTC) or
 *     {@link #elapsedRealtime} (ELAPSED_REALTIME), and cause an
 *     {@link android.content.Intent} broadcast when they occur.
 * </ul>
 */

我翻译一下

  • 核心计时设施
  • 这里面有三种不同的时钟可用,不应该混淆
  • System.currentTimeMillis()是标准的"wall"钟(日期和时间)以来表示毫秒。这个时钟可以由用户或者手机网络设置(见setCurrentTimeMillis(long)),所以时间可能不可预知向前或向后跳。这个时钟只应使用符合真实世界的日期和时间和你重要的,比如在一个日历或闹钟应用程序。时间间隔测量应该使用不同的时钟。如果你打算使用System.currentTimeMillis(),则需要留意ACTION_TIME_TICK ACTION_TIME_TICKACTION_TIME_CHANGED ACTION_TIME_CHANGEDACTION_TIMEZONE_CHANGEDACTION_TIMEZONE_CHANGED的Intent广播从而了解时间的变化。
  • uptimeMillis()表示自系统启动时开始计数,以毫秒为单位。返回的是从系统启动到现在这个过程中的处于非休眠期的时间。当系统进入深度睡眠(CPU关闭,屏幕显示器不显示,设备等待外部输入)时,或者空闲或其他省电机制的影响,此时时钟停止,但是该时钟不会被时钟调整。这个方法是大多数间隔时间的基础,例如Thread.sleep(millls)方法、Object.wait(millis)方法、System.nanoTime()都是基于此方法的。该时钟是被保证为单调的,并且适用当间隔不跨越设备睡眠时间间隔定时。大多数方法接受时间戳的值就像uptimeMillis()方法。
  • elapsedRealtime()和elapsedRealtimeNanos()则是返回系统启动后到现在的的时间,并且包括深度睡眠时间。该时钟保证是单调的,即使CPU在省电模式下,该事件也会继续计时。该时钟可以被使用在当测量事件可能跨越系统睡眠的时间段。
  • 有几种控制事件时间机制
    • 标准的方法像Thread.sleep(millis) 和 Object.wait(millis)总是可用的,这些方法使用的是uptimeMillis()时钟,如果设备进入深度休眠,剩余的时间将被推迟直到系统唤醒。这些同步方法可能被Thread.interrupt()中断,所以你必须处理InterruptedException异常。
    • SystemClock.sleep(millis)是一个类似于Thread.sleep(millis)的实用方法,但是它忽略InterruptedException异常。使用该函数产生的延迟如果你不使用Thread.interrupt(),因为它会保存线程的中断状态。
    • 在android.os.Handler类中执行异步调用的使用会用到一个绝对的时间或者相对时间的概念。所以Handler使用uptimeMillis()方法获取一个时钟,并且需要调用android.os.Looper来进行事件循环)(通常存在于任何GUI应用程序中)。
    • 即使设备或者应用程序处于深度休眠或者未运行, android.app.AlarmManage仍然可以发出一次或者重复事件。事件可以根据你的选择来的,事件可以是java.lang.System.currentTimeMilli或者是elapsedRealtime(),并且会导致产生一个Intent的广播。

上面提到了一个概念"关于Android的深度睡眠",这里就简单介绍下:

1、Android的深度睡眠

所以Android的深度睡眠,即屏幕关闭后,一段时间不做任何操作,不连接USB,然后在一定的时间后,手机很多服务、进程都睡眠了,不再运行了。

2、关于时间间隔

通过上面的注释可以解决我们之前Android一直困扰我们的一个问题:

Android中计算时间间隔的方法:

记录开始时间startTime,然后每次回调,获取当前时间 currentTime,计算差值=currentTime - startTime。

而获取当前时间,Android提供两个方法:

  • SystemClock.uptimeMillis()
  • System.currentTimeMillis()

这两个方法的区别:

  • SystemClock.uptimeMillis():从开机到现在的毫秒数(手机睡眠时间不包括在内)
  • System.currentTimeMillis():从1970年1月1日 UTC到现在的毫秒数

存在的问题:

System.currentTimeMillis()获取的时间,可以通过System.setCurrentTimeMillis(long)方法,进行修改,那么在某些情况下,一旦被修改,时间间隔就不准了,所以推荐使用SystemClock.uptimeMillis()

这里大家可以参考AnimationUtils类的currentAnimationTimeMillis()方法。

二、源码解析

public final class SystemClock {
    private static final String TAG = "SystemClock";

    /**
     * This class is uninstantiable.
     */
    private SystemClock() {
        // This space intentionally left blank.
    }

    /**
     * Waits a given number of milliseconds (of uptimeMillis) before returning.
     * Similar to {@link java.lang.Thread#sleep(long)}, but does not throw
     * {@link InterruptedException}; {@link Thread#interrupt()} events are
     * deferred until the next interruptible operation.  Does not return until
     * at least the specified number of milliseconds has elapsed.
     *
     * @param ms to sleep before returning, in milliseconds of uptime.
     */
    public static void sleep(long ms)
    {
        long start = uptimeMillis();
        long duration = ms;
        boolean interrupted = false;
        do {
            try {
                Thread.sleep(duration);
            }
            catch (InterruptedException e) {
                interrupted = true;
            }
            duration = start + ms - uptimeMillis();
        } while (duration > 0);
        
        if (interrupted) {
            // Important: we don't want to quietly eat an interrupt() event,
            // so we make sure to re-interrupt the thread so that the next
            // call to Thread.sleep() or Object.wait() will be interrupted.
            Thread.currentThread().interrupt();
        }
    }
    
    /**
     * 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) {
        IBinder b = ServiceManager.getService(Context.ALARM_SERVICE);
        IAlarmManager mgr = IAlarmManager.Stub.asInterface(b);
        if (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;
    }

    /**
     * Returns milliseconds since boot, not counting time spent in deep sleep.
     *
     * @return milliseconds of non-sleep uptime since boot.
     */
    native public static long uptimeMillis();

    /**
     * Returns milliseconds since boot, including time spent in sleep.
     *
     * @return elapsed milliseconds since boot.
     */
    native public static long elapsedRealtime();

    /**
     * Returns nanoseconds since boot, including time spent in sleep.
     *
     * @return elapsed nanoseconds since boot.
     */
    public static native long elapsedRealtimeNanos();

    /**
     * Returns milliseconds running in the current thread.
     * 
     * @return elapsed milliseconds in the thread
     */
    public static native long currentThreadTimeMillis();

    /**
     * Returns microseconds running in the current thread.
     * 
     * @return elapsed microseconds in the thread
     * 
     * @hide
     */
    public static native long currentThreadTimeMicro();

    /**
     * Returns current wall time in  microseconds.
     * 
     * @return elapsed microseconds in wall time
     * 
     * @hide
     */
    public static native long currentTimeMicro();
}

通过上面源代码,我们得出两个结论

  • 构造函数是私有的,是不允许外部new的
  • 所有方法都是static的,所以可以直接通过类直接调用。

三、方法解析

(一)、sleep(long)方法

/**
     * Waits a given number of milliseconds (of uptimeMillis) before returning.
     * Similar to {@link java.lang.Thread#sleep(long)}, but does not throw
     * {@link InterruptedException}; {@link Thread#interrupt()} events are
     * deferred until the next interruptible operation.  Does not return until
     * at least the specified number of milliseconds has elapsed.
     *
     * @param ms to sleep before returning, in milliseconds of uptime.
     */
    public static void sleep(long ms)
    {
        long start = uptimeMillis();
        long duration = ms;
        boolean interrupted = false;
        do {
            try {
                Thread.sleep(duration);
            }
            catch (InterruptedException e) {
                interrupted = true;
            }
            duration = start + ms - uptimeMillis();
        } while (duration > 0);
        
        if (interrupted) {
            // Important: we don't want to quietly eat an interrupt() event,
            // so we make sure to re-interrupt the thread so that the next
            // call to Thread.sleep() or Object.wait() will be interrupted.
            Thread.currentThread().interrupt();
        }
    }
1、先来看下注释:

等待一个给定的毫秒数(对uptimemillis())方法返回之前。类似于Thread.sleep(long)方法,却不会抛出InterruptedException。Thread的interrupt()引起的事件将被推迟到下一次中断操作。至少在指定的毫秒数之后才返回。

2、简析

我们看了一下代码,其实这个方法本质上就是封装了Thread.sleep()方法,但是,不会抛出InterruptedException

(二)、setCurrentTimeMillis(long)方法

    /**
     * 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) {
        IBinder b = ServiceManager.getService(Context.ALARM_SERVICE);
        IAlarmManager mgr = IAlarmManager.Stub.asInterface(b);
        if (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、简析

通过代码我们知道,利用AIDL获取IAlarmManager的客户端,然后调用IAlarmManager的setTime()方法

所以这个方法就是用来设置系统指针的。

(三)、uptimeMillis()方法

    /**
     * Returns milliseconds since boot, not counting time spent in deep sleep.
     *
     * @return milliseconds of non-sleep uptime since boot.
     */
    native public static long uptimeMillis();

返回的是系统从启动到当前的处于非休眠期的时间。

(四)、elapsedRealtime()方法

    /**
     * Returns milliseconds since boot, including time spent in sleep.
     *
     * @return elapsed milliseconds since boot.
     */
    native public static long elapsedRealtime();

返回的是系统从启动到现在的时间

(五)、elapsedRealtimeNanos()方法

    /**
     * Returns nanoseconds since boot, including time spent in sleep.
     *
     * @return elapsed nanoseconds since boot.
     */
    public static native long elapsedRealtimeNanos();

返回系统启动到现在的纳秒数,包含休眠时间

(六)、elapsedRealtimeNanos()方法

    /**
     * Returns milliseconds running in the current thread.
     * 
     * @return elapsed milliseconds in the thread
     */
    public static native long currentThreadTimeMillis();

返回当前线程运行的毫秒数

(七)、currentThreadTimeMicro()方法

    /**
     * Returns microseconds running in the current thread.
     * 
     * @return elapsed microseconds in the thread
     * 
     * @hide
     */
    public static native long currentThreadTimeMicro();

返回当前线程中已经运行的时间(单位是milliseconds毫秒)

(八)、currentThreadTimeMicro()方法

    /**
     * Returns current wall time in  microseconds.
     * 
     * @return elapsed microseconds in wall time
     * 
     * @hide
     */
    public static native long currentTimeMicro();

返回当前线程中已经运行的时间(单位是microseconds微秒)

四、JNI和Native对应的代码

通过我们之前的文章Android跨进程通信IPC之3——关于"JNI"的那些事我们知道,SystemClock对应的JNI文件是android_os_SystemClock.cpp

代码很简单,我就不说了 代码如下:

/*
 * System clock functions.
 */

#include <sys/time.h>
#include <limits.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>

#include "JNIHelp.h"
#include "jni.h"
#include "core_jni_helpers.h"

#include <sys/time.h>
#include <time.h>

#include <utils/SystemClock.h>

namespace android {

/*
 * native public static long uptimeMillis();
 */
static jlong android_os_SystemClock_uptimeMillis(JNIEnv* env,
        jobject clazz)
{
    return (jlong)uptimeMillis();
}

/*
 * native public static long elapsedRealtime();
 */
static jlong android_os_SystemClock_elapsedRealtime(JNIEnv* env,
        jobject clazz)
{
    return (jlong)elapsedRealtime();
}

/*
 * native public static long currentThreadTimeMillis();
 */
static jlong android_os_SystemClock_currentThreadTimeMillis(JNIEnv* env,
        jobject clazz)
{
    struct timespec tm;

    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tm);

    return tm.tv_sec * 1000LL + tm.tv_nsec / 1000000;
}

/*
 * native public static long currentThreadTimeMicro();
 */
static jlong android_os_SystemClock_currentThreadTimeMicro(JNIEnv* env,
        jobject clazz)
{
    struct timespec tm;

    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tm);

    return tm.tv_sec * 1000000LL + tm.tv_nsec / 1000;
}

/*
 * native public static long currentTimeMicro();
 */
static jlong android_os_SystemClock_currentTimeMicro(JNIEnv* env,
        jobject clazz)
{
    struct timeval tv;

    gettimeofday(&tv, NULL);
    return tv.tv_sec * 1000000LL + tv.tv_usec;
}

/*
 * public static native long elapsedRealtimeNano();
 */
static jlong android_os_SystemClock_elapsedRealtimeNano(JNIEnv* env,
        jobject clazz)
{
    return (jlong)elapsedRealtimeNano();
}

/*
 * JNI registration.
 */
static JNINativeMethod gMethods[] = {
    /* name, signature, funcPtr */
    { "uptimeMillis",      "()J",
            (void*) android_os_SystemClock_uptimeMillis },
    { "elapsedRealtime",      "()J",
            (void*) android_os_SystemClock_elapsedRealtime },
    { "currentThreadTimeMillis",      "()J",
            (void*) android_os_SystemClock_currentThreadTimeMillis },
    { "currentThreadTimeMicro",       "()J",
            (void*) android_os_SystemClock_currentThreadTimeMicro },
    { "currentTimeMicro",             "()J",
            (void*) android_os_SystemClock_currentTimeMicro },
    { "elapsedRealtimeNanos",      "()J",
            (void*) android_os_SystemClock_elapsedRealtimeNano },
};
int register_android_os_SystemClock(JNIEnv* env)
{
    return RegisterMethodsOrDie(env, "android/os/SystemClock", gMethods, NELEM(gMethods));
}

}; // namespace android

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Jack的Android之旅

刨解OkHttp框架

继AsyncTask,又把手术刀指向OkHttp,有时候解析源码会上瘾。因为源码里包含的东西仿佛就是组成计算机世界的砖头,水分,只要有这些东西,就可以保罗万物,...

1462
来自专栏Android开发实战

谷歌官方Android应用架构库——LiveData

LiveData 是一个数据持有者类,它持有一个值并允许观察该值。不同于普通的可观察者,LiveData 遵守应用程序组件的生命周期,以便 Observer 可...

1203
来自专栏潇涧技术专栏

Pury Project Analysis

Pury的源码:https://github.com/NikitaKozlov/Pury

922
来自专栏后台开发+音视频+ffmpeg

dpvs源码分析(续)

在上一篇https://cloud.tencent.com/developer/article/1180256?s=original-sharing,我们已经介...

5041
来自专栏Android 研究

Android Handler机制7之消息发送

光看上面这些API你可能会觉得handler能法两种消息,一种是Runnable对象,一种是message对象,这是直观的理解,但其实post发出的Runnab...

1081
来自专栏JetpropelledSnake

SNMP学习笔记之SNMP报文协议详解

简单网络管理协议(SNMP)是TCP/IP协议簇的一个应用层协议。在1988年被制定,并被Internet体系结构委员会(IAB)采纳作为一个短期的网络管理解决...

1372
来自专栏西二旗一哥

iOS - autoreleasepool and @autoreleasepool

+ 在一个自动引用计数的环境中(并不是垃圾回收机制),一个包含了多个对象的 NSAutoreleasePool 对象能够接收 autorelease 消息并且...

1674
来自专栏Golang语言社区

用Go实现一门解释型语言

A interpreter language implementation in Go

912
来自专栏雪胖纸的玩蛇日常

Uncaught SyntaxError: Unexpected token ' in JSON at position 1

1.2K3
来自专栏IT开发技术与工作效率

VBA非登陆下载Excel文件并处理例子

1514

扫码关注云+社区

领取腾讯云代金券