System.currentTimeMillis() возвращает неверную метку времени на Huawei
Проблема заключается в том, что System.currentTimeMillis()
возвращает неверные миллисекунды с разными диапазонами времени, главным образом в будущем, иногда до 6 месяцев, но варьируется от нескольких секунд до нескольких месяцев.
Устройство, в котором это происходит, - это модель планшета Huawei M2-A201W на android 5.1.1, версия ядра: **3.10.74-gdbd9055**
Мое первое предположение заключалось в том, что NTP каким-то образом испортился со временем, но у меня есть тысячи этих планшетов, а некоторые из них не имеют сетевого подключения, нет SIM-карты, поэтому нет GSM/3G/4G.
Im использует System.currentTimeMillis()
для сохранения в столбце таблицы, где была строка, созданная в локальной базе данных sqlite.
Это аномально происходит очень часто (30% каждого вызова System.currentTimeMillis()
) на планшетах, которые я использую.
Ответы
Ответ 1
Как обходной путь для использования 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
Ответ 2
API-интерфейс java API System.currentTimeMillis()
на платформе Android использует POSIX api gettimeofday
, чтобы получить время в миллисекундах. См. здесь.
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 с вашей собственной реализацией, как показано ниже. Вызовите эти API POSIX по порядку, если 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);
Ответ 3
Как получить выборку временной метки, выполнив команду "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());
Это даст вам простой формат даты. например Вт Авг 08 16:15:58 GMT + 06: 00 2017
Ответ 4
Если у вас нет SIM-карты, поэтому нет GSM/3G/4G, ваш телефон не сможет обновить правильное время на основе предоставленного в сети времени/зоны.
![введите описание изображения здесь]()
Итак, устройства с сетью показывают правильное время, в то время как другие устройства без сети могут показывать неправильное время - вам нужно вручную установить правильное время. System.currentTimeMilis() считывает время из вашей системы. g Но при включении питания часы работают.
Проверьте, заблокирован ли NTP (UDP-порт 123) приложениями с помощью Socket или DatagramSocket. Примечание. NTP применяется к сценарию, в котором часы всех хостов или маршрутизаторов в сети должны быть одинаковыми. Если ваше устройство переключается на две (или более) разные сети и получает время, обновляемое из разных источников, оно может меняться во времени.
В конечном счете, ваше системное время меняется, поэтому он колеблется. Если вы вручную System.currentTimeMilis() после ручного отключения автоматической даты и времени, я считаю, что он не колеблется (нет аномалий). Если это так, то ваш планшет Huewai не содержит ошибок.
Ответ 5
Поскольку вы упомянули, что большинство звонков получают правильное время, и это происходит только в 30% случаев, я бы создал приемник для трансляции ACTION_TIME_CHANGED
и ACTION_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);
}
Ответ 6
Есть ли у этого устройства реальный аппаратный 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 (и вы не указали на весь буфер сообщений ядра) не было, тогда вы получите ответ. У вас нет часов, чтобы держать время на этих устройствах. Вы всегда будете нуждаться в NTP и рабочем сетевом соединении с Интернетом, чтобы поддерживать такое устройство без синхронизации часов с мировым временем.
RTC: https://en.wikipedia.org/wiki/Real-time_clock