onTick method of Countdown timer keeps getting called even after calling cancel - java

I am trying to load some data. Id data is loaded in 20 seconds than i start new activity else i will finish by giving some relevant message. I have started a countdownTimer to keep track of time. Once data is loaded, I want to stop the timer. I have Following class :
public class SplashActivity extends AppCompatActivity {
private Context mContext;
private Boolean mDataLoadedFromServer = false;
private String mJSONData;
private SplashTimerForLoadingMasterDataForAllChannels mTimer;
private void stopTimer(){
mTimer.cancel();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
setContentView(R.layout.activity_splash);
mTimer = new SplashTimerForLoadingMasterDataForAllChannels(20000,1000);
mTimer.start();
}
class SplashTimerForLoadingMasterDataForAllChannels extends CountDownTimer {
public SplashTimerForLoadingMasterDataForAllChannels(long millisInFuture, long countDownInterval) {
super(millisInFuture, countDownInterval);
Log.d("testTimer", "SplashTimerForLoadingMasterDataForAllChannels");
//SomE AsyncTAsk
LoadData loaddata = new LoadData();
loaddata.execute();
//SomE AsyncTAsk
}
#Override
public void onTick(long millisUntilFinished) {
Log.d("testTimer", "onTick millisUntilFinished = " + millisUntilFinished + " mDataLoadedFromServer = " + mDataLoadedFromServer);
//mDataLoadedFromServer is modified once Data is loaded in AsyncTask
if(mDataLoadedFromServer) {
stopTimer();
}
}
#Override
public void onFinish() {
Log.d("testTimer", "onFinish");
if(mDataLoadedFromServer) {
mDataSavedAndNextActivityLaunched = true;
if (Utils.checkIfUserLoggedIn()) {
mContext.startActivity(new Intent(mContext, ABCACtivity.class));
} else {
mContext.startActivity(new Intent(mContext, XYZActivity.class));
}
finish();
}
}
}
}
I cancel it in a local method call but onTick still keeps getting called. Can someone please help?

Basically counter does not simply stop if I cancle it from onTick() or from onFinish() ie FROM INSIDE TIMER.
However it stops very easily if I do it from any point outside of timer.
So the point at which my data is fully loaded...I called timer.cancle() and it did the trick.
However I still dont understand why it does not work if we do same from inside timer methods.

I tried this code snippet, since most answers are saying you cannot cancel the timer inside its implementation, thus i tried using a handler inside onFinish. Old post but if anyone comes across this its helpful.
new Handler().post(new Runnable() {
#Override
public void run() {
timerTextView.setText("00:" + String.format("%02d", counter));
cancel();
}
});

you need to call stopTimer() outside of the CountDownTimer's onTick(),
something like this
#Override
public void onFinish() {
Log.d("testTimer", "onFinish");
if(mDataLoadedFromServer) {
mDataSavedAndNextActivityLaunched = true;
if (Utils.checkIfUserLoggedIn()) {
mContext.startActivity(new Intent(mContext, ABCACtivity.class));
} else {
mContext.startActivity(new Intent(mContext, XYZActivity.class));
}
finish();
stopTimer();
}
}

Related

Cancel a CountDownTimer in another activity

I have a CountDownTimer in one activity that I need to cancel in a different activity. The problem I'm having is that the CountDownTimer is not getting cancelled even when I make a call to the endTestTimer() method in another activity. Here is the code for the CountDownTimer:
private long testMillisLeft, questionMillisLeft;
private static CountDownTimer testTimer;
private void startTestTimer()
{
long startTime = DataHolder.getTestTime()*1000; //getTestTime() is the seconds
testTimer = new CountDownTimer(startTime, 1000) {
#Override
public void onTick(long millisUntilFinished) {
testMillisLeft = millisUntilFinished;
updateTestTimeLeft();
}
#Override
public void onFinish() {
Intent intent = new Intent(MainActivity4.this, MainActivity5.class);
startActivity(intent);
}
}.start();
}
public static void endTestTimer()
{
if(testTimer != null)
{
testTimer.cancel();
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main4);
testTimeLeft = (TextView) findViewById(R.id.testTimerTextView);
mathProblem = (TextView) findViewById(R.id.mathProblemTextView);
testTimer = null;
startTestTimer();
}
And here is how I am trying to cancel the same timer in another activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MainActivity4.endTestTimer();
}
A comment on this related post says that the static CountDownTimer creates memory leak. If that's the case, how can I call the endTestTimer() method in another activity and have it cancel the timer? Alternatively, how can I access the testTimer CountDownTimer directly and call .cancel() on it in another activity if it can't be static?
Looking into Android lifecycles led me to this post and this post, both of which I found very helpful. To my understanding, when the user hits the back button, the onDestroy() method is called for the currently-displayed activity, and then the onCreate() method is called for the activity to be displayed. So, to cancel the timers on MainActivity4 (the current activity in my case), I added this code to its class file:
#Override
protected void onDestroy() {
testTimer.cancel();
questionTimer.cancel();
super.onDestroy();
}
Now, when the user backs out of MainActivity4 (regardless of what other activity that takes them to) and onDestroy() is called automatically, both timers are cancelled and then MainActivity4 is destroyed. This seems to work fine for me, but I'm not sure if there are any downsides to doing it this way, so please let me know if there are.

