Unfortunately my architecture does not work so i need your help.
The Problem which i want to solve:
I programmed a mini game in which you can select exercises like "run for 1 minute".
showing countdown anywhere in app: fragment, activity, actionbar...
do task after finish
countdown should run ahead if leaving the fragment
countdown should with the right progress again if i go back to the fragment
I tried to solve the Problem while implementing CountDownTimer. But if the countDownTimer is part of the fragment it will be destroyed if i leave the fragment. When i come back the countdowntimer begins from the beginning...
So i thought about a thread... but how can i display the countdown in the fragment and anywhereelse i want?
Will the thread alive until he is done? Even if i close the application?
So can you name please some keywords i can search for to solve the problem. Or instead describing a solution?
Thanks for your advices ;)
You should probably put your counter in a Service which will run as a remote process and bind to that service everywhere you need to display the timer, then use the observer pattern to call a listener callback from you service CountDownTimer's onTick method whenever you you want to update the observers:
mCountDownTimer = new CountDownTimer(startTime, 1000) {
//update after each tick
public void onTick(long millisUntilFinished) {
long seconds = (millisUntilFinished / 1000) % 60;
long minutes = ((millisUntilFinished / 1000) / 60) % 60;
long hours = (((millisUntilFinished / 1000) / 60) / 60) % 24;
long days = (((millisUntilFinished / 1000) / 60) / 60) / 24;
if(listeners!=null){
Log.d(TAG, "days:"+days + "hours:"+ hours + "minutes:"+ minutes + "seconds:"+ seconds);
listeners.onTimeChanged(days, hours, minutes, seconds);
}
}
public void onFinish() {
if(listeners!=null){
Log.d(TAG, "timer finished");
listeners.onTimerFinish();
}
}
}.start();
I wouldn't try to make an actual countdown, because your threads should die when the appli leave and that seems really complex.
Instead, write somewhere (sqlite db ? file ?) the start time and the duration of the countdown running.
Whenever you need to display it, query it, and do the needed process to update view, whatever view is in place. That answers easily (for me) all you specifications, without the threading burden, and only uses a tiny amout of disk space. Plus you can save all your countdown specific data without any problem, which will probably prove useful.
Related
How to update widget per second ?
I used AlarmManager but it only works on below kitkat version.
Here, i am making user wait for 30 seconds on a screen and hiding the view and hitting service when it finishes. You can customize according to your need.
CountDownTimer timer = new CountDownTimer(31000, 1000) {
#Override
public void onTick(long millisUntilFinished) {
timer_text.setText("Remaining Seconds. " + millisUntilFinished / 1000 + " s");
if (count >= 30) {
} else {
count++;
}
}
#Override
public void onFinish() {
timer_text.setVisibility(View.GONE);
/// VOLLEY SERVICE
}
}.start();
AlarmManager cannot have a timeout less than 20 seconds. Any timeout less than that will be rounded up.
First off, I would question whether you actually need it updated every second. That's an incredible amount of processing power used, and pretty much against the idea of a widget. If it needs to be updated that frequently it should be an app. If your updates are coming from a server and you want to display them immediately, look into push messaging instead. Then you only need to update when you receive a message, no alarms or timers needed.
Secondly- the way you do a timer for short periods like that is via Handler.postDelayed. It can have a much smaller resolution. However it may not run if the phone is asleep.
Let's say I have this variable:
long myMillis = 20000;
This means that I want my Chronometer to start at exactly 20 seconds (00:20).
I tried doing this:
chronometer.setBase(myMillis);
But it doesn't work. It dosn't start with 20 seconds. It starts with some weird time that doesn't make sense.
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)))
so make it:
mChronometer.setBase(SystemClock.elapsedRealtime() - (2* 60000 + 0 * 1000)))
For Kotlin,
To start Chronometer with starting time 20 seconds, you can use
val timeInMilSeconds = 20000
chronometer.base = SystemClock.elapsedRealtime() - timeInMilSeconds
chronometer.start()
This will start Chronometer with starting time 20 seconds i.e. 00:00:20
Its Late but may help others.
I have used following code in first fragment
chronometerTimer.setBase(SystemClock.elapsedRealtime());
chronometerTimer.start();
and then move on some condition to next fragment where chornometer should start at same time of previous chornometer ends, i get elapsed time using this code.
long elapsedMillis = SystemClock.elapsedRealtime() - chronometerTimer.getBase();
and i send elapsedMilis in next fragment and use following code
chronometerTimer.setBase(SystemClock.elapsedRealtime() - elapsedTime);
chronometerTimer.start();
it worked perfectly.
I'm new to Android development.I want to create a stopwatch with precision of 0.01 seconds.Here is part of my code(which I think the problem lies within):
private void runTimer()
{
final TextView timeView = (TextView) findViewById(R.id.time_view);
final Handler handler = new Handler();
handler.post(new Runnable() {
#Override
public void run() {
int seconds = centiseconds / 100;
int centisecs = centiseconds % 100;
int hours = seconds / 3600;
int minutes = (seconds % 3600) / 60;
int secs = seconds % 60;
String time = String.format("%d:%02d:%02d.%02d", hours, minutes, secs, centisecs);
timeView.setText(time);
if (isRunning) {
centiseconds += 1;
}
handler.postDelayed(this, 10);
}
});
}
}
Since postDelayed method's delay is in milliseconds, ten times of it would be 1 centiseconds. So I'm incrementing my centiseconds variable every 10ms.So far so good.
But when I test my app on my device, it seems the seconds are ticking slower than they should. Is is probable that the codes corresponding to division and modulo operations cause so much lag that hinder the increment and reduce the accuracy?
I've rewritten the app for 0.1 seconds(deciseconds) (by: centiseconds / 10 and % 10 and postDelayed(..., 100) ) and it seems it is ticking correctly.
P.S.
Is this the reason my 4.3 Jellybean's Stopwatch has 0.1 seconds accuracy?
What is the limit of precision in android for such app? ( Timely's has 0.01 seconds so I think it is at least 0.01 seconds)
This is wrong approach as you cannot rely on system message queue as source of precise ticks as it does NOT guarantee any precision in delivery. postDelayed() queues your runnable to be delivered no sooner than now + delay but for precise delivery is not quaranteed and additional delays can happen for many reasons which in longer run would give you noticeable cumulative error in measurements.
You can however use postDelayed() to update your UI, but to know how much time passed you should use system clock methods, not own counters.
You also should fire your runnable at least twice per precision, i.e. if you want to update timer display once per minute, you should "tick" twice per minute so if there'd be any delay in message queue handling your UI shall still get at least one tick per second on time.
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()
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