System.currentTimeMillis()在华为上返回错误的时间戳

时间:2017-08-04 14:15:59

标签: android linux ntp huawei

问题是System.currentTimeMillis()返回错误的毫秒,不同的时间范围主要在未来有时长达6个月,但它从几秒到几个月不等。

正在发生这种情况的设备是平板电脑型号华为M2-A201W 在Android 5.1.1 上,内核版本是:**3.10.74-gdbd9055**

我的第一个假设是 NTP 在某种程度上搞砸了时间,但我有数千台平板电脑,其中一些没有网络连接,没有SIM卡所以没有GSM / 3G / 4G

我正在使用System.currentTimeMillis()保存一个表的列,以便在本地sqlite数据库中创建行的时间。

在我使用的平板电脑上,这种异常情况经常发生(每次System.currentTimeMillis()通话的30%)。

6 个答案:

答案 0 :(得分:6)

作为使用System.currentTimeMillis()的解决方法,也许您可​​以让sqlite处理时间戳创建,看看是否能解决您的问题?

如果您需要毫秒,则使用timestamp default current_timestampdefault(strftime('%Y-%m-%d %H:%M:%f', 'now'))定义/更改“已创建”列 - 如下所示:

sqlite> create table my_table(id integer primary key autoincrement not null, name text, created timestamp default(strftime('%Y-%m-%d %H:%M:%f', 'now')) not null);

sqlite> insert into my_table(name) values ('MyTestRow1');
sqlite> insert into my_table(name) values ('MyTestRow2');

sqlite> select * from my_table;

1|MyTestRow1|2017-08-07 10:08:50.898
2|MyTestRow2|2017-08-07 10:08:54.701

答案 1 :(得分:5)

Android平台中的Java API调用System.currentTimeMillis()使用POSIX api gettimeofday来获取以毫秒为单位的时间。请参阅here

static jlong System_currentTimeMillis(JNIEnv*, jclass) {
    timeval now;
    gettimeofday(&now, NULL);
    jlong when = now.tv_sec * 1000LL + now.tv_usec / 1000;
    return when;
}

假设每次拨打gettimeofday都会成功。我想你的问题可能会在这里发生。

最好检查每个API调用的返回值,并确定错误发生时下一步该做什么。

所以我建议在JNI中使用您自己的实现更可靠的方法,如下所示。按顺序调用这些POSIX API,如果gettimeofday失败,请致电clock_gettime,如果再次失败,请致电time

struct timeval now;
if (gettimeofday(&now, NULL) != 0) {
    struct timespec ts;
    if (clock_gettime(CLOCK_REALTIME, &ts) == 0) {
        now.tv_sec = ts.tv_sec;
        now.tv_usec = ts.tv_nsec / 1000LL;
    } else {
        now.tv_sec = time(NULL);
        now.tv_usec = 0;
    }
}
jlong when = now.tv_sec * 1000LL + now.tv_usec / 1000LL;
__android_log_print(ANDROID_LOG_INFO, "TAG", "%lld", when);

答案 2 :(得分:5)

当您没有SIM卡而没有GSM / 3G / 4G时,您的手机无法根据网络提供的时间/区域更新正确的时间。

enter image description here

因此,具有网络的设备显示正确的时间,而没有网络的其他设备可能显示不正确的时间 - 您必须手动设置正确的时间。 System.currentTimeMilis()从系统中读取时间。 g但是在电源转动时,时钟工作。

检查使用Socket或DatagramSocket的应用是否阻止NTP(UDP端口123)。注意:NTP适用于网络中所有主机或路由器的时钟必须相同的场景。如果您的设备切换到两个(或更多)不同的网络并从不同的来源获得时间更新,则可能会有时间波动。

最终,您的系统时间正在改变,这就是它波动的原因。如果手动禁用自动日期和时间后手动System.currentTimeMilis(),我相信,它不会波动(没有异常)。如果是这种情况,那么您的Huewai平板电脑没有错误。

答案 3 :(得分:4)

如何通过运行" date +%s "来本地获取时间戳? Linux内核中的命令?

此处," +%s" 是自1970-01-01 00:00:00 UTC以来的秒数。 (GNU Coreutils 8。24日期手册)

  try {
            // Run the command
            Process process = Runtime.getRuntime().exec("date +%s");
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));

            // Grab the results
            StringBuilder log = new StringBuilder();
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                log.append(line);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

如果你打印这个,

Log.e("unix_time: ", "" + log.toString());

您将获得一个Unix时间戳,例如的 1502187111

要将其转换回日期对象,请乘以1000,因为java需要毫秒,

Date time = new Date(Long.parseLong(log.toString()) * 1000);
Log.e("date_time: ", "" + time.toString());

这将为您提供简单的日期格式。例如 2008年8月8日16:15:58 GMT + 06:00 2017

答案 4 :(得分:3)

由于您提到大多数调用正在获得适当的时间,并且只发生了30%的情况,我会为ACTION_TIME_CHANGEDACTION_TIMEZONE_CHANGED意图广播创建一个接收器,以找出何时时间变化。也许这会给你一个改变时间的线索。

ConnectivityManager一起,您可以检测设备是否已连接以及您拥有的连接类型,也许某些连接会触发时间更改。

// init the register and register the intents, in onStart, using:
receiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
      getNetworkInfo();
      if (Intent.ACTION_TIME_CHANGED.equals(intent.getAction()))
         Log.d(this.getClass().getName(), "Detected a time change. isWifiConn: " +
             isWifiConn + " isMobileConn: " + isMobileConn);
      if (Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction()))
         Log.d(this.getClass().getName(), "Detected a timezone change. isWifiConn: " +
             isWifiConn + " isMobileConn: " + isMobileConn);
    }
};
IntentFilter filters = new IntentFilter();
filters.addAction(Intent.ACTION_TIME_CHANGED);
filters.addAction(Intent.ACTION_TIMEZONE_CHANGED);
registerReceiver(receiver, filters);
Log.d(DEBUG_TAG, "Receiver registered");
// do not forget to unregister the receiver, eg. onStop, using:
unregisterReceiver(receiver);
//...
private void getNetworkInfo() {
    ConnectivityManager connMgr = (ConnectivityManager)
            getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
    isWifiConn = networkInfo.isConnected();
    networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
    isMobileConn = networkInfo.isConnected();
    Log.d(DEBUG_TAG, "Wifi connected: " + isWifiConn);
    Log.d(DEBUG_TAG, "Mobile connected: " + isMobileConn);
}

答案 5 :(得分:1)

此设备是否具有真正的硬件RTC?我无法通过Google搜索和阅读规格表找到明确的答案。一阵:

$ dmesg -s 65535 | grep -i rtc
来自shell的

应该给你一个答案。您应该看到类似的内容(因芯片组和内核版本而异):

[    3.816058] rtc_cmos 00:02: RTC can wake from S4
[    3.816429] rtc_cmos 00:02: rtc core: registered rtc_cmos as rtc0
[    3.816510] rtc_cmos 00:02: alarms up to one month, y3k, 242 bytes nvram, hpet irqs

如果grep没有返回消息(并且你对整个内核消息缓冲区进行了grep),那么你的答案是否正确。你没时间在这些设备上留出时间。您将始终需要NTP以及与Internet有效的网络连接,以使这种设备在没有时钟芯片的情况下与世界时间保持同步。

RTC:https://en.wikipedia.org/wiki/Real-time_clock