How to make Android custom clock time accurate? - android

I made a appWidget which show the current time of the server(2012-08-29, 12:00:08 for example). I request the server time every fix duration(1 hour for example). If receives the server time, updates the appWidget display. During the duration, I launch a Handler to update the time like this:
mTick = new Runnable() {
public void run() {
mMillis += 1000;
long now = SystemClock.uptimeMillis();
long next = now + (1000 - now % 1000);
mHandler.postAtTime(mTicker, next);
}
}
mTicker.run();
My questions:
1 After a long time(one day elapsed), The time displayed in AppWidget is slow than the real server time.
I doubt that my method used above is not accurate enough to update the time.
Any suggestions about this problem?

You should not use SystemClock.uptimeMillis() because it does not include time spent in deep sleep, thats why your app widget is out of sync.
You should use SystemClock.elapsedRealtime() call instead
Upd: sorry, I think I misunderstand the problem here. What you are trying to do is to use postAtTime to post runnable after some time in future. Please notice that postAtTime does not include time when device is in deep sleep.
What you need is to track accurate ammount of deltas between redrawing of your widget. You should use SystemClock.elapsedRealtime() for that.
Algorithm should be like this:
long serverTime = getServerTime();
long lastTime = SystemClock.elapsedRealtime();
// Somewhere in updateWidget() or call on timer:
serverTime = serverTime + SystemClock.elapsedRealtime() - lastTime;
lastTime = SystemClock.elapsedRealtime();
// At this moment in serverTime variable you have "server" time adjusted by the time which passed on device, including time spent in deep sleep

Related

Mana recovery issue

We're making a game in Android Studio and we got stuck. The resource (mana) used for specific spells should recover on time, e.g. 1 mana point per 5 minutes. We don't really get how to make it recover while the game is off. Is there a method to check current date/time and count the amount of mana replenished? Converting date and time to String and comparing it with the new date/time seems to be an "exciting" work to do, but we would bypass these mechanics if there is a way.
Thank you in advance.
The best way to do this in the background is to register a receiver in your manifest. This means the receiver will keep listening for broadcasts even if the app is off.
What you need is this particular action when registering your receiver Intent.ACTION_TIME_TICK
There is a more detailed answer about this matter here Time change listener
Another solution is to use the Calendar class in java. With it you can get the exact minutes passed from a point in the past to this moment. This way you don't have to worry about parsing dates and similar. I can't provide you specific examples because me myself have not used the Calendar class very much, but I'm sure you can find lots of stuff in the official documentation and on stackoverflow about it.
No need to work with Date objects, the simple usage of System.currentTimeMillis() should work. Here's a basic outline:
long mLastManaRefreshTime = System.currentTimeMillis();
void refreshMana()
{
long timeDelta = System.currentTimeMillis() - mLastManaRefreshTime;
mLastManaRefreshTime = System.currentTimeMillis();
float totalManaToRefresh = (float)AMOUNT_TO_REFRESH_IN_ONE_MINUTE * ((float)timeDelta / 60000f);
mMana += totalManaToRefresh;
if (mMana > MAX_MANA)
mMana = MAX_MANA;
}
This method is of course just an outline. You will need to call this once every update cycle. It will calculate how much time passed since the last time refreshMana was called, and replenish the required amount.
If you need this to work while the game is off, you can save the mLastManaRefreshTime to a SharedPreferences object and reload it when the game loads up again.
With System.currentTimeMillis() you can a current time-stamp in milliseconds.
You could save the latest time-stamp in your Preferences with every 5 min tick of the running game. For the other case, when your App comes back from a state where it does not do this (i.e. called the first time, woken up etc.).
Something like this:
int manacycles = ((int) (((System.currentTimeMillis() - oldtimestamp) / 1000) / 60) ) % 5;
would give you the number of Mana points you would have to add.
Alternately you could do the same thing with the Calendar class.
Also keep in mind players could cheat this way by simply changing their time. If your game is online you could get the time from the internet, with something like this:
try {
TimeTCPClient client = new TimeTCPClient();
try {
// Set timeout of 60 seconds
client.setDefaultTimeout(60000);
// Connecting to time server
// Other time servers can be found at : http://tf.nist.gov/tf-cgi/servers.cgi#
// Make sure that your program NEVER queries a server more frequently than once every 4 seconds
client.connect("nist.time.nosc.us");
System.out.println(client.getDate());
} finally {
client.disconnect();
}
} catch (IOException e) {
e.printStackTrace();
}

