I am working on a stopwatch app which has a start button. After clicking on the start button, the timer starts and button text is updated to Pause. After clicking on Pause, the timer stops and button text changes to Resume. When I click on resume, I expect the timer to start from where it paused, but the timer jumps to few seconds forward. Below is the code snippet:
#Override
protected void onCreate(Bundle savedInstanceState) {
this.timerHandler = new Handler();
startButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if(!MainActivity.running && !MainActivity.paused) {
MainActivity.running = true;
MainActivity.paused = false;
MainActivity.startTime = System.currentTimeMillis();
startTimer();
startButton.setText("PAUSE");
}
else if (MainActivity.running && !MainActivity.paused) {
MainActivity.running = false;
stopTimer();
MainActivity.paused = true;
MainActivity.pauseTime = System.currentTimeMillis();
MainActivity.pauseTimerText = timerText(); // Eg: 00:00:08
startButton.setText("RESUME");
}
else if(MainActivity.paused) {
MainActivity.running = true;
MainActivity.paused = false;
startTimer();
startButton.setText("PAUSE");
}
resetButton.setVisibility(View.VISIBLE);
}
});
}
Runnable timerRunnable = new Runnable() {
#Override
public void run() {
if (!MainActivity.running) {
return;
}
if (pauseTimerText != null)
{
MainActivity.timerTV.setText(pauseTimerText); // this gets set to 00:00:08 here
pauseTimerText = null;
}
else
{
MainActivity.timerTV.setText(timerText());
}
timerHandler.postDelayed(timerRunnable, 100); // this changes the timer text.
}
};
void startTimer() {
MainActivity.running = true;
timerRunnable.run();
}
void stopTimer() {
MainActivity.running = false;
timerHandler.removeCallbacks(timerRunnable);
timerHandler.removeCallbacksAndMessages(null);
}
If my pauseTimerText is 00:00:08, then in runnable "MainActivity.timerTV.setText(pauseTimerText);" line does set the timer text to 00:00:08, but I noticed that after postDelayed gets executed, instead of timer showing 00:00:08 on UI, it would show something like 00:00:13 i.e. it also adds up and shows the time it was paused.
Looks like either postDelayed is not working right or removeCallBacks is not removing everything.
Related
I have a code that plays 5 sounds with 1 second delay between the sounds and I want this part of code to be executed every 5 seconds (so it will run in a row so far as a boolean variable is true, and when it becomes false the tread run stopped - I have a button to both start and stop this executions). Everything works perfectly, but the issue is that I can't get rid of the 5 seconds delay in the first time I click the button, so when I first click, the sounds beggins only after 5 seconds. How can I make it start right away and only after the first time start taking the delays?
Here is the button onClick code:
public void clickHandlerStartTempo(final View view) {
if (!tempoOn) {
Toast toast = Toast.makeText(getApplicationContext(), "Start Tempo!", Toast
.LENGTH_SHORT);
toast.show();
tempoOn = true;
final Handler handler = new Handler();
final int delay = 5000; //milliseconds
handler.postDelayed(new Runnable() {
public void run() {
if (tempoOn) {
runCode(view);
handler.postDelayed(this, delay);
}
}
}, delay);
} else {
Toast toast = Toast.makeText(getApplicationContext(), "Stop Tempo!", Toast
.LENGTH_SHORT);
toast.show();
tempoOn = false;
}
}
And here is the runCode method:
public void runCode(View view) {
Runnable runnable = new Runnable() {
#Override
public void run() {
playSound(0);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 4; i++) {
if (tempoOn) {
playSound(1);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
return;
}
}
}
};
Thread thread = new Thread(runnable);
Log.i(TAG, "runCode: Thread id = " + thread.getId());
thread.start();
}
I'm new to android development and any help would be very much appreciated.
Thanks.
First you need to playsound without thread after that you will execute your reaming 5 second logic stop thread after 4 count.
public void onStartPress(){
playSound();
someMethod();
}
public void someMethod(){
Handler uiHandler = new Handler(Looper.getMainLooper());
uiHandler.postDelayed(new Runnable() {
#Override
public void run() {
playSound();
someMethod();
}
},1000);
}
Don't use actual Threads unless you really want to do something off the Ui thread. Most of the time you do want to keep things on the Ui thread.
For simple repeating tasks, you can easily repurpose the CountDownTimer class. Often with an (almost) infinite run time or Long.MAX_VALUE (292 million years). The fist onTick happens immediately after starting.
private CountDownTimer mTimer;
private void start() {
if (mTimer == null) {
mTimer = new CountDownTimer(Long.MAX_VALUE, 5000) {
#Override
public void onTick(long millisUntilFinished) {
// start a beeping countdown
new CountDownTimer(5000, 1000) {
private int state = 1;
#Override
public void onTick(long millisUntilFinished) {
playSound(state);
state = state + 1 % 2;
}
#Override
public void onFinish() {
playSound(0);
}
}.start();
}
#Override
public void onFinish() { /* ignore, never happens */ }
};
mTimer.start();
}
}
private void stop() {
if (mTimer != null) {
mTimer.cancel();
mTimer = null;
}
}
I want to log when something happened and delay 1 minitues. so I use a timer and timertask live this:
private Timer _checkTimer;
private boolean _timerStart = false;
private void requestPlayPosterItem(long id) {
//
if(_timerStart && _checkTimer != null ){
_checkTimer.cancel();
}
_checkTimer = new Timer();
_checkTimer.schedule(new TimerTask(){
#Override
public void run() {
_timerStart = false;
// do log
}
}, 30000);
_timerStart = true;
// others -----
}
and at activity onPause(), I cancel the timer too:
if(_checkTimer != null && _timerStart){
_checkTimer.cancel();
}
my question: _checkTimer can reuse? is there resource leak(timer)?
I have a clock done by updating a TextView text using a Runnable. When I'm in the Activity the TextView is updated properly, but when I leave and come back to the activity, the code in the run() method is not executed anymore.
Do I have to call run() again in the onResume of my activity? Why? Is the mTicker Runnable stopped?
MyActivity.java
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new Handler();
mTicker = new Runnable() {
public void run() {
if(mClockStopped) return;
long now = System.currentTimeMillis();
mCalendar.setTimeInMillis(now);
mClock.setText(DateFormat.format("kk:mm", mCalendar));
mClock.invalidate();
long upTime = SystemClock.uptimeMillis();
long next = upTime + (60000 - now % 60000);
mHandler.postAtTime(mTicker, next);
}
};
mTicker.run();
/* more stuff */
}
#Override
public void onResume()
{
super.onResume();
mClockStopped = false;
}
#Override
public void onPause()
{
mClockStopped = true;
super.onPause();
}
Maybe is not that simple, but setting mClockStopped to true make your runnable's run() exit.
You should call
mClockStopped = false;
mTicker.run();
In onResume
i doing simple stop watch with [start/pause] and [reset] button. problem occur when i pressed start button after pause. the run method is not calling. please help me.
my code is
public class StopWatch3 extends Activity implements Runnable{
// text view influenced by the Thread
private TextView threadModifiedText;
int time=0;
Button b1,b2,b3;
boolean shouldRun = false;
/** Called when the activity is first created. */
Thread currentThread = new Thread(this);
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.stopwatch);
b1=(Button)findViewById(R.id.button1);
b2=(Button)findViewById(R.id.button2);
b3=(Button)findViewById(R.id.button3);
threadModifiedText = (TextView) findViewById(R.id.textView1);
Log.e("before",""+currentThread.getState());
b1.setOnClickListener(new View.OnClickListener()
{
public void onClick(View view)
{
Log.e("stopw",(String) b1.getText());
if(b1.getText().toString().equals("start")){
if(currentThread.getState()==Thread.State.NEW){
currentThread.start();
Log.e("after",""+currentThread.getState());
shouldRun = true;
b1.setText("pause");
}
else{
shouldRun = true;
b1.setText("pause");
}
}
else if(b1.getText().toString().equals("pause")){
shouldRun = false;
b1.setText("start");
}
}
});
b2.setOnClickListener(new View.OnClickListener()
{
public void onClick(View view)
{
time=0;
}
});
}
#Override
public void run(){
try {
while(shouldRun){
Thread.sleep(1000);
threadHandler.sendEmptyMessage(0);
}
} catch (InterruptedException e) {
}
}
private Handler threadHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
time++;
threadModifiedText.setText(""+time);
}
};
}
You cannot start thread after it finishes it's job, and his state is no longer NEW anyway, you'll have to create a new thread in this case.
When you press "start" the second time, you reach this part:
else{
shouldRun = true;
b1.setText("pause");
}
And nothing in this code will make the thread run again of course...
I think your thread just runs to an end when you set shouldRun to false.
Enclose your while loop into another while loop that is true as long as your program runs.
I have been running through alot of issues try to pause and unpause a timer, and if I lock the orientation to portrait or landscape it works, but thats not exactly what I want to do. Of course, the onCreate method is called when you change orientation, so im canceling my timertask and setting it to null, but after running through the orientation more than once, it doesnt cancel the timertask anymore. Ive looked through other peoples questions on here but none seem to hold the answer to my quesiton. Heres my code. Its a little sloppy at the moment because ive been trying about everything I can to get it to work.
public class singleTimer extends Activity implements OnClickListener {
private Integer setTime = 0;
private Integer tmrSeconds = 0;
private Integer tmrMilliSeconds = 0;
private Timer myTimer = new Timer();
private TimerTask myTimerTask;
private TextView timerText;
private boolean isPaused = true;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.single_timer);
Bundle extras = getIntent().getExtras();
setTime = extras.getInt("com.bv.armyprt.timer_duration");
if (myTimerTask != null) {
myTimerTask.cancel();
myTimerTask = null;
}
if (savedInstanceState != null) {
if (savedInstanceState.getInt("tmrSeconds") == 0) {
tmrSeconds = setTime;
} else {
tmrSeconds = savedInstanceState.getInt("tmrSeconds");
tmrMilliSeconds = savedInstanceState.getInt("tmrMilliseconds");
if (isPaused == false) {
myTimer = new Timer();
myTimerTask = new TimerTask() {
#Override
public void run() {
TimerMethod();
}
};
myTimer.schedule(myTimerTask, 0, 100);
}
}
} else {
tmrSeconds = setTime;
}
timerText = (TextView)findViewById(R.id.timerText);
timerText.setText(String.format("%03d.%d", tmrSeconds, tmrMilliSeconds));
TextView timerDesc = (TextView)findViewById(R.id.timerDescription);
timerDesc.setText("Timer for: " + setTime.toString());
Button startButton = (Button)findViewById(R.id.timerStart);
Button stopButton = (Button)findViewById(R.id.timerStop);
Button closeButton = (Button)findViewById(R.id.timerClose);
closeButton.setOnClickListener(this);
startButton.setOnClickListener(this);
stopButton.setOnClickListener(this);
}
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case (R.id.timerStart):
isPaused = false;
myTimer = new Timer();
myTimerTask = new TimerTask() {
#Override
public void run() {
TimerMethod();
}
};
myTimer.schedule(myTimerTask,0, 100);
break;
case (R.id.timerStop):
isPaused = true;
myTimerTask.cancel();
myTimerTask = null;
myTimer.cancel();
break;
case (R.id.timerClose):
onDestroy();
this.finish();
break;
}
}
private void TimerMethod()
{
//This method is called directly by the timer
//and runs in the same thread as the timer.
//We call the method that will work with the UI
//through the runOnUiThread method.
this.
tmrMilliSeconds--;
this.runOnUiThread(Timer_Tick);
}
private Runnable Timer_Tick = new Runnable() {
public void run() {
//This method runs in the same thread as the UI.
if (tmrSeconds > 0) {
if (tmrMilliSeconds <= 0) {
tmrSeconds--;
tmrMilliSeconds = 9;
}
} else {
Vibrator v = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
v.vibrate(1000);
myTimer.cancel();
tmrSeconds = setTime;
tmrMilliSeconds = 0;
isPaused = true;
}
//Do something to the UI thread here
timerText.setText(String.format("%03d.%d", tmrSeconds, tmrMilliSeconds));
}
};
#Override
public void onSaveInstanceState(Bundle savedInstanceState){
savedInstanceState.putInt("setTimer", setTime);
savedInstanceState.putInt("tmrSeconds", tmrSeconds);
savedInstanceState.putInt("tmrMilliseconds", tmrMilliSeconds);
super.onSaveInstanceState(savedInstanceState);
}
#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
setTime = savedInstanceState.getInt("setTimer");
tmrSeconds = savedInstanceState.getInt("tmrSeconds");
tmrMilliSeconds = savedInstanceState.getInt("tmrMilliSeconds");
}
}
you can simply add a boolean variable
boolean stopTImer = false ;
and in your timerTask , do something like this :
#Overrride
public void run(){
if(!stopTimer){
//do stuff ...
//...
}
and when you want to stop it , put the boolean to true
You should stop the timer during onStop. Android might create another instance of your Activity and you will lose the reference to your previous timer(task) when you change orientation.
All objects tied to an activity follow the activity lifecycle. That means you have to store the references to objects elsewhere if you want to keep them even if the activity gets deleted (which can happen quite often).