How to stop a runnable by using a buttonStop in Android - android

I am developing a simple application in which buttons background change dynamically after one second. I have a button in my app name stopButtonBackgroundChanging, as I click the button it stops the changing of buttons background but after 2-seconds the app Stops Unexpectedly". Please help me in this respect and also see my code below you might get better idea that what's really I want to do.
UpdateRunnable updateRunnable;
private Runnable mEndlessRunnable;
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.myLayout);
Button stopButtonBackgroundChanging = (Button)findViewById(R.id.buttonStop);
stopButtonBackgroundChanging.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{
//See implementation of stop() method at the end of the code
updateRunnable.stop();
}
});
mEndlessRunnable = (Runnable) new UpdateRunnable(new Handler(), new Button[]
{
(Button) findViewById(R.id.button1),
(Button) findViewById(R.id.button2),
(Button) findViewById(R.id.button3),
(Button) findViewById(R.id.button4),
});
mEndlessRunnable.run();
}// End of onCreate()
//Start of UpdateRunnable
private static class UpdateRunnable implements Runnable
{
private boolean myContinue = true;
private Random mRand = new Random();
private Handler mHandler;
private Button[] mButtons;
private Button mCurButton;
private int mState;
public UpdateRunnable(Handler handler, Button[] buttons)
{
mHandler = handler;
mButtons = buttons;
}
public void run()
{
if(myContinue)
{
// select a button if one is not selected
if (mCurButton == null)
{
mCurButton = mButtons[mRand.nextInt(mButtons.length)];
}
// check internal state, `0` means first bg change, `1` means last
switch (mState)
{
case 0:
mCurButton.setBackgroundResource(R.drawable.buttonyellow);
mState = 1;
break;
case 1:
mCurButton.setBackgroundResource(R.drawable.buttonblue);
// reset state and nullify so this continues endlessly
mState = 0;
mCurButton = null;
break;
}
mHandler.postDelayed(this, 1000);
}
}// End of run()
public void stop()
{
this.myContinue =false;
}
}//End of class UpdateRunnable

I believe you will get this to work by changing the your onClick:
public void onClick(View v)
{
//See implementation of stop() method at the end of the code
((UpdateRunnable) mEndlessRunnable).stop();
}

Related

Handler doesn't work correctly

first of all excuse me if my title doesn't describe my question very well but i couldn't find a better one .
there is a simple stopWatch app that has three button start,stop,reset and a textview to display time . app has just one activity like this:
public class StopwatchActivity extends AppCompatActivity {
private int mNumberOfSeconds = 0;
private boolean mRunning = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_stopwatch);
//if if uncomment this runner method and delete the runner inside onClickStart everything will work find
//runner()
}
public void onClickStart(View view){
mRunning = true;
runner();
}
public void onClickStop(View view){
mRunning = false;
}
public void onClickReset(View view){
mRunning = false;
mNumberOfSeconds = 0;
}
public void runner(){
final TextView timeView = (TextView) findViewById(R.id.time_view);
final Handler handler = new Handler();
handler.post(new Runnable() {
#Override
public void run() {
int hours = mNumberOfSeconds/3600;
int minutes = (mNumberOfSeconds%3600)/60;
int second = mNumberOfSeconds%60;
String time = String.format("%d:%02d:%02d" , hours , minutes , second );
timeView.setText(time);
if (mRunning){
mNumberOfSeconds++;
}
handler.postDelayed(this , 1000);
}
});
}
}
my problem is when i comment the runner() in onClickStart method and put it in the onCreate method everything is ok . but when i change the code like above the code is still running but after i press stop button and then press start again the second will increment by 4 or 5 very fast.
can anyone explain me what is the difference between this two modes?
declare your handler globally
public void runner(){
timeView = (TextView) findViewById(R.id.time_view);
handler = new Handler();
runnable = new Runnable() {
#Override
public void run() {
int hours = mNumberOfSeconds/3600;
int minutes = (mNumberOfSeconds%3600)/60;
int second = mNumberOfSeconds%60;
String time = String.format("%d:%02d:%02d" , hours , minutes , second );
timeView.setText(time);
if (mRunning){
mNumberOfSeconds++;
}
handler.postDelayed(this , 1000);
}
}
handler.post(runnable);
}
in button function
public void onClickStart(View view){
if(handler != null) {
//restart the handler to avoid duplicate runnable
handler.removeCallbacks(runnable);//or this handler.removeCallbacksAndMessages(null);
}
mRunning = true;
runner();
}
public void onClickStop(View view){
mRunning = false;
handler.removeCallbacks(runnable); // this will stop the handler from working
}

Having trouble communicating between threads

