使用 Firebase 性能监控测量加载时间和屏幕渲染

一、简介

最后更新: 2021-03-11

为什么我们需要衡量 Views 的性能?

视图是直接影响用户体验的 Android 应用程序的关键部分。例如,您的 Activity 或 Fragment 包含包含用户与之交互的 View 组件的 UI。在 UI 完全绘制在屏幕上之前,用户无法看到 UI 的全部内容。缓慢和冻结的屏幕将直接影响用户与您的应用程序的交互并造成糟糕的用户体验。

Firebase 性能监控不是开箱即用地提供这些性能指标吗?

Firebase 性能监控会自动捕获一些开箱即用的性能数据,例如您的应用程序启动时间(即,仅您的第一个Activity 的加载时间)和屏幕渲染性能(即,Activity 的缓慢和冻结帧,但不是片段)。但是,行业应用程序通常不会有很多活动,而是一个活动和多个片段。此外,许多应用程序通常为更复杂的用例实现自己的自定义视图。因此,了解如何通过检测应用程序中的自定义代码跟踪来测量活动和片段的加载时间和屏幕渲染性能通常很有用。您可以轻松扩展此 Codelab 以测量自定义视图组件的性能。

你会学到什么

  • 如何将 Firebase 性能监控添加到 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插件将我们的 Android 应用程序注册到 Firebase 项目,并将必要的 Firebase 配置文件、插件和依赖项添加到我们的 Android 项目中——所有这些都来自 Android Studio

将您的应用连接到 Firebase

  1. 转到Android Studio /帮助>检查更新以确保您使用的是最新版本的 Android Studio 和 Firebase 助手。
  2. 选择工具> Firebase以打开助手窗格。

e791bed0999db1e0.png

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

42c498d28ead2b77.png

  1. 返回 Android Studio,在Assistant窗格中,您应该会看到您的应用已连接到 Firebase 的确认信息。

dda8bdd9488167a0.png

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

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

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

9b58145acc4be030.png

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

aa0d46fc944e0c0b.png

作为附加步骤,请按照步骤“(可选)启用调试日志记录”中的说明启用调试日志记录。公共文档中也提供了相同的说明。

3. 运行应用程序

如果您已成功将应用程序与性能监控 SDK 集成,则该项目现在应该可以编译了。在 Android Studio 中,单击运行>运行“应用程序”以在连接的 Android 设备/模拟器上构建和运行应用程序。

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

410d8686b4f45c33.png

在本 Codelab 的以下步骤中,您将学习如何测量 Activity 或 Fragment 的加载时间和屏幕渲染性能。

4.了解Activity或Fragment的加载

在这一步中,我们将了解系统在加载 Activity 或 Fragment 期间正在做什么。

了解 Activity 的加载

对于 Activity,加载时间定义为从 Activity 对象创建到第一帧完全绘制在屏幕上的时间(这是您的用户第一次看到 Activity 的完整 UI 的时间)时间)。要测量您的应用程序是否已完全绘制,您可以使用reportFullyDrawn()方法来测量应用程序启动与完全显示所有资源和视图层次结构之间的经过时间。

概括地说,当您的应用调用startActivity(Intent)时,系统会自动执行以下过程。每个过程都需要时间来完成,这增加了 Activity 创建和用户在屏幕上看到 Activity 的 UI 之间的时间。

c20d14b151549937.png

了解片段的加载

与 Activity 类似,Fragment 的加载时间定义为从 Fragment 一直连接到其宿主 Activity 到 Fragment View 的第一帧完全绘制在屏幕上的时间。

5.测量一个Activity的加载时间

第一帧的延迟可能会导致糟糕的用户体验,因此了解用户所经历的初始加载延迟非常重要。您可以使用自定义代码跟踪来测量此加载时间:

  1. 创建 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");

    // ...

}
  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

测试活动.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 性能监控。我们稍后将在此 Codelab 中在 Firebase 控制台中查看记录的指标。

FirstDrawListener 的用途

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

它实现了ViewTreeObserver.OnDrawListener并覆盖了在视图树即将被绘制时调用的onDraw()回调。然后它包装结果以提供两个实用程序回调onDrawingStart()onDrawingFinish()

FirstDrawListener的完整代码可以在这个codelab 的源代码中找到。

6.测量一个Fragment的加载时间

