>>分享Android开发相关的技术 书籍支持  卫琴直播  品书摘要  在线测试  资源下载  联系我们
发表一个新主题 开启一个新投票 回复文章 您是本文章第 20009 个阅读者 刷新本主题
 * 贴子主题:  UI渲染机制以及优化 回复文章 点赞(0)  收藏  
作者:javathinker    发表时间:2020-03-08 15:37:55     消息  查看  搜索  好友  复制  引用

                                                                                                

Android性能优化:UI渲染机制以及优化

本文将暂不对 SurfaceFlinger服务的执行流程进行剖析,而是从更加底层的角度来了解APP的渲染机制,同时引出在渲染过程中遇到的性能问题,以及如何去发现、优化它们。

1. 渲染机制分析

1.1 渲染机制

对于开发者来说,APP的界面主要是由 XML布局文件来表现的,每个 XML布局文件中又包括了很多视图组件,比如ImageView、Button、TextView等等,它们分别表示了图片、按钮、字符串等不同的信息,那么这些复杂的XML布局文件和标记语言,又是如何转化成为用户能够看得懂的图像的呢?答案是: 格栅化(Rasterization)!所谓格栅化,就是将例如 字符串、 按钮、 路径或者 形状等的一些高级对象,拆分到不同的像素屏幕上进行显示。格栅化是一个非常费时的操作,CPU作为中央处理器本身任务就比较繁重,因此人们引入了 GPU(图像处理器)这块特殊的硬件来加快格栅化操作(硬件加速)。格栅化操作大体如下:

点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

接下来,我们就来详细了解XML布局文件中的UI组件是如何被格栅化并渲染显示在屏幕上的?在APP绘制渲染过程中,与绘制渲染有关的硬件主要有CPUGPU(图形处理器),其中,CPU负责把UI组件计算成 Polygons(多边形)或 Texture(纹理),而GPU负责格栅化和渲染工作。CPU和GPU通过 图形驱动层进行连接,这个 图形驱动层维护了一个 Display List队列,它们分别充当 生产者和 消费者,协作完成具体的绘制渲染过程。绘制渲染过程如下图所示:

点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小
  • 渲染流程如下:
  首先, CPU会对UI组件(View)进行 Measure、 Layout、 Record、 Execute的数据计算工作,以将其计算成的 Polygons(多边形)或 Texture(纹理),它们是GPU能够识别的对象,而承载这些信息的是一个被称为 Display List的结构体,它持有所有交给GPU绘制到屏幕上的数据信息,包含GPU要绘制的全部对象的信息列表和执行绘制操作的OpenGL命令列表。在某个View第一次需要被渲染的时候, Display List会因此被创建,当这个View需要显示到屏幕上时,GPU接收到绘制指令后会执行该 Display List。每个View拥有自己的 Display List, Display Lis本身也构成一个树状的结构,跟 View Hierachy(视图树)保持一致。

点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

其次,由于每次从CPU计算得到的 Polygons或 Texture提交(转移)GPU是一件比较麻烦且耗时的事情( 注:实际提交的是Display List),因此Android系统又引入了OpenGL ES,该库API允许将那些需要渲染的 Polygons或 Texture存储(Hold)在GPU存储器(显存)中,当下一次需要渲染的时候只需要在GPU存储器里引用它,然后告诉OpenGL如何绘制就可以了,而不再需要经过CPU上传。需要注意的是,假如View的绘制内容发生变化,那么GPU Memory存储的 Display List就无法再继续使用,这时就需要CPU重新计算创建 Display List并重新执行指令更新到屏幕。

点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

最后,当GPU完成对纹理的格栅化、渲染后,它会将渲染的结果放入 帧缓冲区,视频控制器会按照 VSync(垂直同步)信号逐行读取帧缓冲区的数据,经过可能的数模转换传递给显示器显示。
  • CPU、OpenGL与GPU之间的关系

    点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

1.2 卡顿现象

前面说到,当GPU渲染完成后会将渲染的结果存储到 帧缓冲区,视频控制器会按照 VSync(垂直同步)信号逐行读取帧缓冲区的数据,经过可能的数模转换传递给显示器显示。那么问题来了,什么是VSYNC信号?在讲解VSYNC之前,我们需要了解两个相关的概念: Refresh Rate和 Frame Rate。
  •   Refresh Rate(刷新频率)
表示屏幕在一秒内被刷新的次数,取决于硬件的固定参数,目前大部分手机这个固定参数(屏幕刷新频率)为 60Hz。也就是说,Android系统每隔约 16ms(1000ms/60=16.66ms)就会重新刷新一次界面(Activity),因此我们有16ms的时间去完成每帧的绘制、渲染的逻辑操作。

点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小
  •   Frame Rate(帧率)
表示GPU在一秒内能够渲染的帧数,通常为 60fps。为什么是 60fps?这是因为人眼与大脑之间的协作无法感知超过 60fps的画面更新。简单的说,人眼看到的动画其实都是一帧帧连续播放的静态图片,当播放的速度达到1秒针24帧( 24fps)时,对于人眼感知来说就是连续的线性运动。当然低于30fps在某些场景仍然无法顺畅表现绚丽的画面,此时就需要 60fps来达到想要的效果。

     现在我们再来理解下VSYNC信号:**屏幕的刷新过程是每一行从左到右(水平刷新, Horizontal Scanning),从上到下(垂直刷新,Verical Scanning)。当整个屏幕刷新完毕,即一个垂直刷新周期完成,会有短暂的空白期,此时Android系统就会发出VSYNC信号,触发下一帧对UI的渲染、显示。VSYNC是一种定时中断,一旦收到VSYNC信号CPU就开始处理帧数据,通常Android系统每隔16ms发出VSYNC信号,这个周期时间由屏幕刷新频率决定。**通常来说,帧率超过刷新频率只是一种理想的状态,在超过 60fps的情况下,GPU所产生的帧数据会因为等待 VSYNC的刷新信息而被Hold住,这样能够保持每次屏幕刷新都有实际的新的数据可以显示。但是基本上我们遇到的是 帧率<=屏幕刷新频率的情况,尤其是在帧率小于刷新频率时,会出现待 VSYNC信号到来时,屏幕没有可以刷新的数据,即帧缓冲区还是之前的那帧图像,这就会导致屏幕显示的该帧画面内容仍然是上一帧的画面,而这种现象我们就称之为" 掉帧",对于用户来说,就是界面出现了 卡顿现象,即运行不流畅。

点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

由此我们可以得出,Android系统之所以会出现 卡顿现象是因为的某些操作耗费了帧间隔时间( 16ms),导致其他类似计算、渲染等操作的可用时间就会变少,在 16ms无法完成正常的绘制、渲染工作。当下一个VSYNC信号到来时,Android系统尝试在屏幕上绘制新的一帧,但是新的一帧还没准备好,就会导致无法进行正常渲染,画面就不会刷新,用户看到的还是上一帧的画面,从而发生了丢帧。产生卡顿的原因有很多,主要有以下几点:
  •   布局Layout过于复杂,无法在16ms内完成渲染;
  •   同一时间动画执行的次数过多,导致CPU或GPU负载过重;
  •   View过度绘制,导致某些像素在同一帧时间内被绘制多次
  •   在UI线程中做了很多耗时的操作;
  •   GC回收时暂停时间过长或者频繁GC产生大量的暂停时间(内存抖动);

1.3 内存抖动

在1.2小节我们谈到,大量不停的GC操作会显著占用帧间隔时间( 16ms),如果在帧间隔时间里面做了过多的GC操作,那么自然其他类似计算,渲染等操作的可用时间就变得少了,就容易出现丢帧现象,而导致这种频繁GC的原因之一就是“ 内存抖动(Memory Churn)”。内存抖动是因为大量的对象被创建又在短时间内马上被释放,瞬间产生大量的对象会严重占用 年轻代(Yong Generation)的内存区域,当达到阀值时剩余空间不够的时候,也会触发GC。即使每次分配的对象占用了很少的内存,但是他们叠加在一起会增加Heap的压力,从而触发更多其他类型的GC。

