Android中各種Time API詳細

1、時間API

為瞭跟蹤性能,我們需要測量時間間隔,即兩個時間點之間的差異。 JDK 為我們提供瞭兩種獲取當前時間的方法:

// Milliseconds since Unix epoch (00:00:00 UTC on 1 January 1970)
System.currentTimeMillis()
// Nanoseconds since the VM started.
System.nanoTime()

Android 提供瞭一個 SystemClock 類,它增加瞭一些:

// (API 29) Clock that starts at Unix epoch.
// Synchronized using the device's location provider.
SystemClock.currentGnssTimeClock()
// Milliseconds running in the current thread.
SystemClock.currentThreadTimeMillis()
// Milliseconds since boot, including time spent in sleep.
SystemClock.elapsedRealtime()
// Nanoseconds since boot, including time spent in sleep.
SystemClock.elapsedRealtimeNanos()
// Milliseconds since boot, not counting time spent in deep sleep.
SystemClock.uptimeMillis()

我們應該選擇哪一個? SystemClock javadoc 有助於回答這個問題:

System#currentTimeMillis 可以由用戶或電話網絡設置,因此時間可能會不可預測地向後或向前跳躍。 間隔或經過時間測量應使用不同的時鐘。

SystemClock#uptimeMillis 在系統進入深度睡眠時停止。 這是大多數間隔計時的基礎,例如 Thread#sleep(long) Object#wait(long) System#nanoTime。 當間隔不跨越設備休眠時,該時鐘適用於間隔計時。

SystemClock#elapsedRealtime SystemClock#elapsedRealtimeNanos 包括深度睡眠。 該時鐘是通用間隔計時的推薦基礎。

應用程序的性能對深度睡眠中發生的事情沒有影響,所以我們最好的選擇是 SystemClock.uptimeMillis() System.nanoTime()

2、uptimeMillis() vs nanoTime()

System.nanoTime() uptimeMillis() 更精確,但這僅對微基準測試有用。 在生產中跟蹤性能時,我們需要毫秒級的分辨率。

讓我們比較一下它們的性能影響。 我克隆瞭 Android Benchmark Samples 存儲庫並添加瞭以下測試:

@LargeTest
@RunWith(AndroidJUnit4::class)
class TimingBenchmark {
    @get:Rule
    val benchmarkRule = BenchmarkRule()

    @Test
    fun nanoTime() {
        benchmarkRule.measureRepeated {
            System.nanoTime()
        }
    }

    @Test
    fun uptimeMillis() {
        benchmarkRule.measureRepeated {
            SystemClock.uptimeMillis()
        }
    }
}

在運行 Android 10 的 Pixel 3 上的結果:

System.nanoTime() 中值時間:208 ns

SystemClock.uptimeMillis() 中值時間:116 ns

SystemClock.uptimeMillis() 幾乎快兩倍! 雖然這種差異應該不會對應用程序產生任何有意義的影響,但我們能弄清楚為什麼它要快得多嗎?

3、uptimeMillis() 實現

SystemClock.uptimeMillis() 實現為帶有@CriticalNative 註釋的本機方法。 CriticalNative 為不包含對象的方法提供更快的 JNI 轉換。

public final class SystemClock {
    @CriticalNative
    native public static long uptimeMillis();
}

原生實現在 SystemClock.c++ 中:

int64_t uptimeMillis()
{
    int64_t when = systemTime(SYSTEM_TIME_MONOTONIC);
    return (int64_t) nanoseconds_to_milliseconds(when);
}

systemTime() 在 Timers.cpp 中定義:

nsecs_t systemTime(int clock) {
    static constexpr clockid_t clocks[] = {
        CLOCK_REALTIME,
        CLOCK_MONOTONIC,
        CLOCK_PROCESS_CPUTIME_ID,
        CLOCK_THREAD_CPUTIME_ID,
        CLOCK_BOOTTIME
    };
    timespec t = {};
    clock_gettime(clocks[clock], &t);
    return nsecs_t(t.tv_sec)*1000000000LL + t.tv_nsec;
}

4、nanoTime() 實現

System.nanoTime() 也被實現為帶有@CriticalNative 註釋的本地方法。

public final class System {
    @CriticalNative
    public static native long nanoTime();
}

本地實現在 System.c 中:

static jlong System_nanoTime() {
  struct timespec now;
  clock_gettime(CLOCK_MONOTONIC, &now);
  return now.tv_sec * 1000000000LL + now.tv_nsec;
}

這兩個實現其實很相似,都調用clock_gettime()

事實證明,@CriticalNative 最近才被添加到 System.nanoTime() ,這就解釋瞭為什麼它變慢瞭!

結論:

在生產應用中跟蹤性能時:

對於大多數用例,毫秒分辨率就足夠瞭。 要測量時間間隔,請使用 SystemClock.uptimeMillis() System.nanoTime() 。 後者在較舊的 Android 版本上速度較慢,但這在這裡無關緊要。

到此這篇關於Android中各種Time API詳細的文章就介紹到這瞭,更多相關Android中各種Time API內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: