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

1. 简介

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

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

视图是 Android 应用中直接影响用户体验的关键部分。例如,您的 Activity 或 Fragment 包含界面,该界面包含用户与之互动的 View 组件。用户无法看到界面的全部内容,直到界面完全绘制在屏幕上。屏幕缓慢和卡顿会直接影响用户与应用的互动,并造成糟糕的用户体验。

Firebase Performance Monitoring 不是开箱即用,可提供这些性能指标吗?

Firebase 性能监控可开箱即用地自动捕获一些性能数据,例如应用启动时间(即仅首次 Activity 的加载时间)和界面渲染性能(即 Activity 的慢帧和冻结帧,但 Fragment 的慢帧和冻结帧除外)。不过,行业应用通常不会有很多 activity,而是一个 activity 和多个 fragment。此外,许多应用通常会针对更复杂的用例实现自己的自定义视图。因此,通过在应用中对自定义代码跟踪记录进行插桩,了解如何衡量 Activity 和 Fragment 的加载时间和屏幕渲染性能通常很有用。您可以轻松扩展此 Codelab,以衡量自定义视图组件的性能。

学习内容

  • 如何向 Android 应用添加 Firebase Performance Monitoring
  • 了解 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
  3. 选择要添加到应用的 Performance Monitoring,然后点击开始使用 Performance Monitoring
  4. 点击相应按钮以创建新项目,然后输入项目名称(例如 Measure Performance Codelab)。
  5. 点击继续
  6. 如果看到相关提示,请查看并接受 Firebase 条款,然后点击继续
  7. (可选)在 Firebase 控制台中启用 AI 辅助功能(称为“Gemini in Firebase”)。
  8. 在此 Codelab 中,您不需要使用 Google Analytics,因此请关闭 Google Analytics 选项。
  9. 接下来,您应该会看到一个对话框,用于将新的 Firebase 应用关联到您的 Android Studio 项目。
    42c498d28ead2b77.png
  10. 返回 Android Studio,在 Assistant 窗格中,您应该会看到确认消息,表明您的应用已连接到 Firebase。
    dda8bdd9488167a0.png

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

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

您应该会看到一个用于接受更改的对话框,之后 Android Studio 应该会同步您的应用,以确保已添加所有必需的依赖项。

9b58145acc4be030.png

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

aa0d46fc944e0c0b.png

作为附加步骤,请按照“(可选)启用调试日志记录”步骤中的说明启用调试日志记录。您也可以在公开文档中找到相同的说明。

3. 运行应用

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

该应用有两个按钮,可将您带到相应的 Activity 和 Fragment,如下所示:

410d8686b4f45c33.png

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

4. 了解 activity 或 fragment 的加载

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

了解 activity 的加载

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

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

c20d14b151549937.png

了解 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() 方法添加的视图。
@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. 重新运行应用,然后使用“记录跟踪指标”过滤 logcat。点按 LOAD ACTIVITY 按钮,然后查找如下所示的日志:
I/FirebasePerformance: Logging trace metric: TestActivity-LoadTime (duration: XXXms)

🎉 恭喜!您已成功测量 Activity 的加载时间,并将该数据报告给 Firebase Performance Monitoring。在本 Codelab 的后面部分,我们将在 Firebase 控制台中查看记录的指标。

FirstDrawListener 的用途

在上一部分中,我们注册了一个 FirstDrawListenerFirstDrawListener 的目的是衡量第一帧何时开始和完成绘制。

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

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

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

🎉 恭喜!您已成功测量 Fragment 的加载时间,并将该数据报告给 Firebase Performance Monitoring。在本 Codelab 的后面部分,我们将在 Firebase 控制台中查看记录的指标。

7. 了解屏幕渲染以及什么是缓慢帧/冻结帧

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

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

8. 衡量 Fragment 的慢帧/冻结帧

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

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

  • recordScreenTrace():开始记录屏幕轨迹
  • sendScreenTrace():停止记录屏幕轨迹,并将自定义指标附加到日志总帧数、慢帧数和冻结帧数

通过附加这些自定义指标,您可以像处理 Activity 的屏幕轨迹一样处理 Fragment 的屏幕轨迹,并且可以在 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

使用“记录跟踪指标”过滤 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.png

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

5e92a307b7410d8b.png

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

ee7890c7e2c28740.png

10. 恭喜

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

您已取得的成就

后续步骤

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

此外,Firebase Performance 还提供自动 HTTP/HTTPS 网络请求监控。这样一来,您无需编写任何代码即可轻松检测网络请求。您能否尝试从应用发送一些网络请求,然后在 Firebase 控制台中查找相应指标?

奖励

现在,您已经了解如何使用自定义代码跟踪记录来衡量 Activity/Fragment 的加载时间和屏幕呈现性能,接下来可以探索我们的开源代码库,看看是否可以开箱即用地捕获应用中任何 Activity/Fragment 的这些指标。如果您愿意,欢迎随时发送 PR :-)

11. 奖励学习

了解 Activity 加载期间发生的情况有助于您更好地了解应用的性能特征。在之前的步骤中,我们从较高层面描述了 Activity 加载期间发生的情况,但下图更详细地描述了每个阶段。

cd61c1495fad7961.png