点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

2. 渲染优化方式

从上一小节的分析可知,Android系统的渲染机制主要体现在CPU的绘制和GPU的格栅化、渲染两个阶段,其中,主要比较耗时的在CPU的绘制过程,即将UI对象转换为一系列的多边形和纹理,和从CPU上传Display List数据到GPU过程。如果CPU/GPU 负载过量或者上传数据次数过多,就会导致 16ms 内没有绘制、渲染完成出现渲染性能问题。因此,对于渲染性能方面的的优化,我们将从这两个方面进行剖析,即减轻CPU/GPU的负载量和减少占用额外的GPU资源。下面我们就介绍几个工具来帮助我们发现、解决一些渲染性能优化问题,主要有 Profile GPU Rendering、 SysTrace以及 TraceView等。

2.1 过度绘制优化

所谓过度绘制,指的是 在屏幕上某个像素在同一帧时间内被绘制多次,从而浪费了CPU和GPU的资源。举个例子来说,假如我们要粉刷房子的墙壁,一开始刷绿色,接着又开始刷黄色,这样黄色就将绿色盖住了,为此第一次的粉刷就白干了。产生过度绘制主要有两个原因:
  •   在XML布局中,控件有重叠且都有设置背景;
  •   View的onDraw在同一区域绘制多次;

2.1.1 Show GPU overdraw

  Show GPU overdraw是Android系统提供的一个查看界面 过度绘制的工具,我们可以在手机设置里的开发者选项中开启它,即 开发者选项->调试GPU过度绘制。Show GPU overdraw效果:

点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

开启调试GPU过度绘制后,我们的界面就会出现各种颜色,它们的具体含义如下:
  •   白色:没有过度绘制,即每个像素点在屏幕上只绘制了一次;
  •   蓝色:一次过度绘制,即每个像素点在屏幕上绘制了两次;
  •   绿色:二次过度绘制,即每个像素点在屏幕上绘制了三次;
  •   粉红色:三次过度绘制,即每个像素点在屏幕上绘制了四次;
  •   红色:四次以上过度绘制;
为此我们可以得出,一个合格的界面应该以白色和蓝色为主,绿色以上的区域不要超过整体的三分之一,总之颜色越浅越好。

2.1.2 Profile GPU Rendering

Profile GPU Rendering是Android4.1系统引入用于 展示GPU渲染每帧时各个阶段所消耗的时间,以便于我们可以直接观察到渲染该帧时各个阶段的耗时情况,即有没有超过16ms。我们可以在手机设置里开发者选项中打开它( 开发者选项->GPU显示配置文件/GPU呈现模式分析->在屏幕上显示为条形图),接着手机屏幕的底部就会出现彩色的柱状。效果如下图所示:

点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

图中绿色横轴代表帧时间,彩色的柱状(纵轴)表示某一帧的耗时。绿色的横线为警戒线,超过这条线则意味着该帧绘制渲染的时间超过了16ms,我们应尽量保证垂直的彩色柱状图保持在绿线下面,任何时候一帧超过绿线,我们的app将会丢掉一帧。每一个彩色柱状图代表一帧的渲染,越长的垂直柱状图表示这一帧需要渲染的时间越长。当APP在运行时,我们会看到手机底部的柱状图会从左到右动态地显示,随着需要渲染的帧数越来越多,他们回堆积在一起,这样就可以观察到这段时间帧率的变化。下图为柱状图中不同颜色代表的意义:

点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

