看invalidate源码的目的了解为什么说invalidate调用onDraw方法
public void invalidate() {
invalidate(true);
}
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) {
if (p != null && ai != null && l < r && t < b) {
final Rect damage = ai.mTmpInvalRect;
damage.set(l, t, r, b);
p.invalidateChild(this, damage);
}
}
}
//这个方法是在ViewParenet中的,所以我们去ViewGroup
public void invalidateChild(View child, Rect r);
看ViewGroup的invalidateChild
@Override
public final void invalidateChild(View child, final Rect dirty) {
do {
//调用外部的方法
parent = parent.invalidateChildInParent(location, dirty);
if (view != null) {
// Account for transform on current parent
Matrix m = view.getMatrix();
if (!m.isIdentity()) {
RectF boundingRect = attachInfo.mTmpTransformRect;
boundingRect.set(dirty);
m.mapRect(boundingRect);
dirty.set((int) Math.floor(boundingRect.left),
(int) Math.floor(boundingRect.top),
(int) Math.ceil(boundingRect.right),
(int) Math.ceil(boundingRect.bottom));
}
}
} while (parent != null);
}
public ViewParent invalidateChildInParent(int[] location, Rect r);
//去ViewGroup中找invalidateChildInParent并没有发现自己想要的这时候去找最外面的实现类ViewRootImpl
ViewRootImpl中的invalidateChildInParent
@Override
public void invalidateChild(View child, Rect dirty) {
invalidateChildInParent(null, dirty);
}
@Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
//检查线程是不是主线程
checkThread();
if (DEBUG_DRAW) Log.v(mTag, "Invalidate child: " + dirty);
if (dirty == null) {
invalidate();
return null;
} else if (dirty.isEmpty() && !mIsAnimating) {
return null;
}
if (mCurScrollY != 0 || mTranslator != null) {
mTempRect.set(dirty);
dirty = mTempRect;
if (mCurScrollY != 0) {
dirty.offset(0, -mCurScrollY);
}
if (mTranslator != null) {
mTranslator.translateRectInAppWindowToScreen(dirty);
}
if (mAttachInfo.mScalingRequired) {
dirty.inset(-1, -1);
}
}
//看这个
invalidateRectOnScreen(dirty);
return null;
}
checkThread:这就是为什么不能再子线程中更新UI
void checkThread() {
//mThread 在构造函数中,是主线程
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
invalidateRectOnScreen:一路下看会有个方法scheduleTraversals();,现在主要看这个方法
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//看mTraversalRunnable这个方法
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
//最终看这个
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
doTraversal:
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
//看这个
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
performTraversals:这个方法非常重要,方法非常多,简单讲我们需要关注的
//①
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);//测量的方法
//②
if (didLayout) {
performLayout(lp, mWidth, mHeight);//layout方法
}
//③
performDraw();//draw方法
1.performMeasure
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
//view的测量方法
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
performLayout
try {
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
}
performDraw:选着看
//最后一行看drawSoftware
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
return;
}
//最后在方法中我们会看到 mView.draw(canvas);
到此我们就知道了为什么invalidate会重新调用onDraw方法
postinvalidate源码分析
view中
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);
}
}
public void dispatchInvalidateDelayed(View view, long delayMilliseconds) {
Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view);
mHandler.sendMessageDelayed(msg, delayMilliseconds);
}
switch (msg.what) {
case MSG_INVALIDATE:
((View) msg.obj).invalidate();
break;
其他的和invalidate一样,着我们就可以看到了,postinvalidate在主线程和非主线程中都可以调用,但是Invalidate不能直接在线程中调用
高级面试题:如何像WX朋友圈一样优化过度渲染
看自己界面有没有过度渲染
开发者选项 打开调试GPU过度绘制,不要见红 网上找(99%) 优化(不是特别靠谱)
自己写一些界面会非常复杂 QQ空间 WX朋友圈 列表嵌套列表( Item里面布局可能嵌套布局)
- 1.网上的解决方案
尽量不要嵌套
能不设置背景不要设置背景
........ - 2.最好的解决方案(蛋疼)
获取到数据去设置 setText() setImageView其实 onInvalidate()
最好是自己画,不要用系统的嵌套布局 运行效率高,实现功能效率低(抉择问题)