How can I pass a variable into a postdelayed runnable?

4.2k Views Asked by At

I have created two classes implemented from runnable(). I then call them several times, with varying delay. The problem is, when I run the runnable later, it needs to know the value of a variable back when the postdelay was called, not when it's actually run.

I think what I now need to do is pass the variable into the runnable instance when I call the post delay. But for the life of me I can't figure out how to do it. Here is the source code for reference:

public void PlaySequence() throws InterruptedException{

for(int i = 0;i<level;i++){
    mHandler.postDelayed(mToggleButtonOn, (i+1)*playbackOffTime);
    mHandler.postDelayed(mToggleButtonOff, (i+1)*playbackOnTime);           
    currentProgIteration++;
}        
currentProgIteration=0;     
}

private Runnable mToggleButtonOn = new Runnable() {

public void run() {
    try {
            if(Sequence[currentProgIteration]==0){
                ImageCard[0].setImageDrawable(getResources().getDrawable(R.drawable.yellow));
            }
            else if(Sequence[currentProgIteration]==1){
                ImageCard[1].setImageDrawable(getResources().getDrawable(R.drawable.blue));
            }
            else if(Sequence[currentProgIteration]==2){
                ImageCard[2].setImageDrawable(getResources().getDrawable(R.drawable.red));
            }
            else if(Sequence[currentProgIteration]==3){
                ImageCard[3].setImageDrawable(getResources().getDrawable(R.drawable.green));
            }                   
        }
     catch (IllegalStateException e) {
        e.printStackTrace();
    }

}
};

private Runnable mToggleButtonOff = new Runnable() {

public void run() {
    Log.d("Info", "running Off currentProgIteration: "+currentProgIteration);

    try {
            if(Sequence[currentProgIteration]==0){
                ImageCard[0].setImageDrawable(getResources().getDrawable(R.drawable.darkyellow));
            }
            else if(Sequence[currentProgIteration]==1){
                ImageCard[1].setImageDrawable(getResources().getDrawable(R.drawable.darkblue));
            }
            else if(Sequence[currentProgIteration]==2){
                ImageCard[2].setImageDrawable(getResources().getDrawable(R.drawable.darkred));
            }
            else if(Sequence[currentProgIteration]==3){
                ImageCard[3].setImageDrawable(getResources().getDrawable(R.drawable.darkgreen));
            }                   
        }
    catch (IllegalStateException e) {
        e.printStackTrace();
    }

}
};

The runnables are using global variable currentProgIteration, but by the time they run after the delay, that variable has been set back to 0.

Thanks for any advice.

2

There are 2 best solutions below

2
On

The simplest approach may be to use an anonymous Runnable.

final int delay = (i+1)*playbackOffTime;
mHandler.postDelayed(new Runnable() {
   final Color[] colours = { R.drawable.yellow, R.drawable.blue, R.drawable.red, R.drawable.green } ;
   public void run() {
        try {
           // can use "delay" here
           int seq = Sequence[currentProgIteration];
           ImageCard[seq].setImageDrawable(getResources().getDrawable(colours[seq]));
        } catch (IllegalStateException e) {
            e.printStackTrace();
        }
    }
});
0
On

As Peter said, you can use anonymous Runnable

to make removeCallbacks(...) possible you could remove runnables with token:

create top level token to distinct Runnables posted onto MessageQueue, like

val animToken = Any()

in order to track your runnable, use following extension:

fun Handler.postDelayedWithToken(token: Any, delayInMillis: Long, runnable: Runnable) {
    postAtTime(runnable, token, SystemClock.uptimeMillis() + delayInMillis)
}

sample use: handler.postDelayedWithToken(animToken, 3000L, { hideSomething() })

in order to dispose (e.g: before onPause / onStop / onDestroyView):

handler.removeCallbacksAndMessages(animToken)