android如何获取系统开机时间

开发过程中有时候需要获取android开机时间,这个是两年前遇到的一个问题,在此总结一下。

方法一、通过系统SystemClock接口运算

android.os.SystemClock的elapsedRealtimeNanos返回从系统开机现在的经历过的时间(单位:纳秒),包括系统休眠待机时间。所以我们可以使用当前时间减去系统开机到现在的时间计算出系统的启动时间

// 返回开机时间,单位微妙
long bootTime() {
    return System.currentTimeMillis() - SystemClock.elapsedRealtimeNanos() / 1000000;
}

根据SmarxPan的提示可以使用毫秒函数SystemClock.elapsedRealtime()

// 返回开机时间,单位微妙
long bootTime() {
    return System.currentTimeMillis() - SystemClock.elapsedRealtime();
}

方法二、通过读取/proc/stat文件获取系统开机时间

方法一获取系统开机时间非常方便,但是有一个缺点必须在Android 4.2以上才能支持该接口,也就是说在低平台调用接口会应为找不到该函数而闪退。

不过没关系,我们可以使用linux层提供的信息。
关于 /proc/stat文件: stat文件记录了当前系统常有状态,例如cpu时钟,内存页,开机时间等等。其中开机时间是btime字段后面的数值单位是秒

以下是本人手机设备的 /proc/stat内容(华为, Android6.0系统)

shell@HWGRA:/proc $ cat stat
cpu  1840729 131539 1145597 10753491 13057 57 45011 0 0 0
cpu0 246543 17369 249206 7219225 3792 11 32119 0 0 0
cpu1 282309 19254 210287 545809 2092 39 4639 0 0 0
cpu2 279958 19834 209812 547156 2380 3 2743 0 0 0
cpu3 276153 20196 203136 548855 2257 4 1970 0 0 0
cpu4 173666 14209 67996 502766 1131 0 1414 0 0 0
cpu5 205458 13675 70375 448196 528 0 703 0 0 0
cpu6 210211 13822 70829 449078 430 0 710 0 0 0
cpu7 166431 13180 63956 492406 447 0 713 0 0 0
intr 164085213 0 0 0 0 341 0 0 0 167 167 0 0 0 4 3 0 0 0 0 0 0 0 0 0 0 0 0 19263077 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 50348 0 0 0 0 0 0 6615253 0 0 0 0 0 0 68541 0 0 620 0 0 0 0 3304 0 0 0 0 0 0 0 0 177234 26390930 24 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 53695 0 0 32981493 9285925 2391853 2011 0 0 0 0 0 0 0 0 0 485 0 0 0 0 0 1480860 100 2118249 0 0 0 0 5 998345 0 196 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 991829 51562 0 0 0 131664 0 918 0 429104 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 69486 31005 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 1 21 0 0 0 0 0 0 286338 0 0 0 8 1 0 143155 1
ctxt 197939068
btime 1501803547
processes 238588
procs_running 2
procs_blocked 0
softirq 78287404 12496 14322913 16214 439295 11578 11578 45998374 3558439 4601 13911916

可以看到btime字段
btime 1501803547
表示从1970-01-01 00:00:00 +0000起始的时间,单位:秒
可以用java轻松实现,本博客使用jni实现。
主要实现方案
头文件:android_system_utils .h

#ifndef ANDROID_SYSTEM_UTILS_H
#define ANDROID_SYSTEM_UTILS_H

#include <cstddef>

class android_system_utils {
public:
    /**
     * get android system boot time(unit second).
     * time since 1970-01-01 00:00:00 +0000 (UTC).
     */
    static long getBootTime();

private:
    static long parseBootTime(const char* text, size_t size);
};

#endif

源文件:android_system_utils.cpp

#include "android_system_utils.h"

#include <cstdio>
#include <cstdlib>
#include <fcntl.h>
#include <cctype>

#include "common.h"
#include "file_utils.h"

static char TAG[] = "android_system_utils";

