目錄
前言
1、postInvalidate
2、post
3、總結(jié)
前言
我們知道view有一系列post方法,用于在非UI線程中發(fā)出一些頁面處理。view還有另外一個(gè)postInvalidate方法,同樣在非UI線程中發(fā)起重繪。
同樣是在非UI線程向UI線程發(fā)出消息,但是這里面有很大的區(qū)別。 ?
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);}
}
可以看到當(dāng)mAttachInfo為null的時(shí)候,這個(gè)流程就直接結(jié)束了。而mAttachInfo則是當(dāng)view被DetachedFromWindow的時(shí)候會(huì)被置為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();}
}
所以當(dāng)view被從頁面上移除后,postInvalidate就無效了。 當(dāng)mAttachInfo不為null的時(shí)候,則執(zhí)行mViewRootImpl的dispatchInvalidateDelayed函數(shù),代碼如下:
public void dispatchInvalidateDelayed(View view, long delayMilliseconds) {Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view);mHandler.sendMessageDelayed(msg, delayMilliseconds);
}
直接用mHandler發(fā)出了消息。
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;
}
同樣當(dāng)mAttachInfo不為null的時(shí)候,直接使用mHandler發(fā)出消息。 但是!注意但是!當(dāng)mAttachInfo為null時(shí),并不直接結(jié)束流程,而是將runnable存入了一個(gè)RunQueue。RunQueue是一個(gè)隊(duì)列,部分代碼如下:
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 {...}
}
當(dāng)RunQueue的executeActions函數(shù)被調(diào)用時(shí),會(huì)遍歷隊(duì)列再去用handler發(fā)送消息。 那么executeActions什么時(shí)候被調(diào)用? 在ViewRootImpl的performTraversals函數(shù)中,如下:
private void performTraversals() {...// Execute enqueued actions on every traversal in case a detached view enqueued an actiongetRunQueue().executeActions(mAttachInfo.mHandler);...
}
而performTraversals在doTraversal函數(shù)中被調(diào)用
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中一個(gè)Runnable對(duì)象mTraversalRunnable中執(zhí)行
final class TraversalRunnable implements Runnable {@Overridepublic void run() {doTraversal();}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
mTraversalRunnable則在ViewRootImpl的scheduleTraversals函數(shù)中被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則在很多地方被執(zhí)行,比如:
void handleAppVisibility(boolean visible) {if (mAppVisible != visible) {mAppVisible = visible;scheduleTraversals();if (!mAppVisible) {WindowManagerGlobal.trimForeground();}}
}void handleGetNewSurface() {mNewSurfaceNeeded = true;mFullRedrawNeeded = true;scheduleTraversals();
}...
這里就不一一列舉了,大家有興趣可以自己去源碼里搜索一下。 總結(jié)一下就是當(dāng)view被從頁面上移除后,通過post系列函數(shù)傳的消息并不會(huì)立刻用handler發(fā)出去,而是先將其存入一個(gè)隊(duì)列里。當(dāng)view再次被添加到頁面上時(shí),會(huì)從隊(duì)列中的取出消息再用handler發(fā)出去。
3、總結(jié)
所以當(dāng)我們使用
post(new Runnable() {@Overridepublic void run() {invalidate();}
});
它其實(shí)與postInvalidate還是有區(qū)別的。
總結(jié)
以上是生活随笔 為你收集整理的非UI线程下页面处理:view的postInvalidate和post对消息处理的差异化 的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔 推薦給好友。