How to stop Runnable in Android that's looping using postDelayed()?

Amit Prajapati picture Amit Prajapati · Dec 5, 2012 · Viewed 11.8k times · Source

I need to stop a Runnable from running when an image is clicked in my Android app. I'm running this Runnable repeatedly using ImageView.postDelayed():

r = new Runnable() {
    public void run() {
        imgview.setImageResource(imageArray[i]);
        i++;
        if (i >= imageArray.length) {
            i = 0;
        }
        imgview.postDelayed(r, 20); // set to go off again in 3 seconds.
        // imgview.setOnClickListener(this);
    }

};
imgview.postDelayed(r, 20); // set first time for 3 seconds

But under certain conditions I want to stop it from running, after it's already started. Here's the full code for my activity:

    public class MainActivity extends Activity  {

        int i = 0;

        ImageView imgview, imgview2;
        Handler handler = new Handler();
        Runnable r;
        MediaPlayer mMediaPlayer;
        int[] imageArray = { R.drawable.f1, R.drawable.f2, R.drawable.f3,
                R.drawable.f4, R.drawable.f5, R.drawable.f6, R.drawable.f7,
                R.drawable.f8, R.drawable.f9, R.drawable.f10, R.drawable.f11,
                R.drawable.f12, R.drawable.f13, R.drawable.f14, R.drawable.f15,
                R.drawable.f16, R.drawable.f17, R.drawable.f18, R.drawable.f19,
                R.drawable.f20, R.drawable.f21, R.drawable.f22, R.drawable.f23,
                R.drawable.f24, R.drawable.f25, R.drawable.f26, R.drawable.f27,
                R.drawable.f28 };

        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.tapp_activity);

            imgview = (ImageView) findViewById(R.id.imageView1);

            mMediaPlayer = new MediaPlayer();
            mMediaPlayer = MediaPlayer.create(MainActivity.this, R.raw.water);
            mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);



            imgview2 = (ImageView) findViewById(R.id.imageView2);

            imgview2.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    // TODO Auto-generated method stub




                    if(i==0)
                    {
                    mMediaPlayer.setLooping(true);
                    mMediaPlayer.start();
                    i=1;
                    r = new Runnable() {
                        public void run() {
                            imgview.setImageResource(imageArray[i]);
                            i++;
                            if (i >= imageArray.length) {
                                i = 0;
                            }
                            imgview.postDelayed(r, 20); // set to go off again in 3 seconds.
                            // imgview.setOnClickListener(this);
                        }

                    };
                    imgview.postDelayed(r, 20); // set first time for 3 seconds
                    }
                    else
                    {
                    i=0;

                    mMediaPlayer.stop();
                    imgview.setBackgroundResource(R.drawable.tapstill);
                    }



                }
            });


        }


        @Override
        protected void onDestroy() {
            // TODO Auto-generated method stub
            super.onDestroy();
            mMediaPlayer.stop();

        }



    }

What can I change in my code so that my Runnable stops running in the else condition of my onClick() method?

Answer

cHao picture cHao · Dec 5, 2012

You might try removing the callback.

imgview.removeCallbacks(r);

In order for this to work, though. you'd have to ensure that r is the same Runnable as the one you posted. You could do this by creating it once, possibly in onCreate. Since the Runnable doesn't have any dependency on the ClickListener anyway, this shouldn't be a problem.

You might also need to do some synchronization in order to prevent the case where you're removing a currently running callback, though, now that i think about it. The volatile boolean running idea is probably less complex overall.