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

一、简介

最后更新: 2021-03-11

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

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

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

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

你会学到什么

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

将您的应用连接到 Firebase

  1. 转到Android Studio / Help > Check for updates以确保您使用的是最新版本的 Android Studio 和 Firebase Assistant。
  2. 选择“工具” > “Firebase”以打开“助手”窗格。

e791bed0999db1e0.png

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

42c498d28ead2b77.png

  1. 返回 Android Studio,在“助手”窗格中,您应该会看到您的应用已连接到 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 的完整 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)

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

FirstDrawListener 的目的

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

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

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

6.测量一个片段的加载时间

测量 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)

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

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

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

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

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

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

AppStateMonitor类(它是负责为 Activity 记录屏幕跟踪的性能监控 SDK 的一部分)中汲取灵感,我们实现了ScreenTrace类(它是此 Codelab 源代码存储库的一部分)。 ScreenTrace类可以连接到 Activity 的FragmentManager的生命周期回调以捕获慢速/冻结帧。该类提供了两个公共 API:

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

通过附加这些自定义指标,片段的屏幕跟踪可以像处理活动的屏幕跟踪一样处理,并且可以与其他屏幕渲染跟踪一起显示在 Firebase 控制台的性能仪表板中。

以下是为您的 Fragment 记录屏幕跟踪的方法:

  1. 在承载片段的 Activity 中初始化ScreenTrace类。

主活动.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. 加载片段时,注册FragmentLifecycleCallbacks并覆盖onFragmentAttached()onFragmentDetached()回调。我们已经为您完成了。您需要在onFragmentAttached()回调中开始录制屏幕轨迹,并在onFragmentDetached()回调中停止录制。

主活动.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 Performance Monitoring 成功测量了 Activity 和 Fragment 的加载时间和屏幕渲染性能!

你取得了什么成就

下一步是什么

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

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

奖金

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

11.红利学习

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

cd61c1495fad7961.png