使用 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,以衡量自定义视图组件的性能。

学习内容

  • 如何将 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

  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

最后,您应该会在 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)

🎉? 恭喜!您已成功测量了 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. 重新运行应用。然后,使用“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?)。如果您的应用存在界面渲染缓慢的问题,系统会不得不跳过一些帧,这会导致用户感觉您的应用不流畅。我们将这种情况称为卡顿

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

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

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

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

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

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

以下是为 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。点击跟踪记录名称,查看呈现速度缓慢的帧和冻结的帧的汇总数据。

ee7890c7e2c28740

10. 恭喜

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

您的成果

后续步骤

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

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

花絮

现在,您已经知道了如何使用自定义代码跟踪记录衡量 activity/fragment 的加载时间和屏幕渲染性能。接下来,您可以探索我们的开源代码库,看看能否为应用中的任何 activity/fragment 直接捕获这些指标。如果愿意,请随时发送 PR :-)

11. 免费学习

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

cd61c1495fad7961