目錄
前言
1、postInvalidate
2、post
3、總結
前言
我們知道view有一系列post方法,用于在非UI線程中發出一些頁面處理。view還有另外一個postInvalidate方法,同樣在非UI線程中發起重繪。
同樣是在非UI線程向UI線程發出消息,但是這里面有很大的區別。 ?
1、postInvalidate
先來看看postInvalidate
public void postInvalidate() {postInvalidateDelayed(0);
}public void postInvalidateDelayed(long delayMilliseconds) {// We try only with the AttachInfo because there's no point in invalidating// if we are not attached to our windowfinal AttachInfo attachInfo = mAttachInfo;if (attachInfo != null) {attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds);}
}
可以看到當mAttachInfo為null的時候,這個流程就直接結束了。而mAttachInfo則是當view被DetachedFromWindow的時候會被置為null,代碼如下:
void dispatchDetachedFromWindow() {AttachInfo info = mAttachInfo;if (info != null) {int vis = info.mWindowVisibility;if (vis != GONE) {onWindowVisibilityChanged(GONE);}}onDetachedFromWindow();onDetachedFromWindowInternal();InputMethodManager imm = InputMethodManager.peekInstance();if (imm != null) {imm.onViewDetachedFromWindow(this);}ListenerInfo li = mListenerInfo;final CopyOnWriteArrayList<OnAttachStateChangeListener> listeners =li != null ? li.mOnAttachStateChangeListeners : null;if (listeners != null && listeners.size() > 0) {for (OnAttachStateChangeListener listener : listeners) {listener.onViewDetachedFromWindow(this);}}if ((mPrivateFlags & PFLAG_SCROLL_CONTAINER_ADDED) != 0) {mAttachInfo.mScrollContainers.remove(this);mPrivateFlags &= ~PFLAG_SCROLL_CONTAINER_ADDED;}mAttachInfo = null;if (mOverlay != null) {mOverlay.getOverlayView().dispatchDetachedFromWindow();}
}
所以當view被從頁面上移除后,postInvalidate就無效了。 當mAttachInfo不為null的時候,則執行mViewRootImpl的dispatchInvalidateDelayed函數,代碼如下:
public void dispatchInvalidateDelayed(View view, long delayMilliseconds) {Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view);mHandler.sendMessageDelayed(msg, delayMilliseconds);
}
直接用mHandler發出了消息。
2、post
下面再來看看post
public boolean post(Runnable action) {final AttachInfo attachInfo = mAttachInfo;if (attachInfo != null) {return attachInfo.mHandler.post(action);}// Assume that post will succeed laterViewRootImpl.getRunQueue().post(action);return true;
}
同樣當mAttachInfo不為null的時候,直接使用mHandler發出消息。 但是!注意但是!當mAttachInfo為null時,并不直接結束流程,而是將runnable存入了一個RunQueue。RunQueue是一個隊列,部分代碼如下:
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 removeCallbacks(Runnable action) {...}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();}}private static class HandlerAction {...}
}
當RunQueue的executeActions函數被調用時,會遍歷隊列再去用handler發送消息。 那么executeActions什么時候被調用? 在ViewRootImpl的performTraversals函數中,如下:
private void performTraversals() {...// Execute enqueued actions on every traversal in case a detached view enqueued an actiongetRunQueue().executeActions(mAttachInfo.mHandler);...
}
而performTraversals在doTraversal函數中被調用
void doTraversal() {if (mTraversalScheduled) {mTraversalScheduled = false;mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);if (mProfile) {Debug.startMethodTracing("ViewAncestor");}performTraversals();if (mProfile) {Debug.stopMethodTracing();mProfile = false;}}
}
doTraversal則在ViewRootImpl中一個Runnable對象mTraversalRunnable中執行
final class TraversalRunnable implements Runnable {@Overridepublic void run() {doTraversal();}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
mTraversalRunnable則在ViewRootImpl的scheduleTraversals函數中被post出去
void scheduleTraversals() {if (!mTraversalScheduled) {mTraversalScheduled = true;mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);if (!mUnbufferedInputDispatch) {scheduleConsumeBatchedInput();}notifyRendererOfFramePending();pokeDrawLockIfNeeded();}
}
而scheduleTraversals則在很多地方被執行,比如:
void handleAppVisibility(boolean visible) {if (mAppVisible != visible) {mAppVisible = visible;scheduleTraversals();if (!mAppVisible) {WindowManagerGlobal.trimForeground();}}
}void handleGetNewSurface() {mNewSurfaceNeeded = true;mFullRedrawNeeded = true;scheduleTraversals();
}...
這里就不一一列舉了,大家有興趣可以自己去源碼里搜索一下。 總結一下就是當view被從頁面上移除后,通過post系列函數傳的消息并不會立刻用handler發出去,而是先將其存入一個隊列里。當view再次被添加到頁面上時,會從隊列中的取出消息再用handler發出去。
3、總結
所以當我們使用
post(new Runnable() {@Overridepublic void run() {invalidate();}
});
它其實與postInvalidate還是有區別的。
總結
以上是生活随笔 為你收集整理的非UI线程下页面处理:view的postInvalidate和post对消息处理的差异化 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。