Android View刷新原理Invalidate()和PostInvalidate()源码分析
来源:互联网 发布:财务部swot矩阵分析表 编辑:程序博客网 时间:2024/05/21 06:52
一般Ui控件使用来简单开发时,并没有注意到系统如何刷新,而当我们自定义View或开发复杂的view时,就需要主动调用Invalidate或者postInvalidate等来通知系统刷新绘制UI,刷新视图。那接下来一个个来剖这两个Api的具体实现。
Invalidate()
invalidate最后调用到invalidateInternal函数,把view的相对尺寸和相关状态设置传递
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)其中invalidateInternal函数中逻辑不少,主要部分如下,有段调用父view进行刷新: // 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); p.invalidateChild(this, damage);}
那ViewParent.invalidateChild实现是在哪里呢,猜到ViewGroup,可以查源码发现ViewGroup类实现了ViewParent的接口,那什么时候子View的ViewParent什么时候被复制,那来看下子View被如何添加到父View,看下ViewGroup.addView时添加子View的逻辑,截取了关键代码:
addInArray(child, index); // tell our childrenif (preventRequestLayout) { child.assignParent(this);} else { child.mParent = this;}
可以看到addView时对child的ViewPrarent变量mParent进行赋值,把自己传递给子View,那么回到到ViewGroup对ViewParent接口中invalidateChild方法实现,发现里面有循环查找父View逻辑,如下:
do { View view = null; if (parent instanceof View) { view = (View) parent; } ********** 省略代码 // If the parent is dirty opaque or not dirty, mark it dirty with the opaque ********** parent = parent.invalidateChildInParent(location, dirty); if (view != null) { // Account for transform on current parent ******** }} while(parent!=null)
可以看到这个do-while循环一直从当前子View轮询查找上层ViewParent知道parent=null即意味着没有父VIew了,就是顶层View,那什么时候parent.invalidateChildInParent(location, dirty)会返回null呢。这里就要分析View树形结构了,布局结构中最上层的ViewParent是谁,什么时候赋值的。这个要从setContentView函数设置布局文件开始讲,有点长,从中看到布局最顶层view就是DecorView(可以查源码),那DecorView的ViewParent又是谁,这个就必须从添加decorView定位。参考http://blog.csdn.net/luoshengyang/article/details/6689748这边老罗的文章,分析应用启动以及View显示加载过程,从中可以知道在AMS通知PerformResumeActivity命令时开始显示界面,会调用activity的makeVisible(),该函数添加了mDecor(DecorView)
void makeVisible() { if (!mWindowAdded) { ViewManager wm = getWindowManager(); wm.addView(mDecor, getWindow().getAttributes()); mWindowAdded = true; } mDecor.setVisibility(View.VISIBLE); }
ViewManager.addView的实现在WindowManagerGlobal的addView方法中,会看到一个ViewRootImpl,看起来很想最最顶层 的View,查找源码发现:并非是个View,但是其实现了ViewParent,这就差不多连起来了,然后其调用了setView方法查看ViewRootImpl实现如下
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { mView = view; ************很多代码省略 ************* view.assignParent(this); mAddedTouchMode = (res & WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE) != 0; mAppVisible = (res & WindowManagerGlobal.ADD_FLAG_APP_VISIBLE) != 0; ************很多代码省略 ************* } } }
发现 mView = view进行赋值,并调用assignParent(this),把这个ViewParent实现付给DecorView,从持有父View。既然ViewRootImp实现了ViewParent,并且付给了DecorView,那在DecorView查找父parent时(parent.invalidateChildInParent(location, dirty))就可以定位到ViewRootImpl实现了,找到ViewRootImp.invalidateChildInParent:发现终于返回了null,结束了循环,源码如下:
@Override public ViewParent invalidateChildInParent(int[] location, Rect dirty) { checkThread(); if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty); 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; }
invalidateRectOnScreen(dirty);又调用了谁,继续定位
private void invalidateRectOnScreen(Rect dirty) { final Rect localDirty = mDirty; if (!localDirty.isEmpty() && !localDirty.contains(dirty)) { mAttachInfo.mSetIgnoreDirtyState = true; mAttachInfo.mIgnoreDirtyState = true; } // Add the new dirty rect to the current one localDirty.union(dirty.left, dirty.top, dirty.right, dirty.bottom); // Intersect with the bounds of the window to skip // updates that lie outside of the visible region final float appScale = mAttachInfo.mApplicationScale; final boolean intersected = localDirty.intersect(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f)); if (!intersected) { localDirty.setEmpty(); } if (!mWillDrawSoon && (intersected || mIsAnimating)) { scheduleTraversals(); } }
可以看到把重新计算传到ViewRootImpl的Rect对象dirty和刷新之前的mDirty进行合并(union),更新了绘制Rect并重新调用schuduleTraversals()再到doTraversals最后调用到performTraversals函数,这个是view绘制的源头开始处,代码很多,里面通过performMeasure()、performLayout()、performDraw()调用了mView.measure() mView.layout 、mView.draw等方法。终于差不多看到希望了,mView.draw()就是调用了DecorView的draw(),然后就把这个布局遍历绘制了一遍。那么走到执行动画的View的其父View绘制draw方法时候,会走到dispatchDraw,并调用了子VIew的Draw()方法实现View的刷新。
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 window if (mAttachInfo != null) { Message msg = Message.obtain(); msg.what = AttachInfo.INVALIDATE_MSG; msg.obj = this; mAttachInfo.mHandler.sendMessageDelayed(msg, delayMilliseconds); } }
这个message被AttachInfo接受处理如下:
attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds);1
继而走到VIewRootImpl的dispatchInvalidateDelayed方法,并把子VIew对象this传递了,可以查看其实现:
public void dispatchInvalidateDelayed(View view, long delayMilliseconds) { Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view); mHandler.sendMessageDelayed(msg, delayMilliseconds); }
@Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_INVALIDATE: ((View) msg.obj).invalidate(); break; //省略很多代码
从而发现最终还是调用了子View的invalidate(),从而得知实际是一个原理,殊途同归,系统这样设计就可以看出其用意区别,是为了增加在非主线程中调用刷新,invalidate是主线程UI操作的,线程不能直接调用,故此通过postInvalidate通过handle来调用了invalidate,实现了线程中直接刷新办法。
总结invalidate()和postInvalidate()
invalidate和postInvalidate从上述源码分析两者唯一区别就是postInvalidate通过handle方式回到了invalidate从而实现了线程可以刷新UI的目的,这个作用在实现负责动画时候,通过子线程的变量改变之后直接调用postinvalidate就可以实现刷新Ui了。
- Android View刷新原理Invalidate()和PostInvalidate()源码分析
- (转)Android开发:Invalidate和postInvalidate刷新View的区别
- Android View 绘制流程 与invalidate 和postInvalidate 分析--从源码角度
- Android View 分析requestLayout、invalidate与postInvalidate
- Invalidate和postInvalidate,android界面刷新
- Invalidate和postInvalidate,android界面刷新
- Android之界面刷新 View更新 Invalidate和postInvalidate的区别
- View的两种更新方法-从源码角度分析invalidate()和postInvalidate()的区别
- 自定义View-invalidate和postInvalidate
- Android View 深度分析requestLayout、invalidate与postInvalidate
- Android View深度分析requestLayout、invalidate与postInvalidate
- view, surfaceView, invalidate, postInvalidate, 刷新屏幕
- view, surfaceView, invalidate, postInvalidate, 刷新屏幕
- 【android-view】android中Invalidate和postInvalidate的区别
- Android之界面刷新(invalidate和postInvalidate使用)
- Android之界面刷新(invalidate和postInvalidate使用)
- android中刷新Invalidate和postInvalidate的区别
- 详解Android中自定义View的invalidate,Handler和postInvalidate
- 山东大学项目实训——5月28日
- java基础之File(具体案例)
- java中的Timer用法(二) 使用举例
- 【C语言】通讯录:动态开辟内存版和文件版本
- CF 811C (DP)
- Android View刷新原理Invalidate()和PostInvalidate()源码分析
- vim配置
- 多线程的执行过程分析笔记
- 1350 [BA1001] The Stock class
- 深入理解javascript原型和闭包(4)——隐式原型
- 网络:TCP维护安全可靠机制提供的定时器
- PE结构
- Oracle查询锁住的表语句
- EVA存储容量公式