您的当前位置:首页正文

View.post(Runnable r)使用注意事项

2022-06-15 来源:知库网

1 注意的问题

View.post(Runnable r)很多时候在子线程调用,用于进行子线程无法完成的操作,或者在该方法中通过getMeasuredWidth()获取view的宽高。需要注意的是,在子线程调用该函数,可能不会被执行,原因是该view不是attached状态。

2 View.post源码分析

  • 2.1 View
private HandlerActionQueue mRunQueue;
public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);
        }
        // Postpone the runnable until we know on which thread it needs to run.
        // Assume that the runnable will be successfully placed after attach.
        getRunQueue().post(action);
        return true;
    }

private HandlerActionQueue getRunQueue() {
        if (mRunQueue == null) {
            mRunQueue = new HandlerActionQueue();
        }
        return mRunQueue;
    }

如果view已经attached,则调用ViewRootImpl中的ViewRootHandler,放入主线程Lopper等待执行。如果detach,则将其暂存在RunQueue当中,等待其它线程取出执行。

  • 2.2 HandlerActionQueue
 private HandlerAction[] mActions;
 public void executeActions(Handler handler) {
        synchronized (this) {
            final HandlerAction[] actions = mActions;
            for (int i = 0, count = mCount; i < count; i++) {
                final HandlerAction handlerAction = actions[i];
                handler.postDelayed(handlerAction.action, handlerAction.delay);
            }
            mActions = null;
            mCount = 0;
        }
    }

暂存在RunQueue中的Runnable,在executeActions时被取出。

  • 2.3 ViewRootImpl

    在performTraversals()中调用

    performTraversals.png
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;
    }

//performTraversals内部

void performTraversals(){
// Execute enqueued actions on every traversal in case a detached view enqueued an action
   getRunQueue().executeActions(attachInfo.mHandler);
……
if (!mStopped) {
……
                     // Ask host how big it wants to be
                    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
……
}
……
if (didLayout) {
            performLayout(lp, desiredWindowWidth, desiredWindowHeight);
……

if (!cancelDraw && !newSurface) {
            if (!skipDraw || mReportNextDraw) {
……
                performDraw();
            }
        } else {
            if (viewVisibility == View.VISIBLE) {
                // Try again
                scheduleTraversals();
            } else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
                for (int i = 0; i < mPendingTransitions.size(); ++i) {
                    mPendingTransitions.get(i).endChangingAnimations();
                }
                mPendingTransitions.clear();
            }
        }

可以看到,如果在detached的状态下调用post方法,在主线程的ViewRootImpl中调用getRunQueue时,由于RunQueue是线程私有,所以会获取不到原来存放runnable的RunQueue,继而新建一个新的RunQueue,当然就没有执行我们想执行的代码了。

3 什么时候attach和detach?

onResume()调用完成之后,回调View的onAttachToWindow()之前attach。
onDestroy()调用完成之后,回调View的onDettachToWindow()之前detach。
即如果在主线程调用,任何时候都可以执行。在子线程调用,必须在该view处于attached状态下才能保证执行。

显示全文