测量 Fragment 的加载时间与我们测量 Activity 的方法类似,但有一些细微差别。同样,我们将检测自定义代码跟踪

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

🎉 恭喜!您已成功测量 Fragment 的加载时间并将该数据报告给 Firebase 性能监控。我们稍后将在此 Codelab 中在 Firebase 控制台中查看记录的指标。

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

UI 渲染是从您的应用程序生成框架并将其显示在屏幕上的行为。为确保用户与您的应用程序交互顺畅,您的应用程序应在 16 毫秒内渲染帧以达到每秒 60 帧(为什么是 60fps? )。如果您的应用遇到 UI 渲染缓慢的问题,那么系统会被迫跳帧,用户会在您的应用中感觉到卡顿。我们称之为jank

同样,冻结帧是渲染时间超过 700 毫秒的 UI 帧。这种延迟是一个问题,因为您的应用程序似乎被卡住了,并且在渲染帧时几乎整整一秒钟都没有响应用户输入。

8. 测量片段的慢速/冻结帧

Firebase 性能监控会自动捕获 Activity 的慢速/冻结帧(但前提是它是硬件加速的)。但是,此功能目前不适用于 Fragments。 Fragment 的慢/冻结帧定义为在 Fragment 生命周期中onFragmentAttached()onFragmentDetached()回调之间的整个 Activity 的慢/冻结帧。

AppStateMonitor类(它是 Performance Monitoring SDK 的一部分,负责记录 Activity 的屏幕跟踪)中汲取灵感,我们实现了ScreenTrace类(它是这个 codelab 源代码 repo 的一部分)。 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() onFragmentAttached()回调中停止记录。

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

🎉 恭喜!您已成功测量片段的慢速/冻结帧并将该数据报告给 Firebase 性能监控。我们稍后将在此 Codelab 中在 Firebase 控制台中查看记录的指标。

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

  1. 在 logcat 中,单击 Firebase 控制台 URL 以访问详细信息页面以进行跟踪。 ceb9d5ba51bb6e89.jpeg

或者,在Firebase 控制台中,选择包含您的应用的项目。在左侧面板中,找到Release & Monitor部分,然后单击Performance

  • 在主仪表板选项卡中,向下滚动到跟踪表,然后单击自定义跟踪选项卡。在此表中,您将看到我们之前添加的自定义代码跟踪以及一些开箱即用的跟踪,例如_app_start跟踪。
  • 找到您的两个自定义代码跟踪, TestActivity-LoadTimeTestFragment-LoadTime 。单击任一时间的持续时间以查看有关收集数据的更多详细信息。

a0d8455c5269a590.png

  1. 自定义代码跟踪的详细信息页面显示有关跟踪持续时间的信息(即测量的加载时间)。

5e92a307b7410d8b.png

  1. 您还可以查看自定义屏幕跟踪的性能数据。
  • 返回主仪表板选项卡,向下滚动到跟踪表,然后单击屏幕渲染选项卡。在此表中,您将看到我们之前添加的自定义屏幕跟踪以及任何现成的屏幕跟踪,例如MainActivity跟踪。
  • 找到您的自定义屏幕跟踪MainActivity-TestFragment 。点击轨迹名称可以查看慢渲染和冻结帧的聚合数据。

ee7890c7e2c28740.png

10. 恭喜

恭喜!您已经使用 Firebase 性能监控成功测量了 Activity 和 Fragment 的加载时间和屏幕渲染性能!

你已经完成了什么

下一步是什么

Firebase Performance 提供了除自定义跟踪之外的更多应用性能测量方法。它自动测量应用程序启动时间、应用程序在前台和应用程序在后台性能数据。是时候在Firebase 控制台中检查这些指标了。

此外,Firebase Performance 还提供自动 HTTP/S 网络请求监控。有了它,您无需编写任何代码即可轻松检测网络请求。您可以尝试从您的应用发送一些网络请求并在Firebase 控制台中查找指标吗?

奖金

既然您知道如何使用自定义代码跟踪来衡量 Activity/Fragment 的加载时间和屏幕渲染性能,那么您可以探索我们的开源代码库,看看您是否可以为任何 Activity/Fragment 捕获这些指标这是应用程序的一部分?如果您愿意,请随时发送 PR :-)

11. 奖励学习

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

cd61c1495fad7961.png