美文网首页android消息处理机制
记一次错误的使用View.post(Runnable)

记一次错误的使用View.post(Runnable)

作者: 轻微 | 来源:发表于2016-03-24 19:33 被阅读1912次

我的应用场景是这样子的
我在一个异步操作中用EventBus 发送了一个事件.
在另外一个Activity 中对这个事件进行接收.
因为是异步所以我使用了View.post方法企图让它回到主线程更新这些数据.(此时我的View 处于Detach 状态)
但是事情并没有这么美好.
我的Runnable方法没有触发Run()方法.
问题出在哪里?

我排查了很久.很久,很久....

我们进入post方法:

/**
 * <p>Causes the Runnable to be added to the message queue.
 * The runnable will be run on the user interface thread.</p>
 *
 * @param action The Runnable that will be executed.
 *
 * @return Returns true if the Runnable was successfully placed in to the
 *         message queue.  Returns false on failure, usually because the
 *         looper processing the message queue is exiting.
 *
 * @see #postDelayed
 * @see #removeCallbacks
 */
public boolean post(Runnable action) {
    final AttachInfo attachInfo = mAttachInfo;
    if (attachInfo != null) {
        return attachInfo.mHandler.post(action);
    }
    // Assume that post will succeed later
    ViewRootImpl.getRunQueue().post(action);
    return true;
}

因为我的View 处于Detach状态. 所以attachInfo 为空.所以我们进入了
ViewRootImpl.getRunQueue().post(action);



/**
 * The run queue is used to enqueue pending work from Views when no Handler is
 * attached.  The work is executed during the next call to performTraversals on
 * the thread.
 * @hide
 */
static final class RunQueue {
    private final ArrayList<HandlerAction> mActions = new ArrayList<HandlerAction>();

    void post(Runnable action) {
        postDelayed(action, 0);
    }

    void postDelayed(Runnable action, long delayMillis) {
        HandlerAction handlerAction = new HandlerAction();
        handlerAction.action = action;
        handlerAction.delay = delayMillis;

        synchronized (mActions) {
            mActions.add(handlerAction);
        }
    }

 
    void executeActions(Handler handler) {
        synchronized (mActions) {
            final ArrayList<HandlerAction> actions = mActions;
            final int count = actions.size();

            for (int i = 0; i < count; i++) {
                final HandlerAction handlerAction = actions.get(i);
                handler.postDelayed(handlerAction.action, handlerAction.delay);
            }

            actions.clear();
        }
    }

正常流程是我们用post 方法加入mActions 中,在performTraversals 中调用executeActions()对这些信息进行消耗.
到这里看起来一切都是那么美好.
问题出在哪里?


static final ThreadLocal<RunQueue> sRunQueues = new ThreadLocal<RunQueue>();

static RunQueue getRunQueue() {
    RunQueue rq = sRunQueues.get();
    if (rq != null) {
        return rq;
    }
    rq = new RunQueue();
    sRunQueues.set(rq);
    return rq;
}

sRunQueues 是一个ThreadLocal 类型,也就是说你上面加入的mActions跟主线程中的mActions 不是同一个.

总结:
当你使用View 处于Detach 状态.
调用线程为非主线程.
View.post并不能把你的runnable带到主线程执行.

BTW:
我们可以看到RunQueue 上面有这样的注释.The work is executed during the next call to performTraversals on the thread. 也就是说当你正确使用了post 方法(你的Runnable 正确加入到主线程的mActions ) 那么他也是要在下一个performTraversals方法中执行.如果 performTraversals 不到来,你的Runnable 是不会执行的.什么时候确保这个快速的到来呢,使用View.invalidate()

相关文章

网友评论

  • 38205d049967:刚写的博客,只有Attach状态下才post才能立马起效,不然只能等待performTraversals激活处理了,http://blog.csdn.net/yuanyang5917/article/details/78054640
  • Heheaaa:用runonUiThread?
  • 7650769b4b02:SDK 24对这个地方做了改变,getRunQueue获得的Queue变成了view的私有变量,并不是从ViewRootImpl的sRunQueues这个ThreadLocal中取。view中的Queue的executeActions方法只会在dispatchAttachedToWindow的时候调用。但是现象还是你这个现象,就不理解为什么了。如果有时间麻烦指导一下,感谢
    轻微:@70kg 在主线程发也不会有.
    7650769b4b02:@轻微 我知道这个结果,在attach的时候怎么发都可以,我现在不理解的现象是在Detach的时候,异步发不会有回调,在主线程发有回调,但是在SDK 24的时候并没有发现类似ThreadLocal作用的代码,所以比较困惑
    轻微:我的测试结果是. 在异步使用post 在attach 的时候 runnable 会有回调的.
  • 白瓦力:EventBus的支持onEvent在主线程执行的呀
    轻微:@白瓦力 嗯
  • 我是Asha:工作线程未attach调post,这个post会被吃掉永远不会执行
    轻微:@我是Asha 其实都是在异步才会有这些问题..
    轻微:@我是Asha 对的, 我是Attach 过的 fragment 再被Detach. :joy:

本文标题:记一次错误的使用View.post(Runnable)

本文链接:https://www.haomeiwen.com/subject/avmrlttx.html