使用 Firebase Performance Monitoring 衡量加载时间和屏幕渲染情况

1. 简介

上次更新日期:2021 年 3 月 11 日

为什么需要衡量观看次数的效果?

视图是 Android 应用中直接影响用户体验的关键部分。例如,您的 Activity 或 Fragment 包含的界面会保存用户与之互动的 View 组件。用户看不到界面的全部内容,直到界面完全在屏幕上绘制为止。缓慢和冻结的屏幕会直接影响用户与应用的互动,导致糟糕的用户体验。

Firebase Performance Monitoring 不是以开箱即用的方式提供这些性能指标吗?

Firebase Performance Monitoring 可自动捕获一些开箱即用的性能数据,例如应用启动时间(仅第一个 activity 的加载时间)和屏幕呈现性能(即 activity 的呈现速度缓慢的帧和冻结的帧,但不适用于 fragment)。然而,行业应用通常不有很多 Activity,而只有一个 Activity 和多个 Fragment。此外,许多应用通常在更复杂的用例中实现自己的自定义视图。因此,了解如何通过检测应用中的自定义代码跟踪记录来衡量 activity 和 fragment 的加载时间和屏幕渲染性能通常很有用。您可以轻松扩展此 Codelab 来衡量自定义 View 组件的性能。

学习内容

  • 如何将 Firebase Performance Monitoring 添加到 Android 应用
  • 了解 activity 或 fragment 的加载
  • 如何对自定义代码跟踪记录进行插桩,以衡量 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 应用,并将必要的 Firebase 配置文件、插件和依赖项添加到我们的 Android 项目中 - 所有这些都在 Android Studio 中完成!

将您的应用关联至 Firebase

  1. 依次前往 Android Studio/Help > Check for updates,确保您使用的是最新版 Android Studio 和 Firebase Assistant。
  2. 依次选择 Tools > Firebase 以打开 Assistant 窗格。

e791bed0999db1e0.png

  1. 选择要将 Performance Monitoring 添加到您的应用,然后点击 Performance Monitoring 使用入门
  2. 点击 Connect to Firebase 将您的 Android 项目与 Firebase 关联(这将在浏览器中打开 Firebase 控制台)
  3. 在 Firebase 控制台中,点击添加项目,然后输入 Firebase 项目名称(如果您已经有 Firebase 项目,则可以改为选择该现有项目)。点击继续并接受条款,以创建 Firebase 项目和新的 Firebase 应用。
  4. 接下来,您应该会看到一个对话框,用于将新的 Firebase 应用连接到您的 Android Studio 项目。

42c498d28ead2b77

  1. 返回 Android Studio 后,您应该会在 Assistant 窗格中看到一条确认消息,确认您的应用已关联到 Firebase。

dda8bdd9488167a0

将性能监控添加到您的应用

在 Android Studio 的 Assistant 窗格中,点击 Add Performance Monitoring to your app

您应该会看到一个 Accept Changes 对话框,之后 Android Studio 应同步您的应用以确保已添加所有必要的依赖项。

9b58145acc4be030.png

最后,您应该会在 Android Studio 的 Assistant 窗格中看到一条成功消息,表示所有依赖项均已正确设置。

aa0d46fc944e0c0b

此外,您还可以按照“(可选)启用调试日志记录”步骤中的说明启用调试日志记录。另外,您也可以在公开文档中找到相关说明。

3. 运行应用

如果您已成功将应用与 Performance Monitoring SDK 集成,项目现在应该可以编译了。在 Android Studio 中,依次点击 Run > Run ‘app',以在连接的 Android 设备/模拟器上构建并运行应用。

该应用有两个按钮,可将您转到相应的 activity 和 fragment,如下所示:

410d8686b4f45c33

在此 Codelab 的后续步骤中,您将学习如何衡量 activity 或 fragment 的加载时间和屏幕渲染性能。

4.了解 activity 或 fragment 的加载

在此步骤中,我们将了解系统在加载 activity 或 fragment 期间执行的操作。

了解 activity 的加载

对于 activity,加载时间是指从创建 activity 对象一直到第一帧在屏幕上完全绘制所用的时间(这是用户首次看到 activity 的完整界面的时间)。如需衡量应用是否已完全绘制,您可以使用 reportFullyDrawn() 方法测量从应用启动到完全显示所有资源和视图层次结构所用的时间。