How to call a method when Countdown OnFinish from another activity?

I have a class that has a CountDownTimer, which will be use throughout my project and I want to call different methods upon the Countdown onFinish() from different activities.
Here's my CountDownTimer class;
public class CountDownTimer {
private static final long START_TIME_IN_MILLIS = 10000;
private long timeLeftInMillis = START_TIME_IN_MILLIS;
private final TextView textViewCountDown;
private CountDownTimer countDownTimer;
private boolean timerRunning;
public CountDownTimer(TextView textView) {
this.textViewCountDown = textView;
startTimer();
}
public void startTimer() {
countDownTimer = new android.os.CountDownTimer(timeLeftInMillis, 1000) {
#Override
public void onTick(long millisUntilFinished) {
timeLeftInMillis = millisUntilFinished;
updateCountDownText();
}
#Override
public void onFinish() {
timerRunning = false;
}
}.start();
timerRunning = true;
}
public void resetTimer() {
timeLeftInMillis = START_TIME_IN_MILLIS;
updateCountDownText();
}
public void pauseTimer() {
countDownTimer.cancel();
timerRunning = false;
}
}
Example Scenario - Once a specific activity prompted, countdown will start and user has 10s to do whatever he wants otherwise it will automatically collect data and validate. So, once the 10s is over validating and data collecting methods should call from the activity.
I am a newbie to Android Dev and Thanks in advance!
There is multiple ways to achive this, each of them with different preferences. For your usecase i guess you could look into Broadcasts. Have all Activities that need to perform an action after the countdown implement a BroadcastReceiver which reacts to the Broadcast sent by your CountDownTimer.
Another option would be to implement some way of eventhandling. Android doesn't provide a event API, but you could look into the EventBus lib. Alternatively you could also just write your own event framework. For a simple case like yours it shouldn't be to complex.
In cases where I have to call methods/functions from other places in the application I make use of interfaces.
For example:
Here is an activity:
public class SomeActivity extends AppCompatActivity implements RemoteRunner.RemoteRunnerCallback{
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_about_us);
RemoteRunner remoteRunnerObject = new RemoteRunner(this);
remoteRunnerObject.runExternalMethod(); // <--- This part calls the showMessage() function
}
private void showMessage(String message){
Toast.makeText(this, message,Toast.LENGTH_LONG).show();
}
#Override
public void onRemoteCalled(String message)
}
I want to run a method in SomeActivity from this class:
public class RemoteRunner{
public interface RemoteRunnerCallback{
void onRemoteCalled(String message);
}
private RemoteRunnerCallback remoteRunnerListener;
public RemoteRunner(RemoteRunnerCallback remoteRunnerListener){
this.remoteRunnerListener = remoteRunnerListener;
}
public void runExternalMethod(){
remoteRunnerListener.onRemoteCalled("This message is from RemoteRunner");
}
}

Updating TextView with timer onTick after leaving and returning to activity

I'm writing a workout app and am trying to implement a rest timer in the Train activity. CountDownTimer located within Train and is called when the user presses a start button.
public CountDownTimer createTimer(long timerDuration) {
Log.d("new timer duration:", "value: " + timerDuration);
return new CountDownTimer(timerDuration, 1000) {
#Override
public void onTick(long millisUntilFinished) {
int progress = (int) (millisUntilFinished / 1000);
secondsLeftOnTimer = progress; // update variable for rest of app to access
// Update the output text
breakTimerOutput.setText(secondsToString(progress));
}
#Override
public void onFinish() { // Play a beep on timer finish
breakTimerOutput.setText(secondsToString(timerDurationSeconds));
playAlertSound(); // TODO: Fix the delay before playing beep.
}
}.start();
}
The timer works, as long as the user stays in the Train activity. If you switch to another activity, the timer continues to run in the background (the beep still occurs), which is what I want. If you go back to the Train activity, however, the breakTimerOutput TextView is no longer updated by onTick.
How can I "reconnect" breakTimerOutput to onTick when the user re-enters the Train activity?
Here is the full code for the activity, just in case.
I would like to suggest to keep the timer inside a Service and use BroadcastReceiver to receive the tick to update the TextView in your TrainActivity.
You need to start the CountDownTimer from the Service. So in the onCreate method of your Service you need to initialize a LocalBroadcastManager.
broadcastManager = LocalBroadcastManager.getInstance(this);
So on each tick on your timer (i.e. onTick method), you might consider calling a function like this.
static final public String UPDATE_TIME = "UPDATE_TIME";
static final public String UPDATED_TIME = "UPDATED_TIME";
public void updateTextView(String time) {
Intent intent = new Intent(UPDATE_TIME);
if(time != null)
intent.putExtra(UPDATED_TIME, time);
broadcastManager.sendBroadcast(intent);
}
Now in your TrainActivity create a BroadcastReceiver.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.copa);
receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String time = intent.getStringExtra(YourService.UPDATED_TIME);
// Update your TextView here.
}
};
}
And additionally you need to register and unregister the BroadcastReceiver.
#Override
protected void onStart() {
super.onStart();
LocalBroadcastManager.getInstance(this).registerReceiver((receiver),
new IntentFilter(YourService.UPDATE_TIME)
);
}
#Override
protected void onStop() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver);
super.onStop();
}

