隐蔽在背后的Surface
日常平凡同窗们都知道,我们的视图须要被绘制。那么它们被绘制到那了呢?也许很多童鞋脑海里急速浮现出一个词:Canvas。然则,~没错!就是绘制到了Canvas上。那么Canvas又是怎么来的呢?是的,它可以New出来的。然则前面提到过,我们Window中的视图树都是被绘制到一个由Surface供给的Canvas上。忘了的童鞋面壁思过😄。
Canvas实际代表了一块内存,用于储存绘制出来的数据。在Canvas的构造器中你可以看到:
- public Canvas() {
- ...
- mNativeCanvasWrapper = initRaster(null);
- //申请一块内存,并且返回该内存的一个long类型的标记或者索引。
- ...
- }
可以看到,Canvas实际重要就是持有了一块用于绘制的内存块的索引long mNativeCanvasWrapper。每次绘制时就经由过程这个索引找到对应的内存块,然后将数据绘制到内存中。比如:
- public void drawRect(@NonNull RectF rect, @NonNull Paint paint) {
- native_drawRect(mNativeCanvasWrapper,
- rect.left, rect.top, rect.right, rect.bottom, paint.getNativeInstance());
- //在mNativeCanvasWrapper标记的内存中绘制一个矩形。
- }
简单的说一下。Android绘制图形是经由过程图形库Skia(重要针对2D)或OpenGL(重要针对3D)进行。图形库是个什么概念?就比如你在PC上用画板画图,此时画板就相当于Android中的图形库,它供给了一系列标准化的对象供我们画图应用。比如我们drawRect()实际就是操作图形库在内存上写入了一个矩形的数据。
扯多了,我们持续回到Surface上。当ViewRootImpl履行到draw()办法(即开端绘制图形数据了),会根据是否开启了硬件(大年夜Android 4.0开端默认是开启的)加快来决定是应用CPU软绘制照样应用GPU硬绘制。如不雅应用软绘制,图形数据会绘制在Surface默认的CompatibleCanvas上(和通俗Canvas的独一差别就是对Matrix进行了处理,进步在不合设毕喔赡兼容性)。如不雅应用了硬绘制,图形数据会被绘制在DisplayListCanvas上。DisplayListCanvas会经由过程GPU应用openGL图形库进行绘制,是以具有更高的效力。
前面也简单说了一下,每一个Window都邑有一个本身的Surface,也就是说一个应用法度榜样中会存在多个Surface。经由过程膳绫擎的讲解,童鞋们也都知道了Surface的感化就是治理用于绘制视图树的Canvas的。这个Surface是和SurfaceFlinger共享,大年夜它实现了Parcelable接口也可以才想到它会被序列化传递。事实上,Surface中的绘制数据是经由过程匿名共享内存的方法和SurfaceFlinger共享的,如许SurfaceFlinger可以根据不合的Surface,找到它所对应的内存区域中的绘制数据,然落后行合成。
合成师SurfaceFlinger
SurfaceFlinger是体系的一个办事。前面也一向在提到它专门负责把每个Surface中的内容合成缓存,以待显示到屏幕上。SurfaceFlinger在合成Surface时是根据Surface的Z-order次序一层一层进行。比如一个Dialog的Surface就会在Activity的Surface膳绫擎。然后这个器械不多提了。
Vsync(垂直同步)是什么?
终于可以说说你的App为什么这么卡的原因了
经由过程对Android绘制机制的懂得,我们知道造成应用卡顿的根源就在于16ms内不克不及完成绘制衬着合成过程,因为Android平台的硬件刷新率为60HZ,大年夜概就是16ms刷新一次。如不雅没能在16ms内完成这个过程,就会使屏幕反复显示上一帧的内容,即造成了卡顿。在这16ms内,须要完成视图树的所有测量、构造、绘制衬着及合成。而我们的优化工作重要就是针对这个过程的。
复杂的视图树
如不雅视图树复杂,会使全部Traversal过程变长。是以,我们在开辟过程中要控制视图树的复杂程度。削减不须要的层级嵌套。比如应用RelativeLayout可以削减复杂构造的嵌套。比如应用【震动!这个控件绝对值得收藏。轻松实现圆角、文字描边、状况指导等效不雅http://www.jianshu.com/p/cfe18cbc6924】😄,这个控件可以削减既须要显示文字,又须要图片和特别背景的需求的构造复杂程度,所有的器械由一个控件实现。
频繁的requestlayout()
如不雅频繁的触发requestLayout(),就可能会导致在一帧的周期内,频繁的产生构造计算,这也会导致全部Traversal过程变长。有的ViewGroup类型的控件,比如RelativeLayout,在一帧的周期内会经由过程两次layout()操作来计算确认子View的地位,这种少量的操作并不会引起可以或许被留意到的机能问题。然则如不雅在一帧的周期内频繁的产生layout()计算,就会导致严重的机能,每次计算都是要消费时光的!而requestLayout()操作,会向ViewRootImpl一一个名为mLayoutRequesters的List集合里添加须要从新Layout的View,这些View将鄙人一帧中全部从新layout()一遍。平日在一个控件加载之后,如不雅没什么变更的话,它不会在每次的刷新中都从新layout()一次,因为这是一个费时的计算过程。所以,如不雅每一帧都有很多View须要进行layout()操作,可想而知你的界面将会卡到爆!卡到爆!须要留意,setLayoutParams()最终也会调用requestLayout(),所以也不克不及烂用!同窗们在写代码的过程中必定要谨慎留意那些可能引起requestLayout()的处所啊!
推荐阅读
当我第一次收到银行发来的“安然”邮件时,我第一反竽暌功就是这里是否有诈?因为在我看来,它实袈溱是太像垂纶邮件了。这封躺在收件箱里的邮件来源竽暌冠我银行经理的小我邮箱地>>>详细阅读
本文标题:用两张图告诉你,为什么你的App会卡顿?
地址:http://www.17bianji.com/lsqh/35508.html
1/2 1