最近一个优化问题导致我不得不去思考一下这个问题。
一、Fragment的onResume
Fragment的生命周期是与宿主Activity关联的,这里只看onResume方法。因为Activity的绘制操作,是在onResume中进行的,所以想查看Fragment的绘制操作,还要追溯到onResume方法。
上图是Fragment的onResume方法,看到非常简单,只有一行代码。但是注释告诉我们,它是与activity的onResume方法关联的,也就是调用此方法意味着一定调用了activity的onResume方法。那么再去看一下activity的onResume方法。
由于mac上未下载源码,图片后续补上。
Activity的onResume方法,调用了mActivityTransitionState的onResume方法。而这个方法又调用了restoreExitedViews方法。restoreExitedViews方法又调用了mCalledExitCoordinator的resetViews方法。在resetViews方法中调用了decorView的suppressLayout方法。这个suppressLayout方法是ViewGroup的方法,在里面调用了requestLayout方法。这个方法大家应该很熟悉,就是要请求刷新布局了。之后再走view绘制流程。
写到这里我们就可以知道,Fragment的UI绘制,与Activty一样,都是在onResume时候绘制的。下面就来看一看requestLayout方法的原理。
二、requestLayout方法
requestLayout方法是View的方法。而这个方法最终调用了mParent的requestLayout方法。追踪源码发现mParent实际上是ViewRootImpl的实例引用。
而ViewRootImpl的requestLayout方法在执行时,会先调用检测线程方法,如果不是在主线程中,则会抛出异常。这也就是为什么Android中涉及UI的操作一定在主线程中进行的原因。
检测过线程后,会执行scheduleTraversals方法,在此方法中会post一个线程,这个线程中会执行doTraversal方法。而doTraversal中会调用performTraversals方法。
performTraversals方法是View绘制中的关键方法。此方法中顺序执行了performMeasure、performLayout、performDraw方法。而这三个方法最终执行的是View的measure、layout和draw方法。同时在调用这三个方法前,还调用了dispatchAttachedToWindow方法,这个方法将Window和View的关系建立起来了。
从代码中看,requestLayout方法是重新请求了measure、layout和draw。即从头开始完全走了一遍绘制流程。
三、Invalidate方法与postInvalidate方法
这两个方法最终都会调用requestLayout方法,只不过在调用前会做一些标记处理,将需要重新绘制的View进行标记。这样再走三大流程时,只针对标记的View。
而二者的区别在于一个只能运行在主线程,一个可以运行在工作线程。效率上Invalidate要高于postInvalidate。
四、总结
本文是由Fragment布局绘制时机总结引发的针对UI绘制三种方法的思考。
在网上找到了一篇文章,很好地解释三种请求重绘UI方法的区别。
Android View深度分析requestLayout、Invalidate以及postInvalidate的区别
总结下来就是,View不再确定自身大小,如LayoutParams发生了变化,需要父布局重新测量,就使用requestLayout。而如果确定View大小及位置不会变,只是本身内容发生了变化,就可以使用Invalidate以及postInvalidate。