Android進階之路系列:http://blog.csdn.net/column/details/16488.html
我們知道view有一系列post方法,用於在非UI線程中發出一些頁面處理。view還有另外一個postInvalidate方法,同樣在非UI線程中發起重繪。
同樣是在非UI線程向UI線程發出消息,但是這裏面有很大的區別。1、postInvalidate
先來看看postInvalidatepublic 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 window
final 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
下面再來看看postpublic 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;
}
同樣當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 action
getRunQueue().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 {
@Override
public 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() {
@Override
public void run() {
invalidate();
}
});
它其實與postInvalidate還是有區別的。
Android進階之路系列:http://blog.csdn.net/column/details/16488.html