Android: Chronometer SetBase in minutes

Is there a way by which using the android chronometer class to set base of the chronometer in 15 minutes and from that period the times goes down until 0 seconds?
I have tried with setBase(60000) but this isn't work.
Check out this thread Android: chronometer as a persistent stopwatch. How to set starting time? What is Chronometer "Base"? as well as this thread Android - Get time of chronometer widget. Neither answers your question directly, but the nuggets there should lead you to an answer.
In general the chronometer works like this (if you would like to set the Base to a specific nr):
mChronometer.setBase(SystemClock.elapsedRealtime() - (nr_of_min * 60000 + nr_of_sec * 1000)))
what you are asking can be done through a countdown (http://developer.android.com/reference/android/os/CountDownTimer.html)
Or create your own countdown by using the chronometer like this (more work should be done cause i just wrote this and did not test it yet)
private OnChronometerTickListener countUp = new OnChronometerTickListener(){
#Override
public void onChronometerTick(Chronometer chronometer){
long elapsedTime = (SystemClock.elapsedRealtime() - mChronometerCountUp.getBase()) / 60000;
Log.v("counting up", elapsedTime);
// you will see the time counting up
count_down--;
if(count_down == 0){
mChronometerCountUp.stop();
}
// an int which will count down,
// this is not (very) accurate due to the fact that u r using the update part of the chronometer
// u just might implement the countdown i guess
// or 2 chronometers (one counting up and an other counting down using the elapsed time :p)
// just remember programming is creating ur solution to problems u face its like expression urself
};
};
http://developer.android.com/reference/android/widget/Chronometer.html
For set the base time you can use elapsedRealtime(), and you can output format with setFormat()

System.currentTimeMillis() work wrong

I need stopWatch and I used http://www.goldb.org/stopwatchjava.html
It did not work well so I tried write out the value every 1000ms:
stopWatch.start();
HandlerScrollBar.postDelayed(TtScroll, 1000);
private Runnable TtScroll = new Runnable() {
public void run() {
long time = stopWatch.getElapsedTime();
HandlerScrollBar.postDelayed(TtScroll,(long) 1000);
Log.d(TAG, Long.toString(time));
}
};
I can see value of time every second in CatLog and this is result:
Real time is max +5ms but in Column it is at least +3 seconds! How is it possible? It is the same with
new Date().getTime().
Is there some StopWatch class which will pass this test as expected?
Thank you.
If you are measuring elapsed time, and you want it to be correct, you must use System.nanoTime(). You cannot use System.currentTimeMillis(), unless you don't mind your result being wrong.
The purpose of nanoTime is to measure elapsed time, and the purpose of currentTimeMillis is to measure wall-clock time. You can't use the one for the other purpose. The reason is that no computer's clock is perfect; it always drifts and occasionally needs to be corrected.
Since nanoTime's purpose is to measure elapsed time, it is unaffected by any of these small corrections.I would suggest to pick the nanoTime() as it has better accuracy in those microcalculations.
for extremely precise measurements of elapsed time. From its javadoc:
long startTime = System.nanoTime();
// ... the code being measured ...
long estimatedTime = System.nanoTime() - startTime;
Seems impossible. I've never had System.currentTimeMillis() act that way. Also, you're logging out as Log.d() but the logcat you show indicates a Log.e(). You sure that's the right logcat?

Precision of delay

I have a problem with this code used for Android (Java)
handler.postDelayed(new Runnable(){
public void run(){
// Your code goes here...
}
}, 500);
If the delay is about 500ms then the program seems to repeat the task at 0.5s, but if I change to less than 100ms or even less it does not follow any more. I test the brightness change and for a while it can repeat the change of brightness at that rate, but then slow down and come back to normal flash rate again. It seems unstable. Do you have any code that give exact delay regardless of the load of the phone's CPU.
Many thanks
Not from Java, no; stock Java isn't a real-time system.
Timing precision is subject to the whims of the JVM and the OS's scheduler. You may be able to get incrementally more precise, but there's no guarantee of the kind of precision you're looking for.
You might be able to do something more precise if you use a CountDownTimer which has a periodic tick. Essentially you set it to count down for a period which can be hours if need be, and there are two methods one method is called on each tick, and the other at the end of the timer at which point you could start another one. Anyway you could set the tick to be very fast, and then only kick off the code at the delay point by check the actual time difference in the click. I think thats about the best you could do. Essentially inside the tick you would issue a signal if the right amout of time had actually passed. That signal would either kick off the thread or release something the already running thread was waiting on. What is the value of the CountDownTimer, I guess its just that you can do a very frequent polling, and elapsed time check. Although its not guaranteed, the time between the ticks you can set it to a high frequency and check/poll very frequently. This could lead to a smooth performance not unlike a realtime system. Its more likely to be accurate because its just issuing a signal and not taking up the resources of threading just to issue the signal. You might also try an IntentService to perform the tasks and just call startService(intentToIntentService) each call. See if the threading works better inside a service like IntentService which does queue them up I believe.
Date startDate = new Date();
long startTime = startDate.getTime();
// Tick called every 10th of a second. OnFinish called at Signal.
CountDownTimer ctDownTimer = new CountDownTimer(30000, 100) {
long startIntervalTime=startTime;
public void onTick(long millisUntilFinished) {
Date now = new Date();
long nowTime = now.getTime();
if ((startIntervalTime - nowTime) > 100)
{
issueSignal();
intervalStartTime=nowTime;
}
now=null;
}
public void onFinish() {
Log.d("MyClass", "Done") // Maybe start out.
}
}.start();

Metronome Timer slowdown (via Handler, or Threads too)

I have a simple, classic, statement that every 200 milliseconds plays a sound (a metronome).
I wrote it using Handlers, and then in another way, using Threads.
The problem is the same in both ways: when I press hardware home button, or also simply when I press a button to open a ListView, the metronome terribly slowdown for a while.
This problem (not so strong, but however present) presents also doing nothing and leaving the application in foreground.
Any ideas?
Here's the code:
public class Metronome implements Runnable{
private Handler mHandler = new Handler();
public static long mStartTime;
Main mainContext;
public Metronomo(Main context) {
mainContext = context;
}
public void play() {
mStartTime = System.currentTimeMillis();
mHandler.postDelayed(this, 100);
}
public final void stop(){
mHandler.removeCallbacks(this);
}
public void run(){
//play the ogg file in position 1
mSoundManager.playSound(1);
//reschedule the next playing after 200ms
mHandler.postAtTime(this, SystemClock.uptimeMillis() + 200);
}
};
Are you using some kind of pause statement to wait between beats? You could try basing the timing on multiples of a system clock value instead. That way you may still get beats that occur late (or not at all) but you wouldn't get a slow down. Hope that makes some kind of sense.
This is more of a comment but I don't have enough rep to leave comments just yet.
My phone seems to be able to play midi files, which are a pretty compact way to represent sound, perhaps you could dynamically create one and use that for the metronome? I'm assuming that the synthesis is handled at a lower level than would ordinarily be accessible to you so that the timing would be better, but I don't know that for a fact.
When this play sound is called
mSoundManager.playSound(1);
Android waits until that call is finished, then you call
mHandler.postAtTime(this, SystemClock.uptimeMillis() + 200);
however, if you reverse those calls, you may find that the timing is more accurate.
mHandler.postAtTime(this, SystemClock.uptimeMillis() + 200);
mSoundManager.playSound(1);
You can't count on your sound taking exactly the same amount of time to play, so telling the handler to post first is a bit better. Still not ideal, however.
Another consideration is that you're re-computing uptime and adding some more time to that (200 in this case). Why not use the modulus operator on your uptime, to ensure that your next requested post time is more precisely scheduled?
long divisions = SystemClock.uptimeMillis() % 200; // precisely scheduled event timings since system boot.
long nextDivision = divisions + 1; // the next desired event timing
mHandler.postAtTime(this, nextDivision * 200); // scaled back up to number of milli seconds
// now do more heavy lifting that would otherwise have affected uptimeMillis call
mSoundManager.playSound(1);

Categories

Resources