31

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

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

我的第一个假设是NTP不知何故弄乱了时间,但我有成千上万的平板电脑,其中一些没有网络连接,没有 SIM 卡,所以没有 GSM/3G/4G。

我使用System.currentTimeMillis()保存在表的列中,以了解何时在本地 sqlite 数据库中创建行。

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

4

6 回答 6

7

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

在此处输入图像描述

因此,有网络的设备会显示正确的时间,而没有网络的其他设备会显示错误的时间——您必须手动设置正确的时间。System.currentTimeMilis() 从您的系统中读取时间。g 但在开机时,时钟工作。

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

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

于 2017-08-10T06:25:42.653 回答
7

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

timestamp default current_timestamp如果您还需要毫秒,则使用或使用定义/更改您的“创建”列default(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

于 2017-08-07T10:22:46.317 回答
6

System.currentTimeMillis()Android 平台中的 java API 调用使用 POSIX apigettimeofday来获取以毫秒为单位的时间。见这里

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);
于 2017-08-09T04:51:25.837 回答
4

由于您提到大多数呼叫都得到了正确的时间,并且只发生在 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);
}
于 2017-08-11T06:48:45.100 回答
4

通过在 Linux 内核中运行“date +%s ”命令来本地获取时间戳怎么样?

这里,“+%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());

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

于 2017-08-08T10:23:11.653 回答
1

这个设备有真正的硬件 RTC 吗?我无法通过谷歌搜索和阅读规格表找到明确的答案。一连串:

$ dmesg -s 65535 | grep -i rtc

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

[    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

于 2017-08-13T04:25:18.980 回答