System.nanoTime () gives wrong times in Android
I am developing a game in which some events happen after a certain amount of time. To do this, I need to calculate how much time has passed since the start of the game. I do this by spawning a thread in the main loop and calculating the elapsed time using System.nanoTime ().
I have tested the code on my smartphone and it works very well until I press the sleep button putting the phone in sleep (which calls the onpause () method for the activity). But sometimes, when I press the sleep button, very strange things happen: System.nanoTime () seems to give times longer than they actually are (never less).
I tried using System.currentTimeMillis () instead of System.nanoTime (), but it's even worse (I know there were problems with System.nanoTime () on Windows systems with multi-core devices; my smartphone is multi-core too).
Here is the code:
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
public class MainActivity extends Activity implements Runnable {
private Thread gameThread = null;
private volatile boolean running = false;
private long startTime;
private long totalTime;
private boolean[] afterNSeconds = new boolean[11];
private static long oneBillion = 1000000000;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TextView tv = new TextView(getApplicationContext());
tv.setText("Start");
setContentView(tv);
}
@Override
public void onResume() {
super.onResume();
running = true;
gameThread = new Thread(this);
gameThread.start();
}
@Override
public void onPause() {
super.onPause();
totalTime = System.nanoTime() - startTime;
running = false;
while (true) {
try {
gameThread.join();
return;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public void run() {
startTime = System.nanoTime();
while (running) {
long elapsedTime = totalTime + System.nanoTime()
- startTime;
for (int j = 1; j <= 10; j++) {
if (elapsedTime >= j * oneBillion
&& !afterNSeconds[j]) {
Log.d("MainActivity", j + ": elapsedTime = "
+ elapsedTime );
afterNSeconds[j] = true;
}
}
}
}
}
Here's the normal output when I don't press the sleep button:
01-07 23:56:40.722: D/MainActivity(26579): 1: elapsedTime = 1021247230 01-07 23:56:41.708: D/MainActivity(26579): 2: elapsedTime = 2007078538 01-07 23:56:42.701: D/MainActivity(26579): 3: elapsedTime = 3000023615 01-07 23:56:43.701: D/MainActivity(26579): 4: elapsedTime = 4000000538 01-07 23:56:44.701: D/MainActivity(26579): 5: elapsedTime = 5000000846 01-07 23:56:45.701: D/MainActivity(26579): 6: elapsedTime = 6000001230 01-07 23:56:46.701: D/MainActivity(26579): 7: elapsedTime = 7000001384 01-07 23:56:47.701: D/MainActivity(26579): 8: elapsedTime = 8000001923 01-07 23:56:48.701: D/MainActivity(26579): 9: elapsedTime = 9000000769 01-07 23:56:49.701: D/MainActivity(26579): 10: elapsedTime = 10000002000
And here is the abnormal output when I pressed the sleep button after 3 seconds and pressed again after a few seconds. (Abnormal behavior 4 to 6). As you can see, after less than 4 seconds, System.nanoTime () reports 6 seconds instead.
01-08 00:02:48.645: D/MainActivity(26579): 1: elapsedTime = 1001408231 01-08 00:02:49.644: D/MainActivity(26579): 2: elapsedTime = 2000000615 01-08 00:02:50.644: D/MainActivity(27265): 3: elapsedTime = 3000000539 01-08 00:02:50.802: D/MainActivity(27265): 4: elapsedTime = 6000000462 01-08 00:02:50.802: D/MainActivity(27265): 5: elapsedTime = 6000000462 01-08 00:02:50.802: D/MainActivity(27265): 6: elapsedTime = 6000000462 01-08 00:03:21.358: D/MainActivity(27265): 7: elapsedTime = 7000000770 01-08 00:03:22.358: D/MainActivity(27265): 8: elapsedTime = 8000000539 01-08 00:03:23.358: D/MainActivity(27265): 9: elapsedTime = 9000000154 01-08 00:03:23.730: D/MainActivity(27265): 10: elapsedTime = 10000003847
How can I change the code to avoid this?
source to share
SystemClock - the appropriate clock for use on android
In particular, SystemClock.elapsedRealtime()
it gives the time from the moment the system boots, which (unlike System.currentTimeMillis()
) does not depend on hourly changes / user actions and (unlike uptimeMillis()
) includes time in deep sleep
source to share