布景
从开发直播间以来,客户端就一直遇到卡顿和烘托的问题,针对卡顿也做过功能优化专项来处理这些问题。可是有些现象未深化的考虑,比如:
- 为什么Android烘托出问题时视频流是黑屏?
- 为什么UI卡顿甚至卡死的时分视频流不受影响?
- 用户反应卡的时分是不是真的产生了卡顿?
- 如何以技术目标来监控和衡量用户视角的卡顿?
方针
用技术目标体现用户视角是否卡顿、卡顿的影响、卡顿率。
- 监控整体卡顿需求监控全链路,从场景上看应该包含UI、事情处理循环、流媒体(接收图元数据、解码、烘托、显现),各目标互补;
- 同时也要技术目标与事务场景结合剖析,静态页面FPS为0但并不代表卡顿,需求结合事情处理循环剖析有无事情处理
- FPS高未必流通,与每帧画面的烘托时长以及两帧画面间隔有关系
业界方案分两类:
- 东西:以开发调优为切入点的东西类产品比如Perfdog、Android Studioprofile、XcodeInstruments,无法监控线上出产环境的卡顿;
- 自研:各厂自研的APM、流媒体卡顿监控,与事务结合相对紧密不行通用;
烘托根底知识
硬件根底
移动端设备烘托中,有三个中心硬件会参加其中:CPU、GPU、屏幕。下面咱们来简略介绍下三者的概念: CPU:即中央处理器,是体系的运算和操控单元,烘托进程中担任界面的尺度测量、布局核算、软解码等进程。
GPU:即图形处理器,有非常强的并行核算和浮点核算才能,一般用来对纹路数据进行烘托和组成。
屏幕:由一个个像素点组成,以固定的频率(比如每帧16.6ms,即1秒60帧)从缓冲区中取出数据来填充像素点。
总结一句话便是:CPU核算好图元信息提交至GPU,GPU烘托完成后将烘托结果存入帧缓冲区,最终屏幕从缓冲区中读取数据并显现,像下图这样:
屏幕成像原理
下面咱们来看一下屏幕成像的一些技术概念:
屏幕改写频率:是指设备改写屏幕的频率,常见的有60Hz、120Hz、144Hz等,最新的也有可变改写率设备,会随内容的展现而改变。
VSYNC:即垂直同步信号,是由硬件时钟产生的一个脉冲信号,为了协调CPU核算和屏幕制作时刻,使其同时开端制作,能避免画面撕裂的问题。
双缓冲:为了处理单缓冲的功率问题,GPU 通常会引进两个缓冲区,前缓冲和后缓冲。UI先在后缓冲中制作,然后再和前缓冲交流,烘托到显现设备中。
三缓冲:在双缓冲根底上引进三块缓冲区,使CPU、GPU的利用率进一步进步,降低产生画面卡顿的概率。
现在咱们知道,为了使屏幕UI显现流通,体系做了许多优化,从双缓冲到三缓冲,从没有同步到引进VSYNC信号,下面咱们看一下优化的作用。
不运用VSYNC信号
图中有三个时刻轴:Display、CPU、CPU。Display依照固定时刻来取数据的,暂定每个间隔是16ms,也便是每秒60帧。
每一个数字相同的方块表明同一帧,每一帧都依照CPU→GPU→Display的流程来处理。
从图中能够看出,在没有VSYNC信号的时分,CPU制作的开端时刻不确定,导致在第3个16ms时,帧2还没有烘托完成,导致卡顿。
运用VSYNC信号
在参加VSYNC信号同步后,每收到VSYNC信号,CPU就开端处理各帧数据,现已处理了改写不同步的问题。
从图中能够看出,CPU和GPU的烘托时刻都操控在16ms内,烘托很完美,但假如CPU和GPU的烘托时刻超越16ms,就会有新的问题,看下面的图:
1)在第一个16ms时刻段,CPU、GPU依据VSYNC信号开端制作,Display显现A帧。
2)在第二个16ms时刻段,Display本应显现B帧,但却由于GPU还在处理B帧,导致A帧被重复显现,这时CPU无所事事,由于A Buffer被Display运用,B Buffer被GPU运用。
所以第二个16ms和第四个16ms都会产生卡顿,这个问题本质上是缓冲区不行导致的,所以体系引进了三缓冲机制。
三缓冲+VSYNC信号
从图上能够看出来,引进三缓冲机制后,第二个16ms还会卡顿,但后续不会再卡顿,降低了卡顿的产生次数,当时Android和iOS都引进了相似的机制。
Android端烘托原理
UI烘托
整体结构
UI烘托指应用界面在屏幕显现的进程,咱们知道整个进程在硬件上需求CPU、GPU、屏幕三者的协同工作,在应用中需求许多部分共同完成烘托工作:
- App进程:和各个服务交互,办理制作工作
- WindowsManager服务:供给窗口,并绑定画布和窗口
- SurfaceFlinger服务:供给画布,并把各应用画布组成一张图
- HAL:硬件笼统层,简略了解便是屏幕
详细如下图所示:
1)第一条线:App->Wms→SF,App进程在创立Activity后,经过与WindowManagerService交互,向SurfaceFlinger申请创立Surface。创立成功后,App端对应的得到为SharedBufferClient的目标,用来操控同享内存的写入
2)第二条线:App->SC,SF->SC,App担任UI测量、布局、制作,SurfaceFinger担任UI烘托组成,两个进程经过同享内存(SharedClient)来交流数据
3)第三条线:SF→HAL,SurfaceFinger组成内容后,供给给HAL,最终会供给给硬件帧缓冲区,改写到屏幕上
4)躲藏的线:VSYNC信号,HWComposer基于硬件来产生VSYNC信号的,App进程经过监听VSYNC信号来开端制作,SurfaceFlinger经过监听VSYNC信号操控层的组成。
UI视图制作流程
先看一张图,图中是Android应用的View结构,这些UI元素是以树形结构来组织的,即它们存在着父子关系,其中子UI元素坐落父UI元素里边,惯例的烘托分3个进程:
1)测量:从根视图开端,依次核算每一个视图的宽高,最终视图希望的窗口尺度
2)布局:从根视图开端,依次核算每个子视图在父视图中的方位
3)制作:从根视图开端,依照制作布景、制作自己、制作子视图的流程来进行内容制作
现在咱们知道UI的制作流程,那UI如何监听VSYNC信号来触发制作的呢?担任这块的是Choreographer类
1)ViewRootImpl的requestLayout()办法会调用Choreographer的postCallback(),会在主线程添加同步屏障,并经过Choreographer注册VSYNC信号监听
2)Choreographer经过DisplayEventReceiver向framework层注册下一个VSYNC信号
3)ViewRootImpl收到VSYNC信号后,移除同步屏障并履行doTraversal()办法,触发UI制作
到这里咱们就知道UI烘托的整个流程,从监听VSYNC信号开端,收到后开端履行App进程的制作,完成后触发SurfaceFlinger的制作,最终鄙人一个VSYNC信号到来后,烘托到屏幕上。
所以监控UI烘托的中心就在于监控VSYNC信号的接收。
流媒体烘托
特殊的画布
流媒体的流是烘托在SurfaceView上的,它本质上是一个View。但不同于一般视图,SurfaceView最大的特点是它有自己的Surface,如下图所示:
在一个页面(图中的Activity)中,一切的一般视图共用一个画布(图中的aSurface),而SurfaceView有自己的画布(图中的mSurface),一个独立的surface对应的是一个独立的烘托线程。
所以每一个流的烘托都能够在独自线程去做,而其它视图的烘托必须在主线程,这就解释了为什么有时分UI卡顿不会影响流媒体烘托。
另外一方面,由于SurfaceView是一个一般View,所以它也有一般视图的特性:
- 在页面窗口创立时,SurfaceView会收到回调,这时它会经过SurfaceFlinger创立Surface
- 在UI烘托时,SurfaceView会收到制作的回调,这时它会制作一个纯黑色的布景,这也是Android界面是“黑屏”的根源
流媒体烘托流程
流媒体在经历过网络恳求,解码,同步,烘托等流程后,将帧数据经过eglSwapBuffers提交给体系层进行烘托。换句话说,eglSwapBuffers是咱们事务层能接触到的最终一行代码,接下来的surfaceFlinger和HWC等体系层和硬件层超越了咱们的可控范围。
流媒体监控
综上,流媒体的监控,便是要监控每一帧从组帧开端到eglSwapBuffers办法回来为true烘托的悉数进程,记载每个帧是被丢弃了仍是完成了烘托,烘托延时是多少,帧间隔,帧率等等
落地
UI卡顿监控
1)Android体系默许的帧率是60帧/秒。APP 事务层经过监控Choreographer的voiddoFrame(longframeTimeNanos)办法,来监控APP事务层是否卡顿。
2)体系每烘托一帧,就会回调一下doframe这个办法,并回来对应的时刻。经过检测两帧回调之间的时刻间隔能够知道是否产生了制作卡顿。
3)经过剖析MessageQueue的消息履行,能够监控体系一切消息的履行耗时状况。如屏幕点击,播送等
流媒体卡顿监控
1)核算核算一秒内eglSwapBuffers履行并回来true的次数。假如小于满帧数,则阐明呈现了丢帧。假如小于阈值帧,则阐明流媒体产生了卡顿。则进一步进行卡顿归因。
2)从拉流获取网络数据组帧为监控起点(记做起点),到将帧数据提交给OpenGL烘托,履行eglSwapBuffers办法回来true(记做终点)。
3)对这一秒内的帧做ID符号,追寻这一秒内一切帧的链路行为。
4)依据上面数据,定位出卡顿的详细环节,从而针对对应环节做优化
目标
目标 | 释义 | 核算方式 |
---|---|---|
Big Janky Count | 严重卡顿次数 | 单帧制作耗时大于 MOVIE_FRAME_TIME*3 |
FPS | 实践烘托帧率 | 数据获取时刻周期内,实践烘托帧数/ 数据获取间隔时刻 |
Janky Count | 一般卡顿次数 | 单帧制作耗时大于 MOVIE_FRAME_TIME*2 |
Latent Jank | 潜在卡顿 | 依据视觉惯性,单帧耗时大于前三帧均匀耗时2倍 |
RFPS | 相对帧率 | 数据获取时刻周期内,(理论满帧-实践掉帧数)/ 数据获取间隔时刻 |
Stutter | 卡顿率 | 卡顿比。当产生 jank 的帧的累计时长与区间时长的比值。 |
优化思路
软件方向
1)特定机型,体系,高体系负载时进行事务降级
2)体系新特性适配。如Doze适配,Android 12适配
3)线程复用,优化线程锁等候和ANR
硬件方向
1)拉高硬件履行才能:如指定线程和CPU大核绑定,以削减CPU切换开销,进步CPU缓存命中逻辑
2)功耗优化
3)硬解(GPU)替代软解(CPU)
4)Android中的“Metal”–Vulkan,要点优化CPU功能