long android_system_utils::getBootTime() {
    const char filepath[] = "/proc/stat";

    if (!file_utils::isExists(filepath)) {
        return -1;
    }

    int fd = open(filepath, O_RDONLY);
    if (-1 == (fd)) {
        lerror(TAG, "getBootTime. open: %s file failed. errno: %d, error: %s", errno, strerror(errno));
        return -1;
    }
    errno = 0;
    char storebuf[128] = {0}; // 128bit is enough as we only concern about btime text.
    char buf[2] = {0};
    ssize_t len = 0;
    long ret = -1;
    do {
        memset(buf, 0, sizeof(buf));
        if ((len = safe_read(fd, buf, 1)) > 0) { // only read on character.
            if ('\n' == buf[0]) { // match a new line.
                if (-1 != (ret = parseBootTime(storebuf, strlen(storebuf)))) {
                    break;
                }
                memset(storebuf, 0, sizeof(storebuf));
            } else if (strlen(storebuf) < sizeof(storebuf) - 1) { // fill a line data.
                strcat(storebuf, buf);
            }
        }
    } while (len > 0);
    if (-1 == len) {
        lerror(TAG, "getBootTime. read error. errno: %d, error: %s", errno, strerror(errno));
    }

    close(fd);

    return ret;
};

long android_system_utils::parseBootTime(const char *text, size_t size) {
    const char btime[] = "btime";
    const char *find = strstr(text, btime);
    if (NULL == find) {
        return -1;
    }

    char c[2] = {0};
    char buf[64] = {0};
    for (size_t i = 0; i < size; i++) {
        c[0] = *(text + i);
        if (isdigit(c[0])) { // fill buf with digital.
            strcat(buf, c);
        }
    }
    if (0 == strlen(buf)) {
        return -1;
    }

    return atol(buf);
}

三、总结
虽然使用stat文件也能够获取开机时间,但是精度只能到达秒,还是没有系统SystemClock.elapsedRealtimeNanos的精度搞。因此我们折中处理,在android4.2高版本使用系统API接口,低版本使用stat数值接口,即保证兼容性又能够发挥高版本的精度问题。

DeviceHelper.java

package org.penny;

import android.os.Build;
import android.os.SystemClock;

/**
 * Created by lucky on 05/08/2017.
 */

public class DeviceHelper {
    static {
        System.loadLibrary("Pannel");
    }

    /**
     * get android system boot time(unit milli second).
     * time since 1970-01-01 00:00:00 +0000 (UTC).
     */
    public static long getBootTime() {
        if (Build.VERSION.SDK_INT < 17) {
            return native_getBootTime() * 1000;
        }
        return System.currentTimeMillis() - SystemClock.elapsedRealtimeNanos() / 1000000;
    }

    /**
     * get android system boot time(unit second).
     * time since 1970-01-01 00:00:00 +0000 (UTC).
     */
    public static native long native_getBootTime();
}

DeviceHelperTest.java

package org.penny;

import android.os.Build;
import android.support.test.runner.AndroidJUnit4;
import android.util.Log;

import org.junit.Test;
import org.junit.runner.RunWith;

import static org.junit.Assert.assertEquals;

/**
 * Created by lucky on 05/08/2017.
 */

@RunWith(AndroidJUnit4.class)
public class DeviceHelperTest {
    private static final String TAG = DeviceHelperTest.class.getSimpleName();

    @Test
    public void testNativeGetBootTime() {
        Log.d(TAG, "testNativeGetBootTime...");
        Log.d(TAG, "boot time: " + DeviceHelper.native_getBootTime());
    }

    @Test
    public void testGetBootTime() {
        Log.d(TAG, "testGetBootTime...");
        Log.d(TAG, "boot time: " + DeviceHelper.getBootTime());

        if (Build.VERSION.SDK_INT >= 17) {
            assertEquals(DeviceHelper.native_getBootTime(), DeviceHelper.getBootTime() / 1000);
        }
    }
}

测试用例结果

--------- beginning of main
08-05 21:36:35.114 11597 11612 D DeviceHelperTest: testGetBootTime...
08-05 21:36:35.119 11597 11612 D DeviceHelperTest: boot time: 1501803547791
08-05 21:36:35.126 11597 11612 D DeviceHelperTest: testNativeGetBootTime...
08-05 21:36:35.128 11597 11612 D DeviceHelperTest: boot time: 1501803547

因为写博客的时候顺便跑过代码,所以就把代码发到github上面

https://github.com/FreeJack007/Pannel

猜你喜欢

转载自blog.csdn.net/SweetTool/article/details/76652108