重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
一、优先级别不同:iOS最先响应屏幕
创新互联网站建设公司一直秉承“诚信做人,踏实做事”的原则,不欺瞒客户,是我们最起码的底线! 以服务为基础,以质量求生存,以技术求发展,成交一个客户多一个朋友!专注中小微企业官网定制,做网站、成都网站设计,塑造企业网络形象打造互联网企业效应。
当我们使用iOS或者是Android手机时,第一步就是滑屏解锁找到相应程序点击进入。而这个时候往往是所有操控开始的第一步骤,iOS系统产品就表现出来了流畅的一面,但Android产品却给人一种卡顿的现象,更别说后续深入玩游戏或者进行其它操控了。这是为什么?
其实这与两个系统的优先级有关,iOS对屏幕反应的优先级是最高的,它的响应顺序依次为Touch--Media--Service--Core架构,换句话说当用户只要触摸接触了屏幕之后,系统就会最优先去处理屏幕显示也就是Touch这个层级,然后才是媒体(Media),服务(Service)以及Core架构。而Android系统的优先级响应层级则是Application--Framework--Library--Kernal架构,和显示相关的图形图像处理这一部分属于Library,你可以看到到第三位才是它,当你触摸屏幕之后Android系统首先会激活应用,框架然后才是屏幕最后是核心架构。
优先级的不同导致了iOS产品以及Android手机在操控过程中的表现差异,当你滑动屏幕进行操控的时候,iOS系统会优先处理Touch层级,而Android系统则是第三个才响应Library层级,这是造成它们流畅度不同的因素之一。
二、硬件工作配置不同:iOS基于GPU加速
目前智能手机硬件装备竞赛当中,其实处理器等配置已经达到了一个瓶颈期,各大旗舰产品在硬件比拼当中基本上没有太大的区别,而这时候GPU就成为了一个凸显差异的重要因素。一些大型软件像是3D游戏对GPU性能要求都会比较高,苹果iPhone产品采用的Power VR SGX系列GPU在当下来说非常的主流,跑分测试数据证明了它并不会比一些旗舰级别的Android产品差劲。
而iOS系统对图形的各种特效处理基本上正好都是基于GPU硬件进行加速的,它可以不用完全借助CPU或者程序本身,而是通过GPU进行渲染以达到更流畅的操控表现。但是Android系统产品则并非如此,因为Android需要适应不同的手机硬件,需要满足各种差异配置,所以很多图形特效大多都要靠程序本身进行加速和渲染,并严重依赖CPU运算的操作自然会加大处理器的负荷,从而出现卡顿的问题。虽然Android 4.0以及4.1等更高版本中进行了改进将硬件加速设为默认开启,但依旧无法做到所有特效全部都靠GPU进行加速。在很多Android手机里面都自带有“是否开启GPU渲染”这个功能选项,不过开启之后的改善也是微乎其微。
屏幕最先响应的优先级关系,再加上iSO本身GPU加速程序的特性,使得大家在操控过程中感觉iOS手机拥有着不错的流畅性。因为它本身的整个流程都是在为最大化的流畅做服务,不管是第一印象的滑动接触屏幕,还是你进一步使用程序之后的更深层操作都是如此。而GPU加速这点特性,应该是它优于Android系统流畅性的又一个因素。
三、开发机制不同:安卓机制效率低
Android的编程语言是JAVA,而iOS的则为Objective-C,不过要是说Android系统之所以有些卡顿是因为JAVA开发语言的关系,或者是拿它和Objective-C对比肯定会有人提出质疑。Objective-C的优势是效率高但比较“唯一”,而JAVA的优势则是跨平台不过运行效率相对偏低,其实这两个编程语言所带来的机制不同,就已经造成了各自系统之间的流畅性差异化。
iOS的Objective-C,编译器gcc,而这个gcc编译出来的代码又被苹果专为iOS架构优化到了极致,运行过程中也不需要虚拟机在中间插手,执行效率自然很高。这一段话应该是iOS系统本身运行程序的执行过程,而Android是通过JAVA虚拟机来执行,并且系统需要占用大量内存来换取执行速度,再加上不定期的内存自动回收机制,从而直接导致了卡顿现象的出现。
Android的JAVA编程本身运行效率比Objective-C低一些,而且再加上内存自动回收的机制,所以造成了一些卡顿不流畅的现象出现。但根据技术人员讲解,现代的JAVA虚拟机效率已经不再是最大的瓶颈,Android 4.0系统版本之后的卡顿现象明显得到了改善,所以这也是有用户并没有发现自己新买的Android手机出现太多卡顿现象的原因。看来编程语言和机制已经被Android进行了改善,这同样也不是造成它与iOS流畅性偏差的唯一因素,不过影响却是实实在在存在着。
三、系统设计不同:安卓APP无法统一
因为iOS产品的封闭性,所以所有的APP运行对象都比较单一,因为每个应用程序都是被运行在iPhone,iPad等iOS产品当中,它们有着很高的硬件利用效率。因为iOS系统的配件供应商只有那么几家,CPU也是一年换一次,这点不像Android终端年年变月月变,开发者很难遇见未来终端分辨率会包含多少种,GPU驱动会包含哪些等等,所以相对来说Android应用开发成本较高且收益较慢。而iOS应用开发则因为软硬件垂直整合而受益,这样一来苹果自然就保证了应用本身其与硬件产品之间的完美结合程度。
其实Android和iOS两大系统APP开发情况的不同,也正是它们开发和不开放的特性所造成的。如果要是拿旗舰Android手机加上一个专为这款旗舰产品设计的游戏,来和苹果iPhone运行对比的话,你真的不会遇到Android旗舰机出现卡顿延迟的问题,为什么因为这款游戏针对这款手机设计,在软硬等方面都达到了最大化的兼容和优化,自然就不会出现停滞的现象。
而Android系统程序要被安装在各种符合要求的手机上面,开发者也不可能针对所有的机器型号进行开发,只能在比较主流的机器上进行测试并保证运行效果,所以他们为了兼顾整个产品线只能不得不降低游戏体验以达到高中低产品可以共用的效果。最后那些占据了Android终端份额的大量大众用户们由于自己的手机不是旗舰产品而得不到流畅的使用体验,自然而然就会产生Android产品不如iOS流畅的抱怨。
不管是iOS产品感觉比Android流畅还是真的比它流畅,其实说到底原因很简单。苹果会花费一年甚至两年的时间去开发一个桌面icon,一种字体,并去测试屏幕点位,而Android终端中除了Nexus系列之外似乎没有太多产品可以做到用这么长的时间去做这么细致的事情。有网友说得好,Android做的更多的是“让系统跑起来”,而iOS拥有着苹果做的更多的则是“让系统以最高的效率跑起来”,或许这就是iOS产品比Android更流畅的原因吧。但更好的一面的是,随着谷歌对Android的持续升级以及各厂商对自家产品的循序改进,使得越来越多的Android终端正在摆脱卡顿不流畅的束缚,未来安卓用户的期待同样有望得到更好的满足。
[img]背景
前不久我做了一个富文本编辑工具,编辑器遇到了一个性能问题是添加多张图片,当滚动编辑区域,遇到图片切换的时候会有明显的卡顿现象。这篇文章基于这个卡顿的性能问题进行性能瓶颈的分析以及做对应的优化。
可以打开这个链接 iOS使用UITableView实现的富文本编辑器 查看我的文章,这篇文章所用的项目也是基于这个项目的。
结果
最终的分析优化的结果把时间从90ms的数量级降低到了2ms的数量级,达到了一个比较流畅的效果。具体的分析优化步骤请往下看。
问题分析
既然问题是发生在图片切换的时候,图片是放在单独的一个Cell中的,那么就尝试在Cell的渲染方法 cellForRowAtIndexPath 添加两个Log,查看方法执行所用的时间。
对应的结果:
从日志打印的时间上看,大概每渲染一个Cell只要发几毫秒的时间,貌似问题不会出现在这个位置,然而这并不是真相,很明显的,其他地方不会影响到,所以得用更高级的分析工具去分析查看。
发现问题
Instrument是一个很好的性能分析工具,可以分析内存分配、内存泄漏、网络情况、CPU占用等和性能有关的问题,当前的性能问题是耗时的问题,可以使用 Instrument 的 Time Profiler 进行分析
让这个列表滚动,并且有进行图片Cell的切换
可以看到Time Profiler 有下面的记录,红色框中就是Cell切换所耗费的时间值,这个时间的增长很明显的高于其他值了,所以这个就是我们要定位到的地方了。
Tips
alt + 鼠标滚轮 - 缩放时间轴
shift + 鼠标滚轮 - 移动时间轴
按住鼠标框选 - 选择和定位时间轴
第一步要在时间轴上框选一个范围,标识选择这个范围进行分析,才能准确定位到这个问题,如图(1)位置所示;第二步要选在堆栈中的某一个函数,一般的选择到OC函数调用,更底层的函数调用就到了CF层是C语言实现的就不好分析了,所以这里选择的是 [UIImage drawInRect:blendMode:alpha] 这个函数分析,可以看到这个函数调用说花费的时间是 92ms,这是一个比较长的时间了,所以应该就是这里导致的卡顿了。
这个函数花费的时间和image图片的大小有关系的,选择另一个时间峰值范围,这个时间峰值范围是发生在小图之间的切换的
这个地方耗费的时间就比较小一点,不过也是达到了25ms,对于性能也是有一定的影响的。
解决问题
以上的分析可以得出结论:[UIImage drawInRect:blendMode:alpha] 函数的调用是会导致性能问题的,因为UITextView内部处理图片的方式是通过调用 [UIImage drawInRect:blendMode:alpha] 函数绘制图片实现的。
既然是UITextView内部的处理方式,所以这个函数调用行为是应用层改变不了的,不过UIImage对象是我们可以控制的,或者可以改变图片的显示方式来达到优化的目的,所以就有了以下的两种方案。
方案1
第一种方案就是对预览的图片进行压缩,然后再设置到NSTextAttachmen中,放到UITextView中显示
这样修改之后大图的滚到加载时间减少到了40ms左右
虽然减少了一半的时间,不过,40ms的时间还是比较长的,下面会继续进行优化。
方案2
上面的方案进行了图片的压缩,时间的耗费还是因为 [UIImage drawInRect:blendMode:alpha] 函数的调用,所以有没有一种更好的方案呢?答案是肯定的,可以把传给UITextView的image压缩成一个很小的,(这一步也可以不必,传递一个空的UIImageView对象即可,这里设置图片的主要原因是图片区域需要一个编辑的光标),然后在 UITextView 所对应的图片区域添加一个UIImageView,在UIImageView中设置原始的图片即可,这种方案会比方案1的效果好很多。
方案二几个修改点:
设置NSTextAttachment的image为空的UIImage对象
Cell添加ImageView显示Image
下面是使用方案2优化之后的分析图
图中可以看到 cellForRowAtIndexPath 方法总共占用了2ms的时间,从分析的堆栈中可以看到 UITextView setAttributedText: 方法才占用了1ms的时间,所以这个提升是很明显的,因为传递了一个空的UIImageView对象,不用执行 [UIImage drawInRect:blendMode:alpha] 方法,使用了UIImageView直接设置Image的方式几乎不会占用时间,所以堆栈中看不到 [UIImageView setImage:] 方法调用的时间。
总结
Instrument是一个很好工具,你用它可以很方便的帮我们定位到性能问题,问题找到了,那么也就很容易找到解决方案了。
面试的时候总会遇到以下问题:
在屏幕成像的过程中,CPU和GPU起着至关重要的作用。
那CPU和GPU是怎么协作呢?
一个app的展示会包含很多内容,诸如,label,imageview,button等等。这些控件的位置,大小,颜色则都是由CPU来计算,计算完成后CPU会将这些数据提交给GPU来进行渲染,只有经过GPU的渲染才能显示在屏幕上。GPU做的操作则是:将收到的数据转成屏幕能显示的数据格式,所以要进行渲染的操作。渲染的操作是直接放在帧缓存(缓存区)。然后视频控制器从缓存区 读取的数据显示在屏幕上。就完成了一个显示的操作。
在屏幕显示过程中是有信号发送的。一帧一帧的。
屏幕内容是怎么显示到屏幕上的?
CPU(红色)——GPU(蓝色)
1.CPU完成计算,提交给GPU渲染,这是来个垂直同步信号,则会将渲染的内容显示到屏幕上。
2.CPU计算时间正常,CPU渲染时间短,等待VSync
3.CPU计算时间正常或慢,GPU渲染时间长,这时来了VSync,而这一帧还没有渲染完,那么就会出现掉帧现象,屏幕回去显示上一帧的画面。这样就产生了卡顿。
4.而当下一帧VSync出现时,丢掉的那一帧画面才会出现。
1.On-SCreen Rendering:当前屏幕渲染,在当前用语显示的屏幕缓冲区进行渲染操作。
2.Off-Screen Rendring: 离屏渲染,在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作。
1.需要创建新的缓冲区;
2离屏渲染的整个过程,需要多次切换上下文环境,先是从当前屏幕切换到离屏;等到离屏渲染结束以后,将离屏缓冲区的渲染结果显示到屏幕上,又需要将上下文环境从离屏切换到当前屏幕
1.光栅化,layer.shouldRasterize = YES
2.遮罩,layer.mask
3.圆角,同时设置layer.maskToBounds = Yes,Layer.cornerRadis 大于0
考虑通过CoreGraphics绘制裁剪圆角,或者美工提供圆角图片
4.阴影,layer.shadowXXX
如果设置了layer.shadowPath就不会产生离屏渲染
平时所说的“卡顿”主要是因为在主线程执行了耗时的操作。
可以添加Observer到主线程RunLoop中,通过监听RunLoop状态切换的耗时,以达到监听卡顿的目的
推荐一个库:LXDAppFluecyMonitor