android - System.currentTimeMillis() devuelve una marca de tiempo incorrecta en Huawei
linux ntp (6)
El problema es que System.currentTimeMillis()
devuelve los milisegundos incorrectos con diferentes rangos de tiempo, principalmente en el futuro a veces hasta 6 meses, pero varía de unos pocos segundos a meses.
El dispositivo en el que está ocurriendo esto es un modelo de tableta Huawei M2-A201W en Android 5.1.1, la versión del kernel es: **3.10.74-gdbd9055**
Mi primer supuesto fue que el NTP estaba de alguna manera desordenado con el tiempo, pero tengo miles de esas tabletas y algunas de ellas no tienen conexión de red ni tarjeta SIM, por lo que no hay GSM / 3G / 4G.
Estoy usando System.currentTimeMillis()
para guardar en una columna para una tabla para cuando se creó una fila en la base de datos sqlite local.
Esto ocurre anomalmente con mucha frecuencia (30% de cada llamada de System.currentTimeMillis()
) en las tabletas que uso.
¿Este dispositivo tiene un hardware real RTC? No pude encontrar una respuesta definitiva de googlear y leer hojas de especificaciones. Una carrera de:
$ dmesg -s 65535 | grep -i rtc
De una concha debería darte una respuesta. Debería ver algo como esto (variará con la versión del chipset y del kernel):
[ 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
Si grep no devuelve ningún mensaje (y usted contra el búfer de mensajes del kernel completo), entonces está su respuesta. No tienes reloj para mantener el tiempo en estos dispositivos. Siempre necesitará NTP y una conexión de red operativa a Internet para mantener ese dispositivo sin un chip de reloj sincronizado con la hora mundial.
¿Qué hay de obtener la marca de tiempo de forma nativa ejecutando el comando "date +% s " en el kernel de Linux?
Aquí, "+% s" es segundos desde 1970-01-01 00:00:00 UTC. (GNU Coreutils 8.24 Manual de fecha)
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();
}
Si imprime esto,
Log.e("unix_time: ", "" + log.toString());
Obtendrá una marca de tiempo Unix, por ejemplo, 1502187111
Para volver a convertirlo en un objeto de fecha, multiplique por 1000, ya que Java está esperando milisegundos,
Date time = new Date(Long.parseLong(log.toString()) * 1000);
Log.e("date_time: ", "" + time.toString());
Esto le dará un formato de fecha simple. por ejemplo, mar. 08 de agosto, 16:15:58 GMT + 06: 00 2017
Como mencionó que la mayoría de las llamadas están recibiendo el tiempo adecuado y que solo ocurre en el 30% de los casos, crearía un receptor para las ACTION_TIMEZONE_CHANGED
intento ACTION_TIME_CHANGED
y ACTION_TIMEZONE_CHANGED
para averiguar cuándo cambia la hora. Tal vez esto te dará una pista de lo que está cambiando el tiempo.
Junto con el ConnectivityManager puede detectar si el dispositivo está conectado y qué tipo de conexión tiene, tal vez alguna conexión esté provocando el cambio de hora.
// 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);
}
Como una solución para el uso de System.currentTimeMillis()
, tal vez pueda dejar que sqlite maneje la creación de la marca de hora y ver si eso resuelve su problema.
Defina / default(strftime(''%Y-%m-%d %H:%M:%f'', ''now''))
"creada" con timestamp default current_timestamp
o con default(strftime(''%Y-%m-%d %H:%M:%f'', ''now''))
si también necesita milisegundos, Me gusta esto:
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
Cuando no tiene una tarjeta SIM, entonces no hay GSM / 3G / 4G, su teléfono no puede actualizar la hora correcta según la zona / zona provista por la red.
Por lo tanto, los dispositivos con la red muestran la hora correcta, mientras que otros dispositivos sin la red pueden mostrar la hora incorrecta, debe configurar manualmente la hora correcta. System.currentTimeMilis () lee la hora de su sistema. g Pero en el turno de poder, el reloj funciona.
Compruebe si NTP (puerto UDP 123) está bloqueado por aplicaciones que utilizan Socket o DatagramSocket. Nota: NTP se aplica al escenario en el que los relojes de todos los hosts o enrutadores en la red deben ser iguales. Si su dispositivo cambia a dos (o más) redes diferentes y se actualiza el tiempo desde diferentes fuentes, puede variar el tiempo.
En última instancia, se está cambiando la hora del sistema, por eso está fluctuando. Si manualmente System.currentTimeMilis () después de desactivar manualmente la fecha y la hora, creo que no fluctúa (sin anomalías). Si este es el caso, entonces su tableta Huewai no tiene errores.
La API de java llamada System.currentTimeMillis()
en la plataforma Android usa la API POSIX gettimeofday
para obtener el tiempo en milisegundos. Ver here
static jlong System_currentTimeMillis(JNIEnv*, jclass) {
timeval now;
gettimeofday(&now, NULL);
jlong when = now.tv_sec * 1000LL + now.tv_usec / 1000;
return when;
}
Se supone que cada llamada a gettimeofday
será exitosa. Supongo que tu problema podría suceder aquí.
Es mejor verificar el valor de retorno de cada llamada a la API y determinar qué hacer a continuación si se produce un error.
Así que sugiero un método más confiable en JNI con su propia implementación, como se muestra a continuación. Llame a estas API POSIX en orden, si no se pudo obtener clock_gettime
, llame a clock_gettime
, si falla nuevamente, llame al 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);