概括来讲,当您的应用调用 startActivity(Intent) 时,系统会自动执行以下进程。每个进程都需要一些时间才能完成,这会增加从创建 activity 到用户在屏幕上看到 activity 界面所需的时间。

c20d14b151549937

了解 Fragment 的加载

与 Activity 类似,Fragment 的加载时间是指,从 Fragment 附加到其宿主 Activity 开始,一直到 Fragment 视图的第一帧在屏幕上完全绘制的时间。

5. 衡量 Activity 的加载时间

第一帧中的延迟可能会导致糟糕的用户体验,因此务必要了解用户遇到的初始加载延迟有多大。您可以通过插桩自定义代码跟踪记录来衡量此加载时间:

  1. 创建 activity 对象后,立即在 activity 类中启动自定义代码跟踪记录(名为 TestActivity-LoadTime)。

TestActivity.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");

    // ...

}
  1. 替换 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);     

    // ...
}
  1. 我们添加了 FistDrawListener 的实现,它有两个回调:onDrawingStart()onDrawingFinish()(请参阅下文,详细了解 FirstDrawListener 以及可能影响其性能的因素)。在 activity 的 onCreate() 回调结束时注册 FirstDrawListener。您应该在 onDrawingFinish() 回调中停止 viewLoadTrace

TestActivity.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();             
        }         
    });
  1. 重新运行应用。然后,使用“Logging trace metric”过滤 logcat。点按 LOAD ACTIVITY 按钮,然后查找如下所示的日志:
I/FirebasePerformance: Logging trace metric: TestActivity-LoadTime (duration: XXXms)

🎉? 恭喜!您已成功衡量了某项活动的加载时间,并向 Firebase Performance Monitoring 报告了这些数据。在此 Codelab 稍后的部分中,我们将从 Firebase 控制台中查看记录的指标。

FirstDrawListener 的用途

在前面的部分中,我们注册了 FirstDrawListenerFirstDrawListener 的用途是测量第一帧何时开始绘制和完成绘制。

它实现了 ViewTreeObserver.OnDrawListener,并替换了在要绘制 View 树时调用的 onDraw() 回调。然后,它会封装结果以提供两个实用程序回调 onDrawingStart()onDrawingFinish()

FirstDrawListener 的完整代码可在此 Codelab 的源代码中找到。

6. 测量 fragment 的加载时间

衡量 fragment 的加载时间与测量 activity 的加载时间类似,但有一些细微差异。同样,我们将对自定义代码跟踪记录进行插桩处理:

  1. 替换 onAttach() 回调并开始录制 fragmentLoadTrace。我们将此跟踪记录命名为 Test-Fragment-LoadTime

如前一步骤所述,Fragment 对象可以随时创建,但它只有在附加到其宿主 Activity 后才会变为活动状态。

TestFragment.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");
   }
  1. onViewCreated() 回调中注册 FirstDrawListener。然后,与 activity 示例类似,在 onDrawingFinish() 中停止跟踪。

TestFragment.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();
       }
   });
  1. 重新运行应用。然后,使用“Logging trace metric”过滤 logcat。点按 LOAD FRAGMENT 按钮,然后查找如下所示的日志:
I/FirebasePerformance: Logging trace metric: TestFragment-LoadTime (duration: XXXms)

🎉? 恭喜!您已成功测量了 fragment 的加载时间,并向 Firebase Performance Monitoring 报告了这些数据。在此 Codelab 稍后的部分中,我们将从 Firebase 控制台中查看记录的指标。

7. 了解屏幕呈现以及什么是慢帧/冻结的帧

界面呈现是指从应用生成帧并将其显示在屏幕上的行为。为确保用户能够顺畅地与您的应用互动,您的应用呈现每帧的时间不应超过 16 毫秒,从而达到每秒 60 帧的帧速率(为什么是 60fps?)。如果您的应用存在界面呈现缓慢的问题,系统会不得不跳过一些帧,用户会感觉您的应用不流畅。我们将这种情况称为卡顿

同样,冻结的帧是指呈现时间超过 700ms 的界面帧。这种延迟是个问题,因为在帧呈现期间,您的应用几乎有一秒钟的时间卡住不动,对用户输入无响应。

8. 测量 fragment 的缓慢/冻结帧

