问题
I've been playing about with Runnable
s and have discovered that if you postDelayed
a Runnable
on a View
then removing the callback won't work, however if you do the same but post the Runnable
on a Handler
then removing the callback does work.
Why does this work (Runnable
run() code never gets executed):
Runnable runnable = new Runnable() {
@Override
public void run() {
// execute some code
}
};
Handler handler = new Handler();
handler.postDelayed(runnable, 10000);
handler.removeCallbacks(runnable);
where as this doesn't (Runnable
run() code always gets executed)?:
Runnable runnable = new Runnable() {
@Override
public void run() {
// execute some code
}
};
View view = findViewById(R.id.some_view);
view.postDelayed(runnable, 10000);
view.removeCallbacks(runnable);
回答1:
If the View
is not attached to a window, I can see this happening, courtesy of what looks like a bug in Android. Tactically, therefore, it may be a question of timing, making sure that you do not post or remove the Runnable
until after the View
is attached to the window.
If you happen to have a sample project lying around that replicates this problem, I'd like to take a look at it. Otherwise, I will try making my own, so I can have something I can use to report my presumed bug.
UPDATE
As mentioned in the comments, removeCallbacks()
on more ordinary widgets works, so it appears this is a WebView
-specific problem, per the OP's sample code.
回答2:
For various reasons, the View's handler (view.getHandler()) may not be ready when you want to initiate the animation.
Therefor you should probably wait before assigning the runnable to the view.
Assuming you are trying to do that from within an Activity, here is a code that waits for the handler to be available before posting the runnable:
private void assignRunnable(final View view, final Runnable runnable, final int delay)
{
if (view.getHandler() == null) {
// View is not ready, postpone assignment
this.getView().postDelayed(new Runnable() {
@Override
public void run() {
assignRunnable(view, runnable, delay);
}
}, 100);
//Abort
return;
}
//View is ready, assign the runnable
view.postDelayed(runnable, delay);
}
回答3:
Looking at ViewRootImpl.java, the semantics of View.removeCallbacks() seem unclear to say the least.
RunQueue.removeCallbacks
just removes the Runnables from an ArrayList. See here.
If RunQueue.executeActions
is called before removeCallbacks, then the ArrayList is cleared in all cases making removeCallbacks
a no-op. See here.
RunQueue.executeActions
is called for every traversal.... See here.
So unless I miss something, View.removeCallbacks
will not work if a traversal has happened since you called View.post
.
I'll stick to @james-wald comment above and not use View.post
来源:https://stackoverflow.com/questions/9768706/why-is-posting-cancelled-a-runnable-on-a-view-and-handler-result-in-different