自 2025 年 3 月 27 日起,我们建议您使用 android-latest-release
而非 aosp-main
构建 AOSP 并为其做出贡献。如需了解详情,请参阅 AOSP 的变更。
系统应用中的 ViewCapture
使用集合让一切井井有条
根据您的偏好保存内容并对其进行分类。
ViewCapture 是一种软件工具,用于捕获附加到其挂钩的窗口的视图属性(例如位置、大小、缩放比例和可见性)。ViewCapture 会捕获窗口内各种视图及其属性的相关信息,以便您了解特定时刻的用户体验状态,并跟踪随时间的变化。
屏幕录制可以直观呈现视图在特定时间的状态,并显示其变化情况,但它们需要大量的 CPU 资源,并且可能会影响性能。ViewCapture 工具对资源的影响较小,可以更频繁地启用。此外,ViewCapture 会在视图级别逐帧显示可视化图表,与屏幕录制相比,可更轻松地检查特定时刻的视图状态。
本页面介绍了如何在系统应用中初始化 ViewCapture。
使用
ViewCapture.java
可实现 onDrawListener
的实例,并在绘制过程中收集 ViewCapture 跟踪记录。每次帧重绘都会触发对视图树层次结构的遍历,且从窗口的根视图开始。ViewCapture 使用公共 View.java
getter 方法来提取值并将其复制到后台线程,从而提高性能。ViewCapture 实现通过使用 captureViewTree
检查视图是否脏或无效来优化此过程,从而避免遍历整个视图层次结构。captureViewTree
仅适用于系统应用,并且是 UnsupportedAppUsage API 的一部分。此 API 的使用仅限于基于目标 SDK 版本的应用。
限制
以下部分介绍了运行 ViewCapture 时的性能和内存限制。
ViewCapture 性能的主线程平均开销为 195 μs。不过,在最糟糕的情况下,可能需要大约 5 毫秒。请参阅 Perfetto 跟踪记录中的 vc#onDraw
切片。
开销主要源于以下操作:
- 遍历层次结构需要 50 μs,即使进行修剪也是如此。
- 从空闲列表分配器中提取对象以存储视图属性副本需要 20 μs。
- 通过 getter 函数提取每个属性值会导致每个视图进行多次额外的函数调用,耗时 110 μs。
因此,在始终开启跟踪 (AOT) 的情况下启用 ViewCapture 会对系统性能产生负面影响,并导致卡顿。由于存在这些性能和内存限制,因此该方法尚不适用于 AOT。我们建议仅在实验室和本地调试时使用 ViewCapture。
内存
Perfetto 的 ViewCapture 跟踪记录方法使用单个环形缓冲区,该缓冲区具有预定义的内存占用量,可防止过度占用内存。此方法通过避免为每个窗口使用单独的环形缓冲区来避免过度消耗内存,但无法解决针对每个帧在 Perfetto 中存储每个状态的整个视图层次结构的问题。记录单个窗口(例如 NexusLauncher)可能会在 10 MB 缓冲区中生成超过 30 秒的 ViewCapture 数据。不过,从系统界面捕获超过 30 个窗口需要更大的缓冲区或更短的录制时间窗口。
操作说明
请按照以下说明在系统应用中启用 ViewCapture:
将依赖项添加到 Android.bp
文件中,如启动器代码中所示。
android_library {
name: "YourLib",
static_libs: [
...
"//frameworks/libs/systemui:view_capture",
...
],
platform_apis: true,
privileged: true,
}
在创建窗口时创建 ViewCapture 实例,例如:
示例 1:
private SafeCloseable mViewCapture;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
mViewCapture = ViewCaptureFactory.getInstance(this).startCapture(getWindow());
}
示例 2:
private SafeCloseable mViewCapture;
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (enableViewCaptureTracing()) {
mViewCaptureCloseable = ViewCaptureFactory.getInstance(getContext())
.startCapture(getRootView(), ".NotificationShadeWindowView");
}
...
}
在销毁窗口时关闭 ViewCapture 实例,如以下示例所示:
示例 1:
@Override
public void onDestroy() {
...
if (mViewCapture != null) mViewCapture.close();
}
示例 2:
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (mViewCaptureCloseable != null) {
mViewCaptureCloseable.close();
}
...
}
本页面上的内容和代码示例受内容许可部分所述许可的限制。Java 和 OpenJDK 是 Oracle 和/或其关联公司的注册商标。
最后更新时间 (UTC):2025-03-26。
[null,null,["最后更新时间 (UTC):2025-03-26。"],[],[],null,["# ViewCapture in system apps\n\nViewCapture is a software tool that captures the properties of the views (such\nas location, size, scale, and visibility) attached to the windows it's hooked\nonto. ViewCapture captures information about the various views within a window\nand their properties, letting you know the state of the user experience at\nspecific moments in time and track changes over time.\n\nScreen recordings can visualize the state of a view at a specific time\nand show how it changes, but they demand significant CPU resources and can\nimpact performance. The ViewCapture tool has less resource impact\nand can be enabled more frequently. Additionally, ViewCapture displays\nvisualizations frame by frame at the view level, making it more straightforward\nto inspect the view state at specific moments compared to screen recordings.\n\nThis page describes how to onboard ViewCapture in system apps.\n\nUse\n---\n\n[`ViewCapture.java`](https://cs.android.com/android/platform/superproject/+/android-latest-release:frameworks/libs/systemui/viewcapturelib/src/com/android/app/viewcapture/ViewCapture.java) implements an instance of `onDrawListener` and collects a\nViewCapture trace during the drawing process. Each frame redraw triggers a\ntraversal of the view tree hierarchy starting at the root view of the window.\nViewCapture uses public [`View.java`](https://cs.android.com/android/platform/superproject/+/android-latest-release:frameworks/base/core/java/android/view/View.java)\ngetter methods to fetch and copy values to a\nbackground thread for improved performance. The ViewCapture implementation\noptimizes this process by checking if a view is dirty or invalidated using \\[`captureViewTree`\\](https://cs.android.com/android/platform/superproject/+/android-latest-release:frameworks/libs/systemui/viewcapturelib/src/com/android/app/viewcapture/ViewCapture.java?q=%22captureViewTree(View%22),\nthus avoiding traversal of the entire view hierarchy. `captureViewTree` is\navailable only for system apps and is part of the UnsupportedAppUsage API.\nUse of this API is limited to apps based on their target SDK version.\n\nLimitations\n-----------\n\nThe following sections describe the performance and memory limitations in\nrunning ViewCapture.\n\n### Performance\n\nThe average main thread overhead for ViewCapture performance is\n195 μs. However, in the worst-case scenarios, it can take\napproximately 5 ms. Refer to the `vc#onDraw` slice in the\n[Perfetto](https://ui.perfetto.dev/#!/?s=fbc2e14e6ed6ee28901233b2a49df1dccd78b013bb50c14e4a9d6b1c21ebb13e)\ntrace.\n\nThe overhead costs are primarily due to the following actions:\n\n1. Traversing the hierarchy costs 50 μs, even when pruned.\n2. Pulling objects from a freelist allocator to store copies of view properties costs 20 μs.\n3. Fetching each property value through a getter function results in many additional function calls per view, costing 110 μs.\n\nHence, enabling ViewCapture in always on-tracing (AOT) negatively impacts system\nperformance and leads to jank. Due to these performance and memory limitations,\nthis approach isn't ready for AOT. We recommend ViewCapture only for lab and\nlocal debugging.\n\n### Memory\n\nPerfetto's method for ViewCapture traces uses a single ring buffer,\nwhich has a predefined memory footprint to prevent excessive memory use. This\napproach prevents excessive memory consumption by avoiding using separate ring\nbuffers for each window, but doesn't resolve the issue of storing the entire\nview hierarchy for every state in Perfetto for each frame. Recording a single\nwindow, such as NexusLauncher, can produce over 30 seconds of ViewCapture data\nin a 10 MB buffer. However, capturing over 30 windows from System UI\nnecessitates either a larger buffer or a considerably shorter recording time\nwindow.\n\nInstructions\n------------\n\nFollow these instructions to onboard ViewCapture in system apps:\n\n1. Add the dependency to your `Android.bp` file, as shown in\n the [Launcher code](https://android.googlesource.com/platform/packages/apps/Launcher3/+/612b153591ed0cab68ae4d0dbbee152ceaaa7f67/Android.bp#155).\n\n android_library {\n name: \"YourLib\",\n static_libs: [\n ...\n \"//frameworks/libs/systemui:view_capture\",\n ...\n ],\n platform_apis: true,\n privileged: true,\n }\n\n2. Create a ViewCapture instance when creating your window, for example:\n\n - [Example 1](https://cs.android.com/android/platform/superproject/+/android-latest-release:packages/apps/Launcher3/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java;drc=c4f9510874ae0c75db03e5e7dab5b3df5a4acfe6;l=695):\n\n private SafeCloseable mViewCapture;\n\n @Override\n protected void onCreate(Bundle savedInstanceState) {\n ...\n mViewCapture = ViewCaptureFactory.getInstance(this).startCapture(getWindow());\n }\n\n - [Example 2](https://cs.android.com/android/platform/superproject/+/android-latest-release:frameworks/base/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java;l=86):\n\n private SafeCloseable mViewCapture;\n\n @Override\n protected void onAttachedToWindow() {\n super.onAttachedToWindow();\n if (enableViewCaptureTracing()) {\n mViewCaptureCloseable = ViewCaptureFactory.getInstance(getContext())\n .startCapture(getRootView(), \".NotificationShadeWindowView\");\n }\n ...\n }\n\n3. Close the ViewCapture instance when destroying your window, as shown in the\n following examples:\n\n - [Example 1](http://cs.android.com/android/_/android/platform/packages/apps/Launcher3/+/bbda0821a6e0c63c705a525970e5379a66ef8f22:quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java;l=561;drc=c4f9510874ae0c75db03e5e7dab5b3df5a4acfe6;bpv=0;bpt=0):\n\n @Override\n public void onDestroy() {\n ...\n if (mViewCapture != null) mViewCapture.close();\n }\n\n - [Example 2](https://cs.android.com/android/platform/superproject/+/android-latest-release:frameworks/base/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java;l=92):\n\n @Override\n protected void onDetachedFromWindow() {\n super.onDetachedFromWindow();\n if (mViewCaptureCloseable != null) {\n mViewCaptureCloseable.close();\n }\n ...\n }"]]