1、invalidate()
#View.java
public void invalidate(boolean invalidateCache) {
invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
}
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache, boolean fullInvalidate) {
...
mPrivateFlags |= PFLAG_DIRTY;
if (invalidateCache) {
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
// Propagate the damage rectangle to the parent view.
final AttachInfo ai = mAttachInfo;
final ViewParent p = mParent;
if (p != null && ai != null && l < r && t < b) {
final Rect damage = ai.mTmpInvalRect;
damage.set(l, t, r, b);
//ViewParent
p.invalidateChild(this, damage);
}
...
}
}
#ViewRootImpl.java
public void invalidateChild(View child, Rect dirty) {
invalidateChildInParent(null, dirty);
}
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
checkThread();
...
invalidateRectOnScreen(dirty);
return null;
}
private void invalidateRectOnScreen(Rect dirty) {
...
if (!mWillDrawSoon && (intersected || mIsAnimating)) {
scheduleTraversals();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable,null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
可以看到invalidate()的调用顺序为:
View.invalidate()->View.invalidateInternal()->ViewRootImpl.invalidateChild()->
ViewRoottImpl.invalidateChildInParent()->ViewRootImpl.invalidateRectOnScreen()->
ViewRootImpl.scheduleTraversals()
2、postInvalidate()
#View.java
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 window
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds);
}
}
#ViewRootImpl.java
public void dispatchInvalidateDelayed(View view, long delayMilliseconds) {
Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view);
mHandler.sendMessageDelayed(msg, delayMilliseconds);
}
final class ViewRootHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_INVALIDATE:
((View) msg.obj).invalidate();
break;
case MSG_INVALIDATE_RECT:
final View.AttachInfo.InvalidateInfo info =
(View.AttachInfo.InvalidateInfo) msg.obj;
info.target.invalidate(info.left, info.top, info.right, info.bottom);
info.recycle();
break;
...
}
...
}
可以看到postInvalidate()调用顺序为:
View.postInvalidate()->View.postInvalidateDelayed()->ViewRootImpl.dispatchInvalidateDelayed()->
handleMessage(MSG_INVALIDATE/MSG_INVALIDATE_RECT)->View.invalidate()
所以invalidate()和postinvalidate()的区别就是
postinvalidate()官方的注释:
- Cause an invalidate of the specified area to happen on a subsequent cycle through the event loop. Use this to invalidate the View from a non-UI thread.
可以在非UI线程刷新UI。
3、postInvalidateOnAnimation()
#ViewCompat.java
public static void postInvalidateOnAnimation(@NonNull View view) {
//SDK版本
if (Build.VERSION.SDK_INT >= 16) {
view.postInvalidateOnAnimation();
} else {
view.postInvalidate();
}
}
public void postInvalidateOnAnimation() {
// 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.dispatchInvalidateOnAnimation(this);
}
}
public void dispatchInvalidateOnAnimation(View view) {
mInvalidateOnAnimationRunnable.addView(view);
}
final InvalidateOnAnimationRunnable mInvalidateOnAnimationRunnable =
new InvalidateOnAnimationRunnable();
final class InvalidateOnAnimationRunnable implements Runnable {
private boolean mPosted;
private final ArrayList<View> mViews = new ArrayList<View>();
private final ArrayList<AttachInfo.InvalidateInfo> mViewRects =
new ArrayList<AttachInfo.InvalidateInfo>();
private View[] mTempViews;
private AttachInfo.InvalidateInfo[] mTempViewRects;
public void addView(View view) {
synchronized (this) {
mViews.add(view);
postIfNeededLocked();
}
}
public void removeView(View view) {
synchronized (this) {
mViews.remove(view);
...
if (mPosted && mViews.isEmpty() && mViewRects.isEmpty()) {
mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION, this, null);
mPosted = false;
}
}
}
@Override
public void run() {
final int viewCount;
final int viewRectCount;
synchronized (this) {
...
for (int i = 0; i < viewCount; i++) {
mTempViews[i].invalidate();
mTempViews[i] = null;
}
for (int i = 0; i < viewRectCount; i++) {
final View.AttachInfo.InvalidateInfo info = mTempViewRects[i];
info.target.invalidate(info.left, info.top, info.right, info.bottom);
info.recycle();
}
}
private void postIfNeededLocked() {
if (!mPosted) {
mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);
mPosted = true;
}
}
}
postInvalidateOnAnimation()调用顺序为:
View.postInvalidateOnAnimation()->ViewRootImpl.dispatchInvalidateOnAnimation()->
InvalidateOnAnimationRunnable.postIfNeededLocked()->
Choreographer.CALLBACK_ANIMATION->View.invalidate()
总结
invalidate()会立即调用scheduleTraversals().
postinvalidate()会通过消息机制调用invalidate()最终调用scheduleTraversals().
postInvalidateOnAnimation()会通过Vsync机制插入到Choreographer的ANIMATION队列,下次屏幕刷新信号到来时调用invalidate()然后scheduleTraversals()。这个方法也可以在非UI线程调用。
网友评论