Android: onFinish() for CountDownTimer called after new one is declared

I am writing an HIIT (High Intensity Interval Training) activity for which I am implementing an interval timer. The CountDownTimer is supposed to finish 5 minutes of Warm-Up, then proceed to time the HIIT workout.
public class WarmUpActivity extends ActionBarActivity{
TextView Mode;
TextView Time;
int minutes;
long time_remaining;
boolean warmup_finished;
private CountDownTimer HIIT_Timer;
private void StartTimer() {
HIIT_Timer = new CountDownTimer(time_remaining, 1000) {
#Override
public void onTick(long millisUntilFinished) {
time_remaining = millisUntilFinished; //in case activity is paused or stopped
Time.setText(" " + (int)floor(millisUntilFinished / 60000) + ":" + ((millisUntilFinished / 1000) % 60));
if (warmup_finished == true) { //if we are in HIIT mode
if ((int)millisUntilFinished % 60000 == 0) { //every minute
if (Mode.getText() == "Low Intensity")
Mode.setText("High Intensity");
else
Mode.setText("Low Intensity");
}
}
}
#Override
public void onFinish() {
if (warmup_finished==false){
Mode.setText("Low Intensity");
warmup_finished = true;
HIIT_Method();
return;
}
else {
Completed_Method();
return;
}
}
}.start();
}
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.hiit_layout);
Mode=(TextView) findViewById(R.id.mode);
Time=(TextView) findViewById(R.id.time);
warmup_finished=false;
Mode.setText("Warm-Up");
time_remaining=5*60000; //5 minutes when created
}
#Override
public void onStart(){
super.onStart();
StartTimer();
return;
}
private void HIIT_Method(){
minutes=getIntent().getIntExtra(SelectHIITDuration.MINUTES, 0);
time_remaining=minutes*60000;
StartTimer();
return;
}
private void Completed_Method(){
Mode.setText("Workout Completed");
}
}
When warmup is finished and onFinish() is called for the first time, HIIT_Method is called, in which the HIIT timer is supposed to start with user specified duration. The problem is, after the new timer is declared using Start_Timer(), somehow Completed_Method is called. It can only be called from onFinish(). Why is onFinish() being called after I declare a new timer?
We need to move your call to startTimer from onStart to onCreate.
The issue here is understanding the Android lifecycle for an activity. A very good explanation by someone with better understanding than I is explained here.
Now from what I understand, we typically don't touch onStart until we start worrying about service binding and database queries, but other developers may think differently.
The official android documentation on an activity's lifecycle can be found here

How can I call the same functions in onCreate() and onResume() methods?

I want to run the same functions in onCreate() and onResume(). The functions basically record in 10 seconds and then stop and play the recorded sound.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
new CountDownTimer(
10000, // 10 second countdown
9999) { // onTick time, not used
public void onTick(long millisUntilFinished) {
// Not used
}
public void onFinish() {
isRecording = false;
}
}.start();
Thread thread = new Thread(new Runnable() {
public void run() {
isRecording = true;
record(); // starts to record
}
});
thread.start(); // thread start
// thread to start
play();
}
If I hit the Home button, then the app got put into the background. Now if I hit the app's icon button again, I'd like to call the same recording and playing functions.
Can I do same thing like this in onResume()? Basically duplicate the same thing.
public void onResume() {
super.onResume();
new CountDownTimer(
10000, // 10 second countdown
9999) { // onTick time, not used
public void onTick(long millisUntilFinished) {
// Not used
}
public void onFinish() {
isRecording = false;
}
}.start();
Thread thread = new Thread(new Runnable() {
public void run() {
isRecording = true;
record(); // starts to record
}
});
thread.start(); // thread start
play();
}
Just put the stuff you want to run in onResume(). onResume() will get called after onCreate() the first time then it will get called each time the application comes out of the background.
Activity lifecycle can be found (visually) here:
http://developer.android.com/reference/android/app/Activity.html

Categories