My first attempt at both building a game and using multithreading is going mostly well, but I'm stuck at the moment.
It's a simple Whack a mole clone, so I've got a 3x4 grid of mole ImageViews declared in a layout.xml, then I'm using setContentView(R.layout.layout) to put it up, then a separate thread to make one of them appear for a second, then disappear. Here's my Activity's onCreate():
public class WAM_Activity extends Activity {
private static final int MAKE_VISIBLE = 1;
private static final int MAKE_INVISIBLE = 0;
private ImageView[] mole = new ImageView[11];
private ImageView currentMole;
private int[] moleId = {R.id.mole1, R.id.mole3, R.id.mole4, R.id.mole5, R.id.mole6, R.id.mole7, R.id.mole8, R.id.mole9, R.id.mole10, R.id.mole11, R.id.mole12};
private boolean running = true;
private int randomInt = 0;
private Random rand = new Random();
private Handler handler;
//private WAM_Thread wamthread = new WAM_Thread();
private Context cont = this;
private static Handler h;
Message msg;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.wam_view_layout);
//add ImageViews declared in R.layout.wam_view_layout to ImageView objects
for (int i = 0; i < 11; i++) {
mole[i] = (ImageView) findViewById(moleId[i]);
mole[i].setVisibility(View.INVISIBLE);
mole[i].setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(cont, "You clicked one!", Toast.LENGTH_SHORT).show();
}
});
}
handler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch(msg.what) {
case MAKE_VISIBLE:
currentMole = (ImageView) msg.obj;
currentMole.setVisibility(View.VISIBLE);
break;
case MAKE_INVISIBLE:
currentMole = (ImageView) msg.obj;
currentMole.setVisibility(View.INVISIBLE);
}
}
};
Runnable runnable = new Runnable() {
public void run() {
while (running) {
randomInt = rand.nextInt(11);
msg = handler.obtainMessage();
msg.obj = mole[randomInt];
msg.what = MAKE_VISIBLE;
handler.sendMessage(msg);
handler.postDelayed(new Runnable() {
#Override
public void run() {
msg = handler.obtainMessage();
msg.obj = mole[randomInt];
msg.what = MAKE_INVISIBLE;
}
}, 1000);
}
}
};
Thread thread = new Thread(runnable);
thread.start();
}
All the moles are declared invisible by default in the layout xml file, so this code should be making one of them visible, wait a second, then make him invisible again and then make another visible and repeat. Instead, it's making them all visible all the time. They still respond to taps, but that's it.
Anyone see what I'm doing wrong? I've been struggling with this since last night but I'm really close to getting it right.
Two things:
at the end of your runnable, add a Thread.sleep(1000) so that the background thread pauses after making one thing visible
inside your postDelayed runnable, you're not calling handler.sendMessage

stop watch problem in android

i creating for stopwatch with start,stop i use following code it started but not stop. please help me.
my code:
public class StopWatch2 extends Activity implements Runnable{
// text view influenced by the Thread
private TextView threadModifiedText;
int time=0;
Button b1,b2,b3;
/** 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);
b1.setOnClickListener(new View.OnClickListener()
{
public void onClick(View view)
{
currentThread.start();
}
});
b2.setOnClickListener(new View.OnClickListener()
{
public void onClick(View view)
{
currentThread.stop();
}
});
}
//Method you must override to control what the Thread is doing
#Override
public void run(){
try {
while(true){
currentThread.sleep(1000, 0);
threadHandler.sendEmptyMessage(0);
}
// signaling things to the outside world goes like this
} catch (InterruptedException e) {
}
}
private Handler threadHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
time++;
threadModifiedText.setText(""+time);
}
};
}
I think You should use a class level variable boolean shouldRun = true ;
in your run() method while should be used like this way
while(shouldRun)
{
//implementation
}
and in your b2.onClickListener() change shouldRun = false;
Edit:
For suspend and resume you can change the value of shouldRun to shouldRun = true; in the listener for resume button. Remember that when you stop the updates you should reset the time also. and in suspend the time should be remain same.
I implemented stopwatch for rotational puzzles solving time measurements and I have approached it somewhat differently.
For time measurements I use Handler object that triggers after DISPLAY_UPDATE_TIMEOUT. It is time after which you update display about time elapsed.
To start counting I use:
mLastStartTimestamp = System.currentTimeMillis();
mRepetitiveTimeoutHandler.postDelayed(processWatchTimer, DISPLAY_UPDATE_TIMEOUT);
and to stop it
mRepetitiveTimeoutHandler.removeCallbacks(processWatchTimer);
mStartStopElapsed = System.currentTimeMillis() - mLastStartTimestamp ;
updateTimeDisplay();
On each trigger of the handler I do the following:
private Runnable processWatchTimer = new Runnable()
{
public void run()
{
mRepetitiveTimeoutHandler.postDelayed(processWatchTimer, DISPLAY_UPDATE_TIMEOUT);
updateTimeDisplay();
}
};

problem in thread running

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.

Android - How to stop and pause timer

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).

Categories

Resources