使用Systrace前提:
- Python 2.7.x 环境;
- 安卓SDK : systrace 命令在 Android SDK 工具软件包中提供,并且可以在 android-sdk/platform-tools/systrace/ 中找到(旧版带systrace.py)
- chrome://tracing/ :浏览器打开systrace.html
Systrace 适用于 Android 4.3(API 级别 18)及更高版本的所有平台版本,但建议将 Perfetto 用于运行 Android 10 及更高版本的设备
W:放大
S:缩小
A:左移
D:右移
M:高亮
1 :将当前正在使用中的选择模型更改为“选择”模式。
2 :将当前正在使用中的选择模型更改为“平移”模式。
3 :将当前正在使用中的选择模型更改为“缩放”模式。
4 :将当前正在使用中的选择模型更改为“计时”模式。
G :在当前所选任务的开头显示网格。
左键:在当前选定的时间轴上选择上一个事件。
右键:在当前选定的时间轴上选择下一个事件。
CPU按照核心数和架构分类:
1.非大小核架构:八核/四核/两核同构,频率相同,功耗相同
2.大小核架构:小核主频低功耗低,大核主频高功耗高
八核心:CPU0-3是小核心,CPU4-7是大核心
ohter:4小核心+2大核心;6小核心+2大核心
3. 大中小核架构:大核心1~2个,主频功耗高,用来处理繁重高负载任务
绑核
将任务绑定到一个或一组核心上运行来满足实际需求
负载高-------------------大核心组
任务避免频繁切换----绑定某一个核心
任务要求低-------------绑定小核心组
横坐标是以时间为单位,纵坐标是以进程-线程的方式来划分,同一进程的线程为一组放在一起,可收缩/展开,如下图:
在每个app进程,都有一个Frames行,正常情况以绿色的圆点表示。当圆点颜色为黄色或者红色时,意味着这一帧超过16.6ms(即发现丢帧),这时需要通过放大这一帧进一步分析问题。
对于Android 5.0(API level 21)或者更高的设备,该问题主要聚焦在UI Thread和Render Thread这两个线程当中。对于更早的版本,则所有工作在UI Thread。
描绘 SurfaceFlinger 进程(包括 VSync 事件和界面线程交换工作)的其他直方图
Android系统在启动的时候有两个重要的进程:Zygote进程 和 由zygote进程fork出来的system_server进程
system_server 进程主要是用于创建系统服务,AMS、WMS、PMS 都是由它创建的。 具体来说,SystemServer 进程被创建后,主要做了以下工作:
SystemServer进程的核心是SystemServer类,它是Android系统启动后的第一个Java进程。SystemServer类负责启动系统的各种服务,它通过Binder机制提供各种服务接口
SystemServer 由于提供大量的基础服务,所以进程间的通信非常繁忙,且大部分通信都是通过 Binder 。
public final class SystemServer {
private static final String TAG = "SystemServer";
private static final boolean DEBUG_LISTENER = false;
private static final boolean DEBUG_PRIORITY = false;
private static final String ANSI_RED_BACKGROUND = "\u001B[31;40m";
private static final String ANSI_RESET = "\u001B[0m";
...
public static void main(String[] args) {
...
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "InitBeforeStartServices");
SystemServer systemServer = null;
try {
//创建systemServer实例
systemServer = new SystemServer();
//启动系统服务
systemServer.run();
} catch (Throwable ex) {
Log.e("System", "******************************************");
Log.e("System", "************ Failure starting system services", ex);
System.exit(10);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
}
//启动各种系统服务的方法
private void run() {
...
startCoreServices();
startOtherServices();
startBootstrapServices();
...
}
//启动核心服务的方法
private void startCoreServices() {
...
//启动属性服务
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "InitializeSystemProperties");
SystemProperties.init();
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
//启动电源服务
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartPowerManagerService");
power = powerManagerService;
powerManagerService.setPolicy((WindowManagerPolicy) policy);
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
//启动USB服务
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartUsbService");
UsbService usb = new UsbService(context);
ServiceManager.addService(Context.USB_SERVICE, usb);
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
//启动Vibrator服务
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartVibratorService");
vibrator = new VibratorService(context);
ServiceManager.addService(Context.VIBRATOR_SERVICE, vibrator);
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
//启动下载服务
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartDownloadService");
ServiceManager.addService(Context.DOWNLOAD_SERVICE, new DownloadManagerService(context));
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
...
}
//启动其他服务的方法
private void startOtherServices() {
...
//启动媒体服务
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartMediaServer");
media = new MediaServer(context);
ServiceManager.addService(Context.MEDIA_ROUTER_SERVICE, media.getMediaRouter());
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
//启动网络服务
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartNetworkManagementService");
try {
final NetworkManagementService nmService = NetworkManagementService.create(context);
ServiceManager.addService(Context.NETWORKMANAGEMENT_SERVICE, nmService);
} catch (Throwable e) {
reportWtf("starting NetworkManagementService", e);
}
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
//启动位置服务
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartLocationManagerService");
locationManagerService = new LocationManagerService(context);
ServiceManager.addService(Context.LOCATION_SERVICE, locationManagerService);
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
...
}
//启动引导服务的方法
private void startBootstrapServices() {
//重命名文件系统
if (!mRuntimeRestart) {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "RenameFilesystem");
ZygoteInit.renameAndRemoveOldUserSystemDirs();
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
//格式化data分区
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartBootstrapServices");
Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
intent.setPackage("android");
context.sendBroadcastAsUser(intent, UserHandle.ALL);
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
...
}
应用启动流程:点击App时,首先Launcher会启动一个StartingWindow,等App中启动页面的第一帧绘制好了,就会马上从Launcher切换回App的窗口动画。
Android 9.0之前App启动动画在SystemServer里
Android 9.0之后App启动动画由Laucher和App自身第一帧组成
AMS主要负责Android系统中四大组件的启动、切换、调度以及应用进程管理和调度工作
AMS 相关的 Trace 一般会用 TRACE_TAG_ACTIVITY_MANAGER 这个 TAG,在 Systrace 中的名字是 ActivityManager。
WMS管理所有的窗口,包括创建、删除、修改,以及将某个窗口设置为焦点窗口。
一般会用 TRACE_TAG_WINDOW_MANAGER 这个 TAG,在 Systrace 中 WindowManagerService 在 SystemServer 中多在 对应的 Binder 中出现。
ServiceThread 继承自 HandlerThread。
UIThread、IoThread、DisplayThread、AnimationThread、FgThread、SurfaceAnimationThread都是继承自 ServiceThread,分别实现不同的功能,根据线程功能不同,其线程优先级也不同。
每个 Thread 都有自己的 Looper 、Thread 和 MessageQueue,互相不会影响。Android 系统根据功能,会使用不同的 Thread 来完成。
BackgroundThread,许多对性能没有要求的任务,一般都会放到 BackgroundThread 中去执行。
SurfaceFlinger始终显示内容,始终保留一个缓冲区。
作用:接收所有缓冲区,进行合成,发送到显示设备(屏幕)
触发:收到VSync信号后,开始以帧的形式绘图;
流程:收到VSync信号后,SurfaceFlinger遍历层列表,寻找新缓冲区,找到新的缓冲区就获取,未找到新的就使用以前的缓冲区,某个层上没有提交缓冲区就会忽略该层,收集完后询问HWC如何合成
App与BufferQueue交互流程:收到vsync信号后,应用主线程(UI Thread)被唤醒,主线程处理完数据后,会唤醒应用渲染线程RenderThread同步数据,RenderThread会从BufferQueue取出一个Buffer,然后往Buffer写入具体的 drawcall,完成后再将有数据的Buffer还给BufferQueue。
思考后认为,App是不断往BufferQueue里面丢数据的,而vsync信号的发送与前面无关,就是系统提醒Surface Flinger可以画图了,那Surface Flinger就马上去BufferQueue取数据,然后画图。
HWC HAL是用于合成从Surfaceflinger接收到的图层,而分担GLES和GPU执行合成的压力,提升整体性能和效率
使用HWC来合成该窗口而不是用surfaceflinger通过GPU来合成原因?
1)大部分GPU没有为合成优化
2)当使用Surfaceflinger通过GPU来合成layers 时,应用是无法用GPU来做渲染的工作的
Input分类
显示器并不是一次性将画面显示到屏幕上,而是从左到右边,从上到下逐行扫描显示,不过这一过程快到人眼无法察觉到变化。以 60 Hz 刷新率的屏幕为例,即 1000 / 60 ≈ 16ms。
CPU / GPU 生成图像的 Buffer 数据,屏幕从 Buffer 中读取数据刷新后显示。
理想情况下帧率和刷新频率保持一致,即每绘制完成一帧,显示器显示一帧。引入 Vsync 之前的 Android 版本,渲染一帧相关的 Message ,中间是没有间隔的,上一帧绘制完,下一帧的 Message 紧接着就开始被处理。这样的问题就是,帧率不稳定,可能高也可能低。
当 GPU 利用一块内存区域写入一帧数据时,从顶部开始新一帧覆盖前一帧,并立刻输出一行内容。当屏幕刷新时,此时它并不知道图像缓冲区的状态,因此从缓冲区抓取的帧并不是完整的一帧画面(绘制和屏幕读取使用同一个缓冲区)
此时屏幕显示的图像会出现上半部分和下半部分明显偏差的现象,这种情况被称之为 “tearing”(屏幕撕裂)。
发生 “tearing” 现象的根源是由于帧速率与刷新频率不一致导致。
从 Android 4.1 开始, VSYNC 则更进一步,现在 VSYNC 脉冲信号开始用于下一帧的所有处理。
Vsync是信号,用来控制App渲染图像、SurfaceFlinger合成图像的节奏。它可以同步 GPU 的帧速率和显示器的刷新频率,Vsync可以由硬件或软件产生,主要是由硬件HWC生成。
HWC 可生成 VSYNC 事件并通过回调将事件发送到 SurfaceFlinge , DispSync 将 Vsync 生成 Choreographer 使用的 VSYNC_APP 和 SurfaceFlinger 使用的 VSYNC_SF 信号.
Android 主线程运行的本质,其实就是 Message 的处理过程,我们的各种操作,包括每一帧的渲染操作 ,都是通过 Message 的形式发给主线程的 MessageQueue ,MessageQueue 处理完消息继续等下一个消息。
Choreographer 本质是一个Java类,以接收系统的 VSYNC 信号,统一管理应用的输入、动画和绘制等任务的执行时机。
Choreographer 的主要功能是,当收到 VSYNC 信号时,去调用通过 postCallback 设置的回调方法。目前一共定义了五种类型的回调,它们分别是:
DisplayEventReceiver 是一个 abstract class,在其构造方法内会通过 JNI 创建一个 IDisplayEventConnection 的 VSYNC 的监听者,FrameDisplayEventReceiver 是 DisplayEventReceiver 的唯一实现类,并重写 onVsync 方法用于通知 Choreographer。
DisplayEventReceiver 中包含3个非常重要的方法:
doFrame 主要做:
每一帧的 doFrame 的时候,都会根据情况触发下一个 Vsync 的申请,这样我们就可以获得连续的 Vsync 信号。
由于图像绘制和读取使用的是同一个缓冲区,所以屏幕刷新时可能读取到的是不完整的一帧画面,使用Double Buffer解决,让绘制和显示器拥有各自Buffer :Back Buffer 和 Front Buffer。
GPU 将完成的一帧图像数据写入到 Back Buffer,而显示器使用 Frame Buffer,当屏幕刷新时,Frame Buffer 并不会发生变化,Back Buffer 根据屏幕的刷新将图形数据 copy 到 Frame Buffer,copy实际是交换各自的内存地址。
一般来说,在同一个 View Hierarchy 内的不同 View 共用一个 Window,也就是共用同一个 Surface。
每个 Surface 都会有一个 BufferQueue 缓存队列,但是这个队列会由 SurfaceFlinger 管理,通过匿名共享内存机制与 App 应用层交互。
再加入一个Back Buffer ,BufferQueue中两个Back Buffer,一个FrontBuffer进行轮转
当FrontBuffer在使用时,App有两个空闲的Buffer可以用作生产;
如果GPU渲染时间过长,CPU也可以拿到另一个Back Buffer进行生产,SurfaceFlinger消费Front Buffer
[外链图片转存中…(img-3yiMOMem-1697620351599)]
App 主线程超时不一定会导致掉帧,由于 Triple Buffer 的存在,部分 App 端的掉帧(主要是由于 GPU 导致),到 SurfaceFlinger 这里未必是掉帧
[外链图片转存中…(img-Up13LZbe-1697620351599)]
Double Buffer轮转时,App主线程有时必须等待SurfaceFlinger释放Buffer后,才能获取Buffer进行生产,大部分手机并没有设置offset,也就是App与SurfaceFlinger同时收到VSync信号,App等待SurfaceFlinger释放Buffer必然会导致App主线程的执行时间延后
Triple Buffer轮转时,渲染线程一般在dequeueBuffer时,都可以拿到Buffer
GPU:Double Buffer时,GPU渲染超时容易导致SurfaceFlinger无法及时合成Buffer导致掉帧
surfaceFlinger:如果SurfaceFlinger本身耗时,dequeueBuffer没有及时响应,导致掉帧