Viewのネストはどのくらい遅いのか ー 12階層のレイアウトを5階層にして検証してみた

  • 105
    いいね
  • 2
    コメント
この記事は最終更新日から1年以上が経過しています。

Android Developers > Best Practices for Performance > Performance Tips > Improving Layout Performance の中に、Optimizing Layout Hierarchiesという項目があります。

簡単に要約すると

  • Viewのネストを深くするな。narrow and deepよりshallow and wideなレイアウトであれ。
  • LinearLayoutのlayout_weightは重い。描画時に2回計算してしまう。(measure twice)

ということらしいです。

コードレビューの時も結構意識して、極力RelativeLayoutを使ってレイアウトを構成するようにしていたのですが、ふと「実際実機で触るとどれだけ違うんだろう」と疑問に思ったので検証してみることにしました。

検証アプリ

検証用に、Twitter公式アプリと同じレイアウトのTwitterViewerを作ってみることにしました。
リポジトリはこちら

Skitch_Image.png

余談ですが、Twitter公式AndroidSDKとして2014/10に発表されたTwitter Kitを使ってみました。また、appcompat v21を使って、少しマテリアルデザインに対応しています。

Skitch_Image.png

メニューからレイアウトを変更できます。見た目はかわりませんが、ListViewのアイテムのxmlが変わります。

  1. 浅いレイアウト
    RelativeLayoutを使って最小限のネストに抑えたレイアウトです。

  2. 深いレイアウト
    LinearLayout + layout_weight使いまくりでネストを極力深くした糞みたいなレイアウトです。

  3. 深くて画面描画の多いレイアウト
    2にbackgroundを指定しまくって描画を多くしたレイアウトです。端末のGPUオーバードローデバッグを有効にすると、画面が真っ赤になります。

1と3で使い勝手を比較してみたら、1の方が断然ぬるぬるに動くはずだよね、という仮説があったわけです。

LinearLayout と RelativeLayout

浅いレイアウトにするためには、LinearLayoutをやめてRelativeLayoutを使えという話は有名ですが、実際どう違うのかを簡単に説明します。
例えばこのレイアウト。大きく見ると2ペインに分かれています。

Skitch_Image.png

これをLinearLayout と RelativeLayoutで書くと、こんな感じになります。

LinearLayoutの場合

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <ImageView
        android:id="@+id/img_user"
        style="@style/UserImage" />

    <LinearLayout
        android:layout_width="0dp"
        android:layout_marginLeft="@dimen/spacing_large"
        android:layout_marginStart="@dimen/spacing_large"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:orientation="vertical">

        <LinearLayout
            android:id="@+id/container_user_name"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/txt_user_name"
                style="@style/TextUserName"
                android:layout_width="wrap_content"               
                android:layout_height="wrap_content" />

            <TextView
                android:id="@+id/txt_user_screen_name"
                style="@style/TextSub"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="@dimen/spacing_small"
                android:layout_marginStart="@dimen/spacing_small" />

            <!-- 略 -->

        </LinearLayout>

    </LinearLayout>

</LinearLayout>

RelativeLayoutの場合

<ImageView
    android:id="@+id/img_user"
    style="@style/UserImage"
    android:layout_below="@id/txt_retweeted_msg"
    android:layout_marginEnd="@dimen/spacing_large"
    android:layout_marginRight="@dimen/spacing_large" />

<TextView
    android:id="@+id/txt_user_name"
    style="@style/TextUserName"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

<TextView
    android:id="@+id/txt_user_screen_name"
    style="@style/TextSub"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="@dimen/spacing_small"
    android:layout_marginStart="@dimen/spacing_small"
    android:layout_toEndOf="@id/txt_user_name"
    android:layout_toRightOf="@id/txt_user_name" />

<!-- 略 -->

わかりやすくするために少し極端に書いていますが、RelativeLayoutのlayout_abovelayout_toEndOfなどを使うとLinearLayoutよりネストを少なく書くことができます。

Hierarchy Viewerでレイアウトの階層構造を確認

実際に作ったレイアウトを、Hierarchy Viewerで確認します。Hierachy Viewerの使い方はこちらを参考にどうぞ。

浅いレイアウト

Android_Device_Monitor.png

右上のTree Overviewを見ると、浅く広いレイアウトになっていることがわかります。
Layoutに10msかかっているようです。

深いレイアウト

Android_Device_Monitor.png

階層が深いレイアウトになっていることがわかります。
Layoutに29msかかっているようです。
layout_weight使いまくってるんですが、遅くなるという話だったMeasureは0.2msしかかかっていませんでした。意外です。

ただ、LinearLayout + layout_weightのところはやはり重いようで、赤いアラートが表示されていました。

Android_Device_Monitor.png

実機で確認してみる

自分の端末Android4.3のGalaxy Note3で確認してみましたが、結論から言うと 違いがまったくわかりませんでした。
思いっきりスクロールしてみたり、android:hardwareAccelerated="true"のオプションを外してみたりしましたが、気になるほどの劣化はありませんでした。
まぁもともと29msしかかかっていないのが10msになっただけなので当然と言えば当然なのですが。

ただ、Android2.3のGalaxyS端末で見てみたところ、少し違いは感じました。深いレイアウトで作った方は、スクロールする時にカクカク感が強かったです。低スペック端末であれば違いが体感できるようです。

まとめ

  • レイアウトのネストは浅い方がいい、というのは正しい。
  • ただし、最近の高スペック端末だと体感ではほとんど違いがない。
  • 低スペック端末だと若干スクロール時のカクつきに影響する。
  • 基本的にRelativeLayoutでできるところはRelativeLayoutで作るべきだが、LinearLayoutで書いた方がスッキリする場合はあまり神経質になる必要はないかも。
この投稿は Android Advent Calendar 201415日目の記事です。