Firebase Performance Monitoring 会自动捕获 activity 的缓慢/冻结的帧(但仅限硬件加速时)。不过,此功能目前不适用于 Fragment。fragment 的缓慢/冻结的帧是指在 fragment 生命周期的 onFragmentAttached()onFragmentDetached() 回调之间整个 activity 的缓慢/冻结帧。

AppStateMonitor 类(它是 Performance Monitoring SDK 中负责记录 activity 屏幕跟踪记录的一部分)的动力,我们实现了 ScreenTrace 类(这是此 Codelab 源代码库的一部分)。ScreenTrace 类可以挂接到 activity 的 FragmentManager 的生命周期回调,以捕获缓慢/冻结的帧。此类提供了两个公共 API:

  • recordScreenTrace():开始录制屏幕跟踪记录
  • sendScreenTrace():停止录制屏幕跟踪记录,并附加自定义指标来记录“总计”“慢帧”和“冻结帧”的数量

通过附加这些自定义指标,fragment 的屏幕跟踪记录的处理方式与 Activity 的屏幕跟踪记录的处理方式相同,并且能够与其他屏幕呈现跟踪记录一起显示在 Firebase 控制台的“性能”信息中心内。

记录 fragment 的屏幕跟踪记录的方法如下:

  1. 在托管 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);

    // ...
}
  1. 加载 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);
           }
       };
  1. 重新运行应用。然后点按 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

🎉? 恭喜!您已成功测量了 fragment 的慢帧/冻结帧,并向 Firebase Performance Monitoring 报告了这些数据。在此 Codelab 稍后的部分中,我们将通过 Firebase 控制台查看记录的指标。

9. 在 Firebase 控制台中查看指标

  1. 在 logcat 中,点击 Firebase 控制台网址以访问跟踪记录的详情页面。ceb9d5ba51bb6e89.jpeg

或者,在 Firebase 控制台中,选择包含您的应用的项目。在左侧面板中,找到发布和监控部分,然后点击性能

  • 信息中心主标签页中,向下滚动到跟踪记录表,然后点击自定义跟踪记录标签页。在此表中,您将看到我们之前添加的自定义代码跟踪记录以及一些开箱即用的跟踪记录,例如 _app_start 跟踪记录。
  • 找到两个自定义代码跟踪记录 TestActivity-LoadTimeTestFragment-LoadTime。点击任意一个时间范围的时长,即可查看有关已收集数据的更多详细信息。

a0d8455c5269a590

  1. 自定义代码跟踪记录的详情页面会显示有关跟踪记录时长的信息(即测量的加载时间)。

5e92a307b7410d8b

  1. 您还可以查看自定义屏幕跟踪记录的性能数据。
  • 返回信息中心主标签页,向下滚动到跟踪记录表,然后点击屏幕呈现标签页。在此表中,您将看到我们之前添加的自定义屏幕跟踪记录以及所有开箱屏幕跟踪记录,例如 MainActivity 跟踪记录。
  • 找到您的自定义屏幕跟踪记录 MainActivity-TestFragment。点击轨迹名称即可查看呈现速度缓慢的帧和冻结的帧的汇总数据。

e7890c7e2c28740

10. 恭喜

恭喜!您已成功使用 Firebase Performance Monitoring 衡量了 activity 和 fragment 的加载时间和屏幕渲染性能!

您已取得的成就

后续步骤

除了自定义跟踪记录之外,Firebase 性能还提供了更多应用性能衡量方式。它会自动衡量应用启动时间、应用前台和应用后台性能数据。现在,您就可以在 Firebase 控制台中查看这些指标了。

此外,Firebase 性能还提供自动监控 HTTP/HTTPS 网络请求的功能。这样一来,您无需编写任何代码,即可轻松对网络请求进行插桩 (instrument)。您能否尝试从您的应用发送一些网络请求,并在 Firebase 控制台中找到这些指标?

花絮

现在,您已经知道如何使用自定义代码跟踪记录衡量 activity/fragment 的加载时间和屏幕呈现性能了。接下来,您可以浏览我们的开源代码库,看看能否开箱为应用中任何 activity/fragment 捕获这些指标吗?如有需要,欢迎随时发送公关信函 :-)

11. 额外学习

了解 activity 加载期间发生的情况将有助于您更好地了解应用的性能特征。在之前的步骤中,我们简要说明了 activity 加载期间发生的情况,但下图更详细地介绍了每个阶段。

cd61c1495fad7961