【问题标题】:Why does inflating an Android layout take so long?为什么膨胀一个 Android 布局需要这么长时间?
【发布时间】:2018-12-01 07:19:15
【问题描述】:

我在 Android 中有一个对话框。打开时有明显的延迟。这是我所做的调查:

  1. 使用 System.currentTime 的粗略计时,我的 onCreateDialog 方法在正常的一天需要 500-700 毫秒,在正常的一天需要 150 毫秒。

  2. 我实现了自己的 LayoutInflator.Factory。我的工厂什么都不做。它只是返回 null 以让默认工厂完成工作。然而,它会写出自上次调用以来经过的时间。这样,我可以打印出布局 xml 中的每个标签需要多长时间才能膨胀。非常令人惊讶的是,每个元素大约需要 20-70 毫秒才能膨胀!

  3. 我分析了应用程序。看来确实有很多时间花在视图的构造函数或者LayoutParams上。

  4. 为了粗略检查,我用 System.currentTime 测量了调用 TextView 上的构造函数的时间。事实证明,在我强大的 Alienware PC 上的模拟器中,实例化一个 TextView 对象需要 20-70 毫秒!似乎有些不对劲,可能需要这么长时间。

作为参考,布局膨胀、测量和渲染之间存在差异。我现在只关心通货膨胀的表现。

这是我实现 LayoutInflator.Factory 以进行测量的方式:

        LayoutInflater inflater = LayoutInflater.from(getActivity());
    LayoutInflater inflater2 = inflater.cloneInContext(getActivity());
    inflater2.setFactory(new LayoutInflater.Factory() {

        long inner = System.currentTimeMillis();

        @Override
        public View onCreateView(String name, Context context, AttributeSet attrs) {
            Log.d(LOG_TAG, "onCreateView: Called factory for " + name + " took " + (System.currentTimeMillis() - inner));
            inner = System.currentTimeMillis();
            return null;
        }
    });

这是输出:

Called factory for TextView took 34
Called factory for TextView took 30
Called factory for android.support.constraint.Guideline took 76
...

这是我如何测量实例化 TextView 和 LayoutParams 对象的时间。

    Log.d(LOG_TAG, "onCreateDialog: 10 " + (System.currentTimeMillis() - last));  last = System.currentTimeMillis();

    new TextView(getActivity());
    Log.d(LOG_TAG, "onCreateDialog: 11 " + (System.currentTimeMillis() - last));  last = System.currentTimeMillis();

    new ConstraintLayout(getActivity().getApplicationContext());
    Log.d(LOG_TAG, "onCreateDialog: 12 " + (System.currentTimeMillis() - last));  last = System.currentTimeMillis();

这是我的布局,但应用程序中的所有布局似乎都受到了缓慢的影响:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

