I'm studying Java+Android now and I have the following example of a code, that is working fine.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new CountDownTimer(10000,1000){
public void onTick(long msUntillDone){
Log.i("Seconds left",String.valueOf(msUntillDone/1000));
}
public void onFinish(){
Log.i("Finished","We are done!");
}
}.start();
}
}
The question that I have is following. In the onTick function there is msUntillDone variable that I pass to it, and it gets that its 10000. But where does it get that from the new CountDownTimer, I mean how does it know it's value if I don't explicitly assign this name to 10000 value in main method arguments?
I hope you understand my question.
CountDownTimer(long millisInFuture, long countDownInterval)
The CountDownTimer schedules a countdown until a time in the future, with regular notifications on intervals along the way.
It means that the onTick() method is periodically called, every countDownInterval milliseconds (more and less). It is a mandatory parameter or course
You can consider the CountDownTimer as a counter, decreasing from millisInFuture to 0 and which warning the caller about the progress every countDownInterval milliseconds.
If you're interested by how it works under the hood, you can check the implementation : https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/CountDownTimer.java;l=44?q=CountDownTimer&sq=
Related
I have this code in android studio:
The question is that if I give the button back and the main activity takes me, that is going to continue executing.
How do I make it so that once it's behind, it does not run what's in the onfinished?
new CountDownTimer (40000,1000) {
#Override
public void onTick(long l)
{
crono.setText("00:"+l/1000);
if(contador==10)
{
}
}
#Override
public void onFinish()
{
if(value.equals("tiempo"))
{
crono.setText("00:00");
}
else
{
esperar();
}
}
}.start();
CountDownTimer class help us to implement timer with custom time interval and duration. It provide 2 callback onTick(int millisUntilFinished) which called after each interval and onFinish is called when time duration is completed.
If you want to stop countdown then store instance countdown and call countdown.cancel() in onDestroy or button click(any where by user action)
You can refer this
Right now I have a Timer starting an AsyncTask in a Fragments onCreate method (with a timer interval of 1 minute). So that is not a good idea I believe because if there is a configuration change the fragment will call the onCreate method again and I will have 2 running Timer AsyncTasks right?
So I need some way to put the AsyncTask where it's only started once during the whole lifecycle of the app.
no need of Asynctask for simple timer, try this -
class MyFragment extends Fragment{
private int currentTime;
private void startTimer(){
new Handler().
postDelayed(new Runnable(){
#Override
public void run()
{
onTick();
startTimer();
}
},1000*60);
}
private void onTick()
{
currentTime++;
}
#Override
public void onSaveInstanceState(Bundle outState)
{
outState.putInt("currentTime", currentTime);
super.onSaveInstanceState(outState);
}
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
if (savedInstanceState != null)
{
currentTime=savedInstanceState.getInt("currentTime",0);
startTimer();
}
}
You could use a Countdown Timer and cancel it in onDestroy(). That would guarantee that you only have a single one running at any time.
As for starting it exactly only once you will have to persist that knowledge somewhere depending on your needs. Maybe storing a boolean in onSaveInstanceState and reading it in onCreate() would do the trick?
And, as #marcin_j has pointed out, AsyncTasks are executed in sequence (unless specifically started differently), so using one will block every other Async task in your app (like downloading stuff etc).
if there is a configuration change the activity gets destroyed and re-created so no you will not have 2 timers running
At onCreate, I run a task that repeats every minute. Here how I do it:
myTimer = new Timer();
int delay = 30000; // delay for 30 sec.
int period = 600000; // repeat every 60 sec.
doThis = new TimerTask() {
public void run() {
Log.v("TImer","repeated");
wv.reload();
}
};
myTimer.scheduleAtFixedRate(doThis, delay, period);
All that code is in onCreate. So, when the app goes off from the screen, I can see in logcat that timer steel runs, and will not stop unless the app will be destroyed. So, in onPause of activity I call myTimer.cancel(); but it didnt help. I still can see updates in logcat, even when the app is not on the screen. So, how to stop timerTask?
Here is your code put into my file, with the values delay and period tweaked so I don't have to wait so long. I run the app. I see the messages in LogCat. I press the home button on my Galaxy S3. Then the messages stop in LogCat.
public class MainActivity extends Activity {
Timer myTimer;
TimerTask doThis;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myTimer = new Timer();
int delay = 0; // delay for 30 sec.
int period = 1000; // repeat every 60 sec.
doThis = new TimerTask() {
public void run() {
Log.v("TImer","repeated");
}
};
myTimer.scheduleAtFixedRate(doThis, delay, period);
}
#Override
protected void onPause() {
myTimer.cancel();
super.onPause();
}
}
It could be that since the thread has already been sent out, it runs one last time. In the onLoad set a variable to true, then in the onPause set it to false. Then in your timer task only run your code if the variable is true.
Write to the log outside of the new if statement though. If it is indeed running it just one last time, then that might be your solution. But if it is still running it over and over multiple times after the onPause then don't take my solution.
The actual answer is: The onPause method needs to be defined correctly.
The whole story:
The questioner defined the onPause method wrong. That was the reason for asking this question.
I'm writing this because I spent too much time reading question, answers, code and comments. The real answer was hidden in the last comment.
I have a CountdownTimer that counts down from 60 seconds. This CountdownTimer works by setting a textView to the remaining milliseconds, but whenever i rotate my device, the CountdownTimer gets reset.
I know this happens because the Activity gets restarted on rotation. So i tried saving the time remaining in a bundle and then restoring it, after the Activity was restarted.
long transferValue;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_playtimemode);
Log.d("Debug", "onCreate: " + transferValue);
long setTime = 60000;
long difference = setTime - transferValue;
new CountDownTimer(difference, 1000) {
public void onTick(long millisUntilFinished) {
millisUntilFinishedToSave = millisUntilFinished;
tvCountdown.setText("" + millisUntilFinished / 1000);
}
public void onFinish() {
tvCountdown.setText("done!");
}
}.start();
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putLong("millisKey", millisUntilFinishedToSave);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
transferValue = savedInstanceState.getLong("millisKey");
Log.d("Debug", "onRestoreInstanceState(): " + transferValue);
}
This however doesn't work. I am intializing transferValue at the top of this code (hence it returning 0), but how can i else save the data from the savedInstanceState to the CountdownTimer?
07-06 20:21:30.038: D/Debug(28995): onCreate: 0
07-06 20:21:30.043: D/Debug(28995): onRestoreInstanceState(): 55994
I would give your timer it's own thread. Your timer is being stopped because it's on the UI thread (as you stated) and when the UI redraws the Activity is re-initialized. All long running processes should have their own thread. Rule of thumb: get out of the UI thread as soon as possible.
Here is an example of using a Service. This service will start when called and stay in memory regardless of screen orientation changes or even activity focus changes.
Here is the service:
public class Timer extends Service {
#Override
public IBinder onBind(Intent i) {
return null;
}
#Override
public int onStartCommand(Intent i, int flags, int startId) {
// Put your timer code here
}
}
You will need to add this line to your manifest (somewhere between the application open/close tags):
<service android:name=".Timer" />
Then you can start the service at any time by using this (it's important to note that you can start it over and over again, the recursive calls do not make a new service.):
startService(new Intent(this, Timer.class));
Use System.currentTimeMillis to get the current system time, then add 60000 milliseconds to the time and store that as an end time. Then any time you have to change anything, just compare System.currentTimeMillis to the EndTime.
Endtime = System.currentTimeMillis + 60000;
then on every instance
TimeRemaining = Endtime - System.currentTimeMillis
The accepted answer makes the thing very complex. You can do it in a simpler way. The problem in your code is that Activity gets created on rotation (see Activity Lifecycle), so does the CountdownTimer instance.
I could write the whole example code but there is a nice answer from #Drew here: Android Innerclass TextView reference when activity resumes
I'm feeling very, very stupid right now... I feel like I must be missing something really obvious.
I've encountered variations of this problem on multiple occasions now, however here is my current example -
When the activity is created, there will be a button marked Start and text set to --:-- next to it. I would like to have it so that when the button is pressed, a timer starts from one minute and displays the seconds remaining in the --:-- text as 00:59 etc. etc., and the text on the button changes to Pause. If the button is pressed when the timer is running, it pauses the timer and changes the text on the button to Start.
So I was using a boolean, timerRunning, in order to keep track of whether the timer was running or not. But if I try to change timerRunning within the onClickListener it gives an error and tells me to change timerRunning to final, and then once I do that it says "The final local variable timerRunning cannot be assigned, since it is defined in an enclosing type."
I'm sorry if this is unclear - I'm just really confused with where I should be declaring variables / how to access them etc. in Android, I don't really understand why I keep getting weird errors all the time.
Thanks.
public class Timing extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.timing);
Button bStartJammer = (Button) findViewById(R.id.buttonStartJammer);
CountDownTimer cdtJammerTimer;
long lJammerTotalMS = 60000;
final boolean timerRunning = false;
bStartJammer.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
timerRunning = true;
}
});
}
}
Without source context, it's tough to visualize what you're doing.
How are you defining your click handler? If it's an anonymous class, you'll run into the final issues--is your activity or handler so complex that it makes a separate class completely necessary?
In the previous question my click handler was implemented by the activity, so it has access to that instance's variables. A much-abbreviated skeleton of what I had been doing before not using the CountDownTimer:
public class FooTimer extends Activity implements View.OnClickListener {
private CountDownTimer timer;
private TextView timerDisplay;
private Button pauseButton;
private boolean timerRunning = false;
private boolean timerDone = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
pauseButton = (Button) findViewById(R.id.pause_button);
pauseButton.setOnClickListener(this);
timerDisplay = (TextView) findViewById(R.id.timer_display);
timerDisplay.setText(String.format("%d:%02d:%02d", hours, minutes, seconds));
timer = newTimer();
timerRunning = true;
timer.start();
}
public void onClick(View view) {
switch (view.getId()) {
case R.id.pause_button:
toggleTimer();
break;
}
}
private void toggleTimer() {
if (timerRunning) {
timer.cancel();
pauseButton.setText(getResources().getString(R.string.resume_label));
timerRunning = false;
} else if (timerDone) {
finishActivity(0);
} else {
seconds += 1;
timer = newTimer();
timer.start();
pauseButton.setText(getResources().getString(R.string.pause_label));
timerRunning = true;
}
}
private CountDownTimer newTimer() {
millis = (minutes * 60 * 1000) + ((seconds + 1) * 1000);
return new CountDownTimer(millis, 1000) {
public void onTick(long millisUntilFinished) {
timerDisplay.setText(String.format("%d:%02d:%02d", hours, minutes, seconds));
}
public void onFinish() {
timerDisplay.setText("Finished");
timerRunning = false;
timerDone = true;
pauseButton.setText(getResources().getString(R.string.back_label));
}
};
}
}
(I took a bunch of stuff out, and added some really early code back in, so it's a bit of a mish-mosh, but the ideas are there.)
Your new OnClickListener(){..} is actually an anonymous class: http://mindprod.com/jgloss/anonymousclasses.html
Anonymous classes have access to class (static) and instance fields of enclosing class. So a solution is to have timerRunning defined as a field, i.e. define it outside of onCreate() method.
its not starting the code anywhere, your just setting it to true that its a timer. watch the tutorials here and they should really help you out. cornboyzandroid
Some of his earlier videos really describe how to do a timer pretty clearly and step by step. And he helps with global and local variables. around episode 6 or 7 just check his page out.