一、简介
最后更新: 2021-03-11
为什么我们需要衡量Views的性能?
视图是Android应用程序的关键部分,直接影响用户体验。例如,您的 Activity 或 Fragment 包含 UI,其中包含用户与之交互的 View 组件。在 UI 完全绘制在屏幕上之前,用户无法看到 UI 的全部内容。缓慢和冻结的屏幕将直接损害用户与应用程序的交互,并造成糟糕的用户体验。
Firebase 性能监控是否不提供这些开箱即用的性能指标?
Firebase 性能监控会自动捕获一些开箱即用的性能数据,例如您的应用程序启动时间(即仅第一个Activity 的加载时间)和屏幕渲染性能(即 Activity 的缓慢和冻结帧,但不是碎片)。然而,行业应用通常没有很多Activity,而是一个Activity和多个Fragment。此外,许多应用程序通常会为更复杂的用例实现自己的自定义视图。因此,了解如何通过在应用程序中检测自定义代码跟踪来测量“活动”和“片段”的加载时间和屏幕渲染性能通常很有用。您可以轻松扩展此 Codelab 以测量自定义视图组件的性能。
你将学到什么
- 如何将 Firebase 性能监控添加到 Android 应用
- 了解 Activity 或 Fragment 的加载
- 如何检测自定义代码跟踪以测量活动或片段的加载时间
- 了解屏幕渲染以及什么是慢帧/冻结帧
- 如何使用指标来检测自定义代码跟踪以记录缓慢/冻结的屏幕
- 如何在 Firebase 控制台中查看收集的指标
你需要什么
- Android Studio 4.0或更高版本
- Android 设备/模拟器
- Java 版本 8 或更高版本
2. 设置
获取代码
运行以下命令来克隆此 Codelab 的示例代码。这将在您的计算机上创建一个名为codelab-measure-android-view-performance
的文件夹:
$ git clone https://github.com/FirebaseExtended/codelab-measure-android-view-performance.git
$ cd codelab-measure-android-view-performance
如果你的机器上没有git,也可以直接从GitHub下载代码。
将measure-view-performance-start
项目导入Android Studio。您可能会看到一些编译错误,或者可能会看到有关缺少google-services.json
文件的警告。我们将在此步骤的下一部分中更正此问题。
在此 Codelab 中,我们将使用Firebase Assistant插件向 Firebase 项目注册我们的 Android 应用,并向我们的 Android 项目添加必要的 Firebase 配置文件、插件和依赖项 -所有这些都在 Android Studio 中进行!
将您的应用连接到 Firebase
- 转到Android Studio /帮助>检查更新,确保您使用的是最新版本的 Android Studio 和 Firebase Assistant。
- 选择“工具” > “Firebase”以打开“助手”窗格。
- 选择要添加到您的应用程序的性能监控,然后单击性能监控入门。
- 单击“连接到 Firebase”以将您的 Android 项目与 Firebase 连接(这将在浏览器中打开 Firebase 控制台) 。
- 在 Firebase 控制台中,点击添加项目,然后输入 Firebase 项目名称(如果您已有 Firebase 项目,则可以选择该现有项目) 。单击继续并接受条款以创建 Firebase 项目和新的 Firebase 应用。
- 接下来您应该会看到一个对话框,用于将新的 Firebase 应用程序连接到您的 Android Studio 项目。
- 返回 Android Studio,在Assistant窗格中,您应该会看到您的应用已连接到 Firebase 的确认信息。
将性能监控添加到您的应用程序
在 Android Studio 的“助手”窗格中,单击“将性能监控添加到您的应用程序” 。
您应该会看到一个接受更改的对话框,之后 Android Studio 应该同步您的应用程序以确保已添加所有必要的依赖项。
最后,您应该在 Android Studio 的“助手”窗格中看到成功消息,表明所有依赖项均已正确设置。
作为附加步骤,请按照“(可选)启用调试日志记录”步骤中的说明启用调试日志记录。公共文档中也提供了相同的说明。
3.运行应用程序
如果您已成功将应用程序与性能监控 SDK 集成,则该项目现在应该可以编译。在 Android Studio 中,单击运行>运行“应用程序”以在连接的 Android 设备/模拟器上构建并运行应用程序。
该应用程序有两个按钮,可将您带到相应的 Activity 和 Fragment,如下所示:
在此 Codelab 的以下步骤中,您将了解如何测量 Activity 或 Fragment 的加载时间和屏幕渲染性能。
4. 理解Activity或Fragment的加载
在这一步中,我们将了解系统在加载 Activity 或 Fragment 期间正在做什么。
了解 Activity 的加载
对于 Activity,加载时间定义为从 Activity 对象创建一直到第一帧完全绘制在屏幕上的时间(这是用户第一次看到 Activity 的完整 UI 的时间)时间)。要测量您的应用程序是否已完全绘制,您可以使用reportFullyDrawn()
方法来测量应用程序启动和完全显示所有资源和视图层次结构之间所经过的时间。
从较高的层面来看,当您的应用程序调用startActivity(Intent)
时,系统会自动执行以下过程。每个过程都需要时间才能完成,这会增加创建 Activity 和用户在屏幕上看到该 Activity 的 UI 之间的持续时间。
了解 Fragment 的加载
与 Activity 类似,Fragment 的加载时间定义为从 Fragment 附加到其宿主 Activity 开始一直到 Fragment View 的第一帧完全绘制在屏幕上的时间。
5. 测量Activity的加载时间
第一帧的延迟可能会导致糟糕的用户体验,因此了解用户所经历的初始加载延迟程度非常重要。您可以检测自定义代码跟踪来测量此加载时间:
- 创建 Activity 对象后,立即在 Activity 类中启动自定义代码跟踪(名为
TestActivity-LoadTime
)。
测试活动.java
public class TestActivity extends AppCompatActivity {
// TODO (1): Start trace recording as soon as the Activity object is created.
private final Trace viewLoadTrace = FirebasePerformance.startTrace("TestActivity-LoadTime");
// ...
}
- 重写
onCreate()
回调,并获取setContentView()
方法添加的 View。
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Current Activity's main View (as defined in the layout xml file) is inflated after this
setContentView(R.layout.activity_test);
// ...
// TODO (2): Get the View added by Activity's setContentView() method.
View mainView = findViewById(android.R.id.content);
// ...
}
- 我们已经包含了
FistDrawListener
的实现,它有两个回调:onDrawingStart()
和onDrawingFinish()
(有关FirstDrawListener
的更多详细信息以及影响其性能的因素,请参阅下面的下一节)。在 Activity 的onCreate()
回调结束时注册FirstDrawListener
。您应该在onDrawingFinish()
回调中停止viewLoadTrace
。
测试活动.java
// TODO (3): Register the callback to listen for first frame rendering (see
// "OnFirstDrawCallback" in FirstDrawListener) and stop the trace when View drawing is
// finished.
FirstDrawListener.registerFirstDrawListener(mainView, new FirstDrawListener.OnFirstDrawCallback() {
@Override
public void onDrawingStart() {
// In practice you can also record this event separately
}
@Override
public void onDrawingFinish() {
// This is when the Activity UI is completely drawn on the screen
viewLoadTrace.stop();
}
});
- 重新运行应用程序。然后,使用“ Logging Trace metric ”过滤 logcat。点击
LOAD ACTIVITY
按钮,然后查找如下日志:
I/FirebasePerformance: Logging trace metric: TestActivity-LoadTime (duration: XXXms)
🎉恭喜!您已成功测量活动的加载时间并将该数据报告给 Firebase 性能监控。我们稍后将在此 Codelab 中的 Firebase 控制台中查看记录的指标。
FirstDrawListener 的用途
在上面的部分中,我们注册了一个FirstDrawListener
。 FirstDrawListener
的目的是测量第一帧何时开始并完成绘制。
它实现ViewTreeObserver.OnDrawListener
并覆盖即将绘制视图树时调用的onDraw()
回调。然后它包装结果以提供两个实用程序回调onDrawingStart()
和onDrawingFinish()
。
FirstDrawListener
的完整代码可以在此Codelab 的源代码中找到。
6. 测量Fragment的加载时间
测量 Fragment 的加载时间与我们测量 Activity 的加载时间类似,但有一些细微的差别。同样,我们将检测自定义代码跟踪:
- 覆盖
onAttach()
回调并开始记录fragmentLoadTrace
。我们将此跟踪命名Test-Fragment-LoadTime
。
正如前面步骤中所解释的,Fragment 对象可以随时创建,但只有当它附加到其宿主 Activity 时,它才会变为活动状态。
测试片段.java
public class TestFragment extends Fragment {
// TODO (1): Declare the Trace variable.
private Trace fragmentLoadTrace;
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
// TODO (2): Start trace recording as soon as the Fragment is attached to its host Activity.
fragmentLoadTrace = FirebasePerformance.startTrace("TestFragment-LoadTime");
}
- 在
onViewCreated()
回调中注册FirstDrawListener
。然后,与 Activity 示例类似,在onDrawingFinish()
中停止跟踪。
测试片段.java
@Override
public void onViewCreated(@NonNull View mainView, Bundle savedInstanceState) {
super.onViewCreated(mainView, savedInstanceState);
// ...
// TODO (3): Register the callback to listen for first frame rendering (see
// "OnFirstDrawCallback" in FirstDrawListener) and stop the trace when view drawing is
// finished.
FirstDrawListener.registerFirstDrawListener(mainView, new FirstDrawListener.OnFirstDrawCallback() {
@Override
public void onDrawingStart() {
// In practice you can also record this event separately
}
@Override
public void onDrawingFinish() {
// This is when the Fragment UI is completely drawn on the screen
fragmentLoadTrace.stop();
}
});
- 重新运行应用程序。然后,使用“ Logging Trace metric ”过滤 logcat。点击
LOAD FRAGMENT
按钮,然后查找如下日志:
I/FirebasePerformance: Logging trace metric: TestFragment-LoadTime (duration: XXXms)
🎉恭喜!您已成功测量 Fragment 的加载时间并将该数据报告给 Firebase 性能监控。我们稍后将在此 Codelab 中的 Firebase 控制台中查看记录的指标。
7. 了解屏幕渲染以及什么是慢帧/冻结帧
UI 渲染是从应用程序生成框架并将其显示在屏幕上的行为。为了确保用户与应用程序的交互顺畅,您的应用程序应在 16 毫秒内渲染帧,以达到每秒 60 帧(为什么是 60 fps? )。如果您的应用程序的 UI 渲染速度较慢,则系统将被迫跳帧,并且用户会感觉到您的应用程序出现卡顿现象。我们称之为卡顿。
同样,冻结帧是渲染时间超过 700 毫秒的 UI 帧。这种延迟是一个问题,因为您的应用程序似乎被卡住了,并且在帧渲染时几乎一整秒都没有响应用户输入。
8. 测量片段的慢速/冻结帧
Firebase 性能监控会自动捕获活动的慢速/冻结帧(但前提是该活动是硬件加速的)。但是,此功能目前不适用于 Fragment。 Fragment 的慢/冻结帧定义为 Fragment 生命周期中onFragmentAttached()
和onFragmentDetached()
回调之间整个 Activity 的慢/冻结帧。
受到AppStateMonitor
类(它是性能监控 SDK 的一部分,负责记录 Activity 的屏幕跟踪)的启发,我们实现了ScreenTrace
类(它是此 Codelab 源代码存储库的一部分)。 ScreenTrace
类可以连接到 Activity 的FragmentManager
的生命周期回调来捕获缓慢/冻结的帧。该类提供了两个公共API:
-
recordScreenTrace()
:开始记录屏幕轨迹 sendScreenTrace()
:停止屏幕跟踪记录并附加自定义指标来记录总帧数、慢帧数和冻结帧数
通过附加这些自定义指标,片段的屏幕跟踪可以与活动的屏幕跟踪相同的方式处理,并且可以与其他屏幕渲染跟踪一起显示在 Firebase 控制台的性能仪表板中。
以下是记录 Fragment 的屏幕跟踪的方法:
- 在托管 Fragment 的 Activity 中初始化
ScreenTrace
类。
MainActivity.java
// Declare the Fragment tag
private static final String FRAGMENT_TAG = TestFragment.class.getSimpleName();
// TODO (1): Declare the ScreenTrace variable.
private ScreenTrace screenTrace;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// TODO (2): Initialize the ScreenTrace variable.
screenTrace = new ScreenTrace(this, FRAGMENT_TAG);
// ...
}
- 当您加载 Fragment 时,注册
FragmentLifecycleCallbacks
并覆盖onFragmentAttached()
和onFragmentDetached()
回调。我们已经为您做到了这一点。您需要在onFragmentAttached()
回调中开始记录屏幕轨迹,并在onFragmentDetached()
回调中停止记录。
MainActivity.java
private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks =
new FragmentManager.FragmentLifecycleCallbacks() {
@Override
public void onFragmentAttached(@NonNull FragmentManager fm, @NonNull Fragment f, @NonNull Context context) {
super.onFragmentAttached(fm, f, context);
// TODO (3): Start recording the screen traces as soon as the Fragment is
// attached to its host Activity.
if (FRAGMENT_TAG.equals(f.getTag()) && screenTrace.isScreenTraceSupported()) {
screenTrace.recordScreenTrace();
}
}
@Override
public void onFragmentDetached(@NonNull FragmentManager fm, @NonNull Fragment f) {
super.onFragmentDetached(fm, f);
// TODO (4): Stop recording the screen traces as soon as the Fragment is
// detached from its host Activity.
if (FRAGMENT_TAG.equals(f.getTag()) && screenTrace.isScreenTraceSupported()) {
screenTrace.sendScreenTrace();
}
// Unregister Fragment lifecycle callbacks after the Fragment is detached
fm.unregisterFragmentLifecycleCallbacks(fragmentLifecycleCallbacks);
}
};
- 重新运行应用程序。然后,点击
LOAD FRAGMENT
按钮。等待几秒钟,然后单击底部导航栏上的back button
。
使用“ Logging Trace metric ”过滤 logcat,然后查找如下日志:
I/FirebasePerformance: Logging trace metric: _st_MainActivity-TestFragment (duration: XXXms)
使用“ FireperfViews ”过滤 logcat,然后查找如下日志:
D/FireperfViews: sendScreenTrace MainActivity-TestFragment, name: _st_MainActivity-TestFragment, total_frames: XX, slow_frames: XX, frozen_frames: XX
🎉恭喜!您已成功测量片段的慢帧/冻结帧,并将该数据报告给 Firebase 性能监控。我们稍后将在本 Codelab 中查看 Firebase 控制台中记录的指标。
9. 在 Firebase 控制台中检查指标
- 在 logcat 中,单击 Firebase 控制台 URL 以访问跟踪的详细信息页面。
或者,在Firebase 控制台中,选择包含您的应用的项目。在左侧面板中,找到“发布和监控”部分,然后单击“性能” 。
- 在主仪表板选项卡中,向下滚动到跟踪表,然后单击自定义跟踪选项卡。在此表中,您将看到我们之前添加的自定义代码跟踪以及一些现成的跟踪,例如
_app_start
跟踪。 - 找到两个自定义代码跟踪:
TestActivity-LoadTime
和TestFragment-LoadTime
。单击任一持续时间可查看有关所收集数据的更多详细信息。
- 自定义代码跟踪的详细信息页面显示有关跟踪持续时间的信息(即测量的加载时间)。
- 您还可以查看自定义屏幕跟踪的性能数据。
- 返回主仪表板选项卡,向下滚动到跟踪表,然后单击屏幕渲染选项卡。在此表中,您将看到我们之前添加的自定义屏幕跟踪以及任何现成的屏幕跟踪,例如
MainActivity
跟踪。 - 找到您的自定义屏幕跟踪
MainActivity-TestFragment
。单击跟踪名称可查看慢速渲染和冻结帧的聚合数据。
10.恭喜你
恭喜!您已使用 Firebase 性能监控成功测量了 Activity 和 Fragment 的加载时间和屏幕渲染性能!
你已经完成了什么
- 您将 Firebase 性能监控集成到示例应用中
- 现在你已经了解了View加载的生命周期了
- 您通过添加自定义代码跟踪来测量 Activity 和 Fragment 的加载时间
- 您通过添加具有自定义指标的自定义屏幕跟踪记录了慢速/冻结帧
下一步是什么
除了自定义跟踪之外,Firebase Performance 还提供了更多应用性能测量方法。它自动测量应用程序启动时间、应用程序在前台和应用程序在后台的性能数据。现在您可以在Firebase Console中检查这些指标了。
此外,Firebase Performance 还提供自动 HTTP/S 网络请求监控。这样您就可以轻松地检测网络请求,而无需编写任何代码。您可以尝试从您的应用程序发送一些网络请求并在Firebase 控制台中查找指标吗?
奖金
现在您已经知道如何使用自定义代码跟踪来测量 Activity/Fragment 的加载时间和屏幕渲染性能,您可以探索我们的开源代码库,看看是否可以捕获任何 Activity/Fragment 的开箱即用的这些指标这是应用程序的一部分?如果您愿意,请随时发送 PR :-)
11. 奖励学习
了解 Activity 加载过程中发生的情况将帮助您更好地了解应用程序的性能特征。在前面的步骤中,我们概括地描述了 Activity 加载过程中发生的情况,但下图更详细地描述了每个阶段。