颜色意义解释
  •    橘色:代表 处理的时间。代表CPU在等待GPU完成工作的时间,如果过高表示GPU很繁忙;
  •    红色:代表 执行的时间。Android 的2D渲染器向OpenGL发出命令绘制或重绘display lists 花费的时间,柱子的高度等于所有Display list绘制时间的总和。如果红色柱状图很高,可能由于重新提交视图而导致,还有复杂的自定义View也会导致红的柱状图变高;
  •    浅蓝色:向CPU传输Bitmap花费的时间,过高代表了加载了大量图形;
  •    深蓝色:代表 绘制的时间。也就是需要多长时间去创建和更新Dispaly List。过高代表在onDraw中花费过多时间,可能是自定义画图操作太多或执行了其它操作;
  •    淡绿色:代表了 onLayout onMeasure方法消耗的总时间,这段很高代表遍历整个view树结构花费了太多时间;
  •    绿色:代表为该帧内所有animator求值(属性动画中代表通过估值器计算属性的具体值)所花费的时间.如果这部分过高,代表自定义animator性能不佳或者更新view属性引发了某些意外操作;
  •    深绿色:代表app在用户输入事件回调中花费的时间,这部分过高可能意味着app处理用户输入事件时间过长,建议将操作分流到工作线程;
  •    墨绿色:代表在连续两帧间的时间间隔,可能是因为子线程执行时间过长抢占了UI线程被cpu执行的机会。
为此,我们可以得出在Profile GPU Rendering中,假如 红色/橘色占比较大,说明可能出现 重复布局的情况,可以从 否减少视图层级、减少无用的背景图、减轻自定义控件复杂度等方面去优化;假如 蓝色/浅蓝/各种绿色占比较大,应该从 耗时操作这方面去优化。当然,Profile GPU Rendering可以找到渲染有问题的界面,但是想要修复的话,只依赖Profile GPU Rendering是不够的,我们可以通过如下两款工具来定位问题,即 TraceView和 Hierarchy Viewer,前者能够详细分析问题原因;后者能够查看布局的层次和每个View所花费的时间。Systrace

     总之,我们应尽量从以下几个方面避免过度绘制。
  •   移除无用的背景图;
  •   减少视图层级,尽量使用扁平化布局,比如Relativeayout;
  •   减轻自定义控件复杂度,重叠区域可以使用canvas.clipRect方法指定绘制区域;`

2.2 卡顿优化

前面我们谈到,Android系统之所以会出现 卡顿现象是因为的某些操作耗费了帧间隔时间( 16ms),导致其他类似计算、渲染等操作的可用时间就会变少,在 16ms无法完成正常的绘制、渲染工作。当下一个VSYNC信号到来时,Android系统尝试在屏幕上绘制新的一帧,但是新的一帧还没准备好,就会导致无法进行正常渲染,画面就不会刷新,用户看到的还是上一帧的画面,从而发生了丢帧,这就是"卡顿现象"。下面我们介绍两款分析界面卡顿的利器,即 SysTrace和 TraceView,其中, SysTrace为卡顿原因指明大体方向, TraceView则是找到是什么让CPU繁忙、某些方法的调用次数等具体信息。

2.2.1 SysTrace

  SysTrace是Android 4.1中新增的性能数据采样和分析工具,它可以帮助我们收集Android关键子系统的运行信息,如SurfaceFlinger、WMS等Framework部分关键模块、服务、View体系系统等。 Systrace的功能包括跟踪系统的I/O操作、内核工作队列、CPU负载以及Android各个子系统的运行状态。**对于UI显示性能,比如动画播放不流畅、渲染卡顿等问题提供了分析数据。**我们可以在AS的Android Device Monitor(DDMS)开启 Systrace,需要注意的是,AS 3.0以后谷歌已将DDMS从AS面板上移除,但我们仍然可以打开 sdk目录/tools/monitor.bat使用它。 SysTrace使用方法如下:

点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小
  • 具体步骤:
  1.   双击 sdk目录/tools/monitor.bat打开DDMS,单击DDMS面板上的Systrace按钮;
  2.   进入抓取设置界面后设置跟踪的时长,以及trace.html文件输出路径等内容;
  3.   操作APP我们怀疑卡顿的地方,对该过程进行跟踪;
  4.   待跟踪结束后就会在指定路径生产trace.html文件,接下来用Chrome浏览器打开它进行分析。
  • 用Chrome分析trace.html

    点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

    总体来说,trace.html主要记录了Android系统中所有进程和线程运行信息,且同一个进程内按线程进行纵向拆分,每个线程均记录了自己的工作。从上图可知,在trace.html文件中我们应着重关注三个方面: Alerts、 Frames和 Kernel CPU,下面我们就详细分析如何使用它们来定位问题。
  1. Alerts部分
Alerts部分是Systrace自动分析trace中的事件,并能自动高亮某个性能问题作为一个Alerts(警告),建议调试人员下一步怎么做。可以通过点击顶部浅蓝色圆圈查看某一个警告,或者点击右侧的 Alerts选项列出所有警告信息,这些信息将会按类别列出,比如上图中列出了 Scheduling delay。选中一条Alert详情如下:

     点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

      Scheduling delay(调度延迟)的意思就是一个线程在处理一块运算的时候,在很长一段时间都没有被分配到CPU上面做运算,从而导致这个线程在很长一段时间都没有完成工作。从上图可以看出,选中的这帧只运行了 2.343ms,而有 101.459ms是在休眠,这就意味着这一帧的渲染过程非常慢。当然仅仅通过Alerts的提示仍然无法找到渲染慢的原因,接下来就需要借助Frames部分进一步定位。
  1. Frames部分
Frames部分给出的是应该的帧数,每一帧就是一个 F圆圈, F圆圈有三种颜色:绿色、黄色和红色,其中,绿色表示该帧渲染流畅(即没有超过16.66ms,满足每秒60帧稳定所需的帧),黄色和红色表示该帧的渲染时间超过了16.66ms,这就意味着黄色和红色代表的帧存在渲染性能问题(红色比黄色更严重)。我们点击红色的 F圆圈,在面板的底部会给出具体的Alert信息(同Alert部分),然后我们再按下电脑键盘上的 M键可以看到被高亮的部分就是这帧精确的时间耗时。应用区域的Frames展示如下:

     点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

     由于Android 5.0以上系统UI渲染的工作是在 UI Thread和 RenderThread完成的,我们使用键盘的 W键对这两个区域放大,然后选择两个线程中最长的一块区域(表示某个函数方法)观察那些View在被填充过程中耗时比较严重。我们点击 DrawFrame这个方法,在面板的底部可以得到以下信息:

点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

从上图可以看出, Wall Duration代表着这一块区域的开始到结束的耗时,为204.877ms; CPU Duration代表实际CPU在处理这一块区域的耗时,即分配CPU的时长0.899ms。很显然,这两个时间差距非常大,CPU只消耗了不到1ms的时间来运算这块区域。这就意味着可能其他线程/进程长期占用CPU,导致RenderThread无法进行正常运算出现渲染异常。接下来,我们就通过分析Kernel CPU部分进一步确定是哪个线程/进程长期占用CPU。
  1. Kernel CPU部分
在Kernel CPU区域,每一行代表一个CPU核心和它执行任务的时间片,放大后会看到每个色块代表一个执行的进程,色块的长度代表其执行时间。如下图所示:

点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

在之前选中的帧区域,我们很容易看到一个很长的绿色块,位于CPU2上,这个绿色块CPU执行的进程为HeapTaskDaemon,从进程名来看很容易猜出这个进程就是我们的GC守护进程。这就意味着,在这一帧内执行了长时间的垃圾回收操作,由于系统执行GC操作时会将正在执行的其他进行挂起,GC进程会独占CPU,长时间的GC操作或频繁GC就会导致其他进程长时间分配不到CPU时间片,这就是导致渲染变慢的根本原因。我们点击HeapTaskDaemon颜色块,可以看到具体的GC时间,如下图所示:

点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小
  操作快捷键
  • W、S:放大、缩小
  • A、D:向左、向右移动
需要注意的是,由于Systrace是以系统的角度返回一些信息,只能提供一个大概且深度有限,我们可以用它来进行粗略的检查,以便获得具体分析的方向。如果需要进一步得到是哪块代码引起的CPU繁忙、某些方法的调用次数等,就需要借助TraceView这个工具进行。

2.2.2 TraceView

  TraceView是Android SDK中自带的数据采集和分析工具,与 Systrace不同的是,它是从 代码层面来分析性能问题,且针对的是 每个方法来分析,比如当我们发现应用出现卡顿的时候,就可以通过 TraceVIew来分析出现卡顿时在方法的调用上有没有很耗时的操作。开启 TraceView方法与 Systrace一样,都是在 DDMS中启动。在使用 TraceView时,我们需要着重关注以下两个方面:
  • 调用次数不多,但是每一个执行都很耗时;
  • 方法耗时不大,但是调用次数太多;
TraceView使用方法:
  1. 双击 sdk目录/tools/monitor.bat打开DDMS,单击DDMS面板上左上角带小红点按钮,文字提示为 start Method Profiling,然后按钮左上方会出现一个灰色的正方形,文字提示为 stop Method Profiling,此时按钮变为停止采集功能;
  2. 进入 Profiling Options配置采样频率,默认为1000微秒;
  3. 操作APP我们怀疑卡顿的地方,对该过程进行跟踪;
  4. 操作结束后再次点击之前的按钮(文字提示 stop Method Profiling)结束采集。

    点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

    从上图可以看出, TraceView的面板分上下两个部分:
  •   时间线面板以每个线程为一行,右边是该线程在整个过程中方法执行的情况;
  •   数据分析面板是以表格的形式展示所有线程的方法的各项数据指标;
时间线面板:

     时间线面板以每个线程为一行,右边是该线程在整个过程中方法执行的情况,比如上图中显示的main线程就是Android应用的主线程,当然也会存在其他线程,可能会因操作不同而发生改变。每个线程的右边对应的是该线程中每个方法的执行信息,左边为第一个方法执行开始,最右边为最后一个方法执行结束,其中的每一个小立柱就代表一次方法的调用,你可以把鼠标放到立柱上,就会显示该方法调用的详细信息。

     点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

     如上图所示,墨绿色的立柱表示 FibonacciActivity.computeFubonacci()方法的执行情况,可以看出这个方法执行的时间很长,尤其是对于处于主线程中,这是非常不正常的现象。如果你想在分析面板中详细查看该方法,可以双击该立柱,数据分析面板自动跳转到该方法。

     数据分析面板:

     数据分析面板右侧为时间线面板中每个立柱表示的方法,当我点击墨绿色立柱后, FibonacciActivity.computeFubonacci()方法被高亮,展开该方法我们可以看到 Parents和 Children,其中, Parents表示谁调用了computeFubonacci()方法, Children表示computeFubonacci()方法调用了哪些方法。数据分析面板的左侧展示的是每个方法(一行)执行的耗时,我们着重看下computeFubonacci()方法 Incl Real Time值为1595.360ms,这显然是不正常的。另外,再看下 Calls+Recur Calls/Total的值显示computeFubonacci()方法数为1,但是被递归调用了1491次。因此,我们就可以得出之前的APP操作之所以会出现卡顿,是因为 在主线程的FibonacciActivity中递归调用了computeFubonacci()方法,导致CPU被长期占用,从而导致渲染线程无法获得CPU资源出现无法正常渲染的性能问题。

     点击在新窗口中浏览原图
CTRL+鼠标滚轮放大或缩小

     每一列数据代表的含义如下表所示:    
名称 意义
Name 方法的详细信息,包括包名和参数信息
Incl Cpu Time Cpu执行该方法该方法及其子方法所花费的时间
Incl Cpu Time % Cpu执行该方法该方法及其子方法所花费占Cpu总执行时间的百分比
Excl Cpu Time Cpu执行该方法所话费的时间
Excl Cpu Time % Cpu执行该方法所话费的时间占Cpu总时间的百分比
Incl Real Time 该方法及其子方法执行所话费的实际时间
Incl Real Time % 上述时间占总的运行时间的百分比
Excl Real Time % 该方法自身的实际允许时间
Excl Real Time 上述时间占总的允许时间的百分比
Calls+Recur  Calls/Total 调用次数+递归次数
Calls/Total 调用次数和总次数的占比
Cpu Time/Call Cpu执行时间和调用次数的百分比,代表该函数消耗cpu的平均时间
Real Time/Call 实际时间于调用次数的百分比,该表该函数平均执行时间
  • FibonacciActivity源码如下:
   /** UI卡顿现象
* @Auther: Jiangdg
* @Date: 2019/11/18 15:06
* @Description: 使用斐波那契数列人为制造卡顿现象
*/

public  class  FibonacciActivity  extends AppCompatActivity  {
     private  static  final String LOG_TAG  =  "FibonacciActivity" ;

     @Override
     protected  void  onCreate (Bundle savedInstanceState )  {
         super . onCreate (savedInstanceState ) ;
         setContentView (R .layout .activity_fibonacci ) ;

        Button mbtn  =  (Button )  findViewById (R .id .caching_do_fib_stuff ) ;
        mbtn . setText ( "计算斐波那契数列" ) ;

        mbtn . setOnClickListener ( new  View .OnClickListener ( )  {
             @Override
             public  void  onClick (View v )  {
                Log . i (LOG_TAG , String . valueOf ( computeFibonacci ( 40 ) ) ) ;
             }
         } ) ;
        WebView webView  =  (WebView )  findViewById (R .id .webview ) ;
        webView . getSettings ( ) . setUseWideViewPort ( true ) ;
        webView . getSettings ( ) . setLoadWithOverviewMode ( true ) ;
        webView . loadUrl ( "file:///android_asset/shiver_me_timbers.gif" ) ;
     }

     public  int  computeFibonacci ( int positionInFibSequence )  {
         //0 1 1 2 3 5 8
         if  (positionInFibSequence  <=  2 )  {
             return  1 ;
         }  else  {
             return  computeFibonacci (positionInFibSequence  -  1 )
                     +  computeFibonacci (positionInFibSequence  -  2 ) ;
         }
     }
}

----------------------------
原文链接:https://blog.csdn.net/AndrExpert/article/details/103156318
作者:蒋东国

程序猿的技术大观园:www.javathinker.net



[这个贴子最后由 flybird 在 2020-03-10 10:24:44 重新编辑]
  Java面向对象编程-->数据类型
  JavaWeb开发-->JavaWeb应用入门(Ⅰ)
  JSP与Hibernate开发-->Java应用分层架构及软件模型
  Java网络编程-->通过JavaMail API收发邮件
  精通Spring-->CSS过渡和动画
  Vue3开发-->Vue CLI脚手架工具
  Android的Service和广播的讲解
  浅析Android的通知系统
  Android 自定义Menu
  android异步更新UI
  Android Gallery实现循环显示图像
  Android 手势操作GestureDetector
  Android自定义组件
  Android多屏幕适配
  Android 4.0 : 复制APK,复制动态库的Android.mk 文件
  浅析Android的通知系统
  Android Lint分类及常见错误
  android使用工具性能优化
  自定义ViewGroup和FrameLayout实现轮播图(包括底部小圆点)
  Android端使用OpenGL ES加载OBJ文件数据
  uniapp安卓ios百度人脸识别、活体检测、人脸采集APP原生插件
  更多...
 IPIP: 已设置保密
楼主      
1页 0条记录 当前第1
发表一个新主题 开启一个新投票 回复文章


中文版权所有: JavaThinker技术网站 Copyright 2016-2026 沪ICP备16029593号-2
荟萃Java程序员智慧的结晶,分享交流Java前沿技术。  联系我们
如有技术文章涉及侵权,请与本站管理员联系。