<android.support.constraint.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/remote_message_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/remote_dialog_message"
        android:visibility="gone"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <TextView
        android:id="@+id/building_name_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="8dp"
        android:textColor="#000"
        android:textSize="32sp"
        android:textStyle="bold"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/remote_message_text_view"
        tools:text="Bakery"/>

    <ImageView
        android:id="@+id/building_icon_image_view"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:adjustViewBounds="true"
        android:contentDescription="@string/building_icon_content_description"
        app:layout_constraintEnd_toEndOf="@+id/right_guide_line"
        app:layout_constraintStart_toStartOf="@+id/left_guide_line"
        app:layout_constraintTop_toBottomOf="@+id/building_name_text_view"
        tools:src="@drawable/bakery"/>

    <TextView
        android:id="@+id/action_heading_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingEnd="8dp"
        android:paddingStart="8dp"
        android:paddingTop="8dp"
        android:text="@string/actions_heading"
        android:textAllCaps="true"
        android:textColor="#000"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/building_icon_image_view"/>

    <android.support.v7.widget.GridLayout
        android:id="@+id/action_grid_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingEnd="8dp"
        android:paddingStart="8dp"
        app:columnCount="3"
        app:layout_constraintTop_toBottomOf="@+id/action_heading_text_view"
        tools:layout_height="100dp"/>

    <TextView
        android:id="@+id/production_rules_heading_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingEnd="8dp"
        android:paddingStart="8dp"
        android:paddingTop="8dp"
        android:text="@string/production_rules_heading"
        android:textAllCaps="true"
        android:textColor="#000"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/action_grid_layout"/>

    <TextView
        android:id="@+id/production_rules_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingEnd="8dp"
        android:paddingStart="8dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/production_rules_heading_text_view"
        tools:text="1 flour creates 1 bread."/>

    <TextView
        android:id="@+id/production_speed_heading_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingEnd="8dp"
        android:paddingStart="8dp"
        android:paddingTop="8dp"
        android:text="@string/production_speed_heading"
        android:textAllCaps="true"
        android:textColor="#000"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/production_rules_text_view"/>

    <TextView
        android:id="@+id/production_speed_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingEnd="8dp"
        android:paddingStart="8dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/production_speed_heading_text_view"
        tools:text="1 peasant produces 1 unit per 60 minutes.\n(Add a peasant to cut the time to 30 minutes.)"/>

    <TextView
        android:id="@+id/countdown_heading_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingEnd="8dp"
        android:paddingStart="8dp"
        android:paddingTop="8dp"
        android:text="@string/next_unit_count_down_heading"
        android:textAllCaps="true"
        android:textColor="#000"
        android:visibility="gone"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/production_speed_text_view"/>

    <TextView
        android:id="@+id/countdown_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="8dp"
        android:textColor="#000"
        android:textSize="32sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/countdown_heading_text_view"
        tools:text="24 : 60 : 60"/>

    <android.support.constraint.Guideline
        android:id="@+id/left_guide_line"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:orientation="vertical"
        app:layout_constraintGuide_begin="10dp"
        app:layout_constraintGuide_percent=".33"/>

    <android.support.constraint.Guideline
        android:id="@+id/right_guide_line"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:orientation="vertical"
        app:layout_constraintGuide_percent=".66"/>

</android.support.constraint.ConstraintLayout>

为什么膨胀布局如此缓慢。如何加快速度?

【问题讨论】:

  • “我用 System.currentTime 测量了在 TextView 上调用构造函数的时间。结果在我的强大 Alienware PC 上的模拟器中,实例化一个 TextView 对象需要 20-70 毫秒!” -- 我无法重现你的结果。 first TextView 可能需要一点时间,因为有些东西可能是惰性初始化的。在模拟器和硬件上,第二个和后续的速度很快(亚毫秒)。请注意,我使用nanoTime() 进行计时。您的开发机器或模拟器可能存在问题。
  • 当我在 Pixel 设备上重复测量时,LayoutInflator 中的每个步骤需要 5-8 毫秒。总布局膨胀仍然是 102 毫秒,对于简单的布局来说似乎很多。膨胀 TextView 和 LayoutParams 大约需要 3-8 毫秒。我同意这些事情应该花费亚毫秒,整个布局可能需要 0-10 毫秒。一些奇怪的事情正在发生。我已经重新启动了模拟器,没有改变。
  • 确保您已禁用 Instant Run,因为它可能会干扰您的性能分析。
  • 禁用即时运行是一个很好的提示。它更快,但扩展布局仍然需要 100 多毫秒。

标签: android performance android-layout


【解决方案1】:

创建视图只是 LayoutInflater 工作的一小部分。您没有初始化它们,没有将它们添加到它们的父级,没有在它们上设置属性,没有创建 LayoutParams,没有进行布局或测量传递。你甚至没有解析 xml。当你做 10% 的工作时,它需要 10% 的时间。你的比较完全没用。

【讨论】:

  • 当我用 Android Studio profiler 测量时,大部分时间都花在了视图构造函数和 LayoutParams 构造函数上,而不是 10% 的时间。同样简单地调用 TextView 或 LayoutParams 上的构造函数所用的毫秒数与 LayoutInflator 处理一个视图所用的毫秒数大致相同(请参阅我对 Factory 的实验)。
  • 您正在使用 1 参数构造函数。 inflated 解析 props 并使用 3。甚至你的构造函数也做了 10% 的工作
猜你喜欢
  • 2011-08-27
  • 2011-12-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-09
  • 2021-07-26
  • 1970-01-01
相关资源
最近更新 更多