Why is posting & cancelled a runnable on a View and Handler result in different bahviour?

有些话、适合烂在心里 提交于 2019-12-22 05:25:34

问题


I've been playing about with Runnables 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!