How do I prevent my CountDownTimer from running in background? - java

I am making an Android app that has a time limit to the amount of points you can get. But if you close the app, the timer keeps going. How do I pause the CountDownTimer when the app pauses?

You can cancel it in onPause() with something like
#Override
public void onPause()
{
super.onPause();
timer.cancel(); // timer is a reference to my inner CountDownTimer class
timer = null;
}
And use the millisUntilFinished variable to save in a SharedPreference or some other persistent variable. Then use that variable again to start the timer in onResume()
Shared Prefs
This answer may be helpful if you need to pass the value to another Activity.

Related

How to keep the countDownTimer counts after i swap the activity?

i need a countDownTimer keep running when i swap between activities.. i have more than one activity, i put the countDownTimer in the main activity but when i swap to another activity and back to the main activity it turns back to count again from the start, i believe because the method countDownTimer is onCreate method.
So, how should I go about doing this?
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
questionTime();
}
public void updateTimer(int secondsLeft){
int minutes = (int) secondsLeft / 60;
int seconds = secondsLeft - minutes * 60;
String secondString = Integer.toString(seconds);
timerTextView.setText(Integer.toString(minutes) + ":" + secondString);
}
private void questionTime(){
new CountDownTimer(10000, 1000){
#Override
public void onTick(long millisUntilFinished) {
updateTimer((int) millisUntilFinished / 1000);
}
#Override
public void onFinish() {
timerTextView.setText("0:00");
Log.i("finished", "timer Done");
}
}.start();
}
Update: That helped me to reach my purpose How to run CountDownTimer in a Service in Android?
Maybe this is a little far fetched, but the way that I think to solve this issue and not worrying for the Activities is using an IntentService.
Even if you store some sort of value in the Bundle of the onSaveInstance() hook method this can lead to some pretty messy results if you enable the "Don't keep activities" flag in the device's settings.
What I would do is create an IntentService that when It's triggered starts the countdown, then It broadcast the changes of that countdown through EventBus/Otto/BroadcastReceiver back to the UI.
Another way of doing it is having the countdown instance in your Application class, and check it from there.
I would go with the IntentService solution because having a countdown instance running in the Application class sounds a little off.
Let me know if you want any specifics on how to implement the IntentService but a little bit of Googling should show you how to do it.
As soon as the time starts, write the time (unix timestamp) to properties file. And when the user comes back to your main activity, read the properties file and compare it the time in the properties file with current timestamp and update the timer based on that.

Updating Destroyed Activity UI from Handler Runnable

The following code updates a TextView till a certain condition evaluates to false and then the Handler postDelayed is not called further.
However if the activity is destroyed, it'll try to update a null TextView, what is the right way to handle this? I know I could put a defensive null check on TextView but that is still not truly thread safe.
#Override
protected void onCreate(Bundle savedInstanceState) {
Handler durationHandler = new Handler();
durationHandler.postDelayed(updateSeekBarTime, 50);
}
private Runnable updateSeekBarTime = new Runnable() {
public void run() {
timeElapsed = mediaPlayer.getCurrentPosition();
double timeRemaining = finalTime - timeElapsed;
timeLeft.setText(String.format("%d", TimeUnit.MILLISECONDS.toSeconds((long) timeRemaining)));
if (timeRemaining >= 1000)
durationHandler.postDelayed(this, 200);
}
};
In other words the updateSeekBarTime can at any execution point try to access data members of an already destroyed activity, how to prevent that?
Start your handler in onResume().
In onPause() stop the handler with
handler.removeCallbacksAndMessages(null); //null removes everything
I'm building my own Music Player app and I use
durationHandler.removeCallbacks(updateSeekBarTime);
in onStop(). It works for me.
EDIT:
The above line is helped by the fact that I prevent the Activity from being destroyed by using
#Override
public void onBackPressed() {
moveTaskToBack(true);
}
This ensures that the Activity is minimized instead of destroyed so that when opened again, it's very snappy.
EDIT 2:
private Runnable updateSeekBarTime = new MyRunnable();
private class MyRunnable extends Runnable {
private boolean dontWriteText = false;
#Override
public void run() {
timeElapsed = mediaPlayer.getCurrentPosition();
double timeRemaining = finalTime - timeElapsed;
if(!dontWriteText)
timeLeft.setText(String.format("%d", TimeUnit.MILLISECONDS.toSeconds((long) timeRemaining)));
if (timeRemaining >= 1000)
durationHandler.postDelayed(this, 200);
}
public void dontWriteText() {
dontWriteText = true;
}
};
Then call updateSeekBarTime.dontWriteText() in onDestroy() or onStop(). I'd prefer onStop().
So after some code searching and reading blogs I found the answer in sample code of Communicating with the UI Thread
Even though you can and should be removing callbacks from handler:
handler.removeCallbacksAndMessages(null)
But the above does not prevent the existing running Thread from accessing a destroyed Activity or its views.
The answer I was looking for is WeakReference.
Create a weak reference to the TextView or UI element that you'll
access. The weak reference prevents memory leaks and crashes, because
it automatically tracks the "state" of the variable it backs. If the
reference becomes invalid, the weak reference is garbage-collected.
This technique is important for referring to objects that are part of
a component lifecycle. Using a hard reference may cause memory leaks
as the value continues to change; even worse, it can cause crashes if
the underlying component is destroyed. Using a weak reference to a
View ensures that the reference is more transitory in nature.
You should still check for null reference but now the view will be set to null by the active thread/runnable so you will not face a race-condition.

Save timer time and resume on onResume

I'am a beginner.
I have a timer in my puzzle game. But there is a bug. When the user turns off the screen, the timer doesn`t stop. If user turns screen on again he can see game over screen and this is not good from UX perspective.
Here is my code for timer
private void initializeProgressBar() {
//initialize progressbar
progress = ApplicationConstants.GAME_TIME;
mProgress = (ProgressBarDetermininate) findViewById(R.id.progressDeterminate);
mProgress.setMax(progress);
mProgress.setProgress(progress );
timer = new Timer();
progressBarUpdateTask = new ProgressBarUpdateTask();
timer.schedule(progressBarUpdateTask, 20, 20);
}
class ProgressBarUpdateTask extends TimerTask {
#Override
public void run() {
runOnUiThread(new Runnable(){
#Override
public void run() {
progress-=1;
if(progress==0)
{
TimeOver();
}
mProgress.setProgress(progress);
}
});
}
#Override
protected void onResume() {
super.onResume();
}
#Override
protected void onPause() {
super.onPause();
this.timer.cancel();
}
I can`t figure out how to fix this onResume, because now timer stops when user turn off the screen, but doesnt resume when user turn on screen.
Then you should pause the timer in your onPause() method not cancel it and in the onResume() method you could restart the timer with the seconds which were left, before the screen has been turned off. For further informations about the lifecycle of an activity have a look at the Activity Lifecycle.
But what I think you really need is the CountDownTimer class not the Timer class.
look into CountDownTimer. The timer you are using now is not supposed to really be used like that. Unfortunately there is no pause so onTick save a variable to know what the timer is currently at, then on pause cancel the timer. then in onresume create a new time and set the time to the variable you are using to keep track of the last timer.
http://developer.android.com/reference/android/os/CountDownTimer.html

Android Thread Allocation - growing heap?

Hi everyone out there,
i am developing an android application against API 7 at the moment in which i use an activity which need to be restarted. Lets say my activity looks like this:
public class AllocActivity extends Activity implements OnClickListener{
Button but;
private Handler hand = new Handler();
#Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_alloc);
but = (Button) findViewById(R.id.button);
but.setText("RELOAD");
but.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0){
Intent intent = getIntent();
startActivity(intent);
finish();
}
});
}
#Override
protected void onDestroy(){
super.onDestroy();
System.gc();
}
/****** THREADS AND RUNNABLES ******/
final Runnable fullAnim = new Thread(new Runnable(){
#Override
public void run(){
try{
hand.post(anim1);
Thread.sleep(2000);
hand.post(anim2);
Thread.sleep(1000);
// and so on
}catch(InterruptedException ie){ie.printStackTrace();}
}
});
final Runnable anim1 = new Runnable() {
#Override
public void run(){
// non-static method findViewById
ImageView sky = (ImageView)findViewById(R.id.sky);
}
};
}
The problem is that the gc doesnt seem to free the fullAnim thread so that the heap is growing by ~100K at every restart - till it slows down and crashes. Declaring fullAnim as static does solve this problem - but as i use non static references this doesnt work out for me.
So at this point i am kindof lost - and i hope u can advice me where to go next. Is there something i might be doing wrong or is there a tool i can use to manage threads to drop and free heap after restart.
kindly regards
UPDATE
thanks to everyone who answered - helped alot. using TimerTask did the trick in the end. i did the following change:
/****** THREADS AND RUNNABLES ******/
final TimerTask fullAnim = new TimerTask(){
#Override
public void run(){
try{
hand.post(anim1);
Thread.sleep(2000);
hand.post(anim2);
Thread.sleep(1000);
// and so on
}catch(InterruptedException ie){ie.printStackTrace();}
}
};
as the activity was more than 6k loc long this was a pretty decent solution without facing bigger impacts. KUDOS!
i dont use a Timer to shedule the task - dont know if its bad practice but
the animation is called like this:
Thread t = new Thread(fullAnim);
t.start();
A running Thread is never garbage collected.
A Thread is not stopped automatically if your Activity stops or is destroyed. It could run forever.
Every non-static inner class keeps a reference to the enclosing instance. E.g. hand.post(anim1); works inside that inner class because it has an implicit reference to AllocActivity.this.
So what you effectively do is to keep a reference to your Activity alive for longer than it is supposed to be alive, i.e. until after onDestroy.
Make sure to stop threads manually if you don't want them anymore.
Because final variable have low priority for GC. So you need to explicitly release the runneable objects in onPause() method because there is not ensurence onDestory() will call immediate after finish() call .
#Override
protected void onPause(){
super.onPause();
//cancel timer to stop animations
if(t!=null){
t.cancel();
}
System.gc();
}
UPDATE
use timer to achieve this
boolean isFirstAnim=true;
Timer t = new Timer();
t.schedule(new TimerTask() {
#Override
public void run() {
if(isFirstAnim){
// play your first animation at every
}else{
// play your second animation at every
}
}
}, 0, 3000);
What happens when all activities of an application finishes?
"When you call finish() this doesn't mean the Activity instance is
garbage collected. You're telling Android you want to close the
Activity (do not show it anymore). It will still be present until
Android decides to kill the process (and thus terminate the DVM) or
the instance is garbage-collected."
You need to implement your own stop method to stop the running thread, you can make a call to it in onDestroy
refer this Stopping a runnable
Alternatively
you can perform your operation in an asynctask and use onProgressUpdate() to publish progress on UI thread and use cancel(true) in combination with check in doInBackground() whether cancel has been called to stop the task.

Easiest way of creating a timer in android?

Im trying to make a timer for a game, and I'm stuck atm.
This is my code so far. I made a new activity for the intent, cause I read somewhere that's one way to do it.
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//Timer timer = new Timer();
//task = new TimerTask() {
Button b1 = (Button) findViewById(R.id.button1);
b1.setOnClickListener(new View.OnClickListener() {
public void onClick(View v){
startActivity(new Intent("com.loltimer.Timer"));
}
});
};
}
When I press the first button I want to have a timer that goes from 5minutes down to 0. And at 0 I want a sound to be played.
Thanks in advance!
Has got to be the Android count down timer class: http://developer.android.com/reference/android/os/CountDownTimer.html
Timing operations are best handled by a Handler in Android.
http://developer.android.com/reference/android/os/Handler.html
You can wrap the operation you want to run in the future as a Runnable and pass it to postDelayed(). If the operation needs to run repeatedly on that interval, you can call postDelayed() at the end of the Runnable to schedule the next run.
CountdownTimer can also be useful if you need to run some code a finite number of times on a regular interval. This class is actually just a thin wrapper around Handler.

Categories