Androidアプリ開発でViewBindingを採用する利点

現在、弊社でのAndroidアプリ開発では、画面部品の表示制御にViewBindingを利用しています。
今日はこのViewBindingについて話したいと思います。

ViewBindingとは?


ViewBindingとは、Androidアプリ開発において、javaやkotlinのコードから画面部品をコントロールするための機能です。
アプリでデータを扱うならば、画像でも文章でも最終的には画面に表示する工程が必要になりますが、Androidの開発環境の変遷とともに表示する手法も改善されてきました。
ViewBindingは最新の手法ではないですが、それ以前の画面制御の手法と比べて非常にメリットが大きいため、Androidアプリ開発では基本的にViewBindingを採用しています。
この記事では、ViewBindingまでの各画面制御を比較し、ViewBindingを採用するに至った利点を整理してみます。

画面制御機能の変遷とViewBindingの利点

ViewBindingという機能が登場する以前、Androidの画面制御にはfindViewByIdとkotlin Android Extensionsという2つの機能がありました。
このうちfindViewByIdには様々な問題がありましたが、kotlin Android Extensions、ViewBinding、と変遷する上でその多くが解決されてきました。
ここではfindViewByIdの問題点と、それらがどのように改善されていったかを見ていきたいと思います。

findViewById

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    val userName = findViewById<TextView>(R.id.user_name)
    userName.text = "山田 太郎"
}

Androidアプリ開発の最初期において、画面表示の制御には基本的にfindViewByIdを使うしかありませんでした。
しかしfindViewByIdは、以下のような問題が有りました。

問題1:型安全でない

Androidアプリの画面はXML形式のレイアウトファイルで定義されますが、その中の各部品にidを設定することで、プログラムからの制御が可能となります。
このidから画面部品を取得するのがfindViewByIdなのですが、例えばテキスト表示用の部品も画像表示用の部品も全く同じコードで取得できてしまいます。
テキストと画像では当然扱いが異なるため、間違った型として認識したままプログラムを書けば、当然バグります。

問題2:レイアウト変更時にプログラムの修正を忘れた場合、発覚が遅れる

レイアウト部品の型を変えたり、別のレイアウトへ移した場合、findViewById周りのプログラムも修正が必要になりますが、それが発覚するのはアプリをビルドして実際に変更対象の画面を表示した時です。
プログラムの修正を忘れていたとしても、アプリをビルドするまでは通ってしまいます。
この「ビルドが通ってしまう」のが大きな問題で、最悪の場合、確実にエラーで落ちてしまうアプリをGoogle Playへアップロードすることさえできてしまいます。

問題3:キャッシュされず、毎回部品を取り直してしまう

何度も更新されうる画面部品を制御する場合に、都度都度findViewByIdをすると毎回View部品を探すところから処理が行われてしまいます。

取得した部品を自分でキャッシュすれば初回以外の処理を回避できますが、今度はその部品の管理を自分で行う必要が出てきます。
上記2つに比べると軽微な問題ですが、アプリが重くなったり動作が不安定になる懸念があります。

問題4:存在しない部品も指定できてしまう

findViewByIdはidのみで参照するため、全く関係ない画面の部品をfindViewByIdすることさえできてしまいます。 画面に存在しない部品をfindViewByIdした場合はNULLが返されるため、実行する意味はありませんし、そういったコードが紛れていたとして、開発者の想定通りに動くことは無いでしょう。

kotlin Android Extensions

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    user_name.text = "山田 太郎"
}

kotlin Android Extensionsを利用すると、レイアウトXMLで定義したid名に基づいて、画面部品の変数が自動生成されます。

findViewByIdと比べたkotlin Android Extensionsの改善点、利点について見ていきます。

改善点1:型安全になった

レイアウトXMLで定義したidを元に自動で変数が定義されるため、レイアウトで設定した型情報も含めたコードが生成されます。
既に型が決まっているため、間違った型として扱ってしまってもビルドエラーになり、その時点でミスに気づくことができます。

改善点2:レイアウト変更に追従して変数も更新されるようになった

自動生成なので、レイアウトを変更すれば変数名も型も自動で変更されます。
レイアウトの方で型を変更して、万が一プログラムの修正を忘れていたとしても、ビルドエラーとなりアプリが実行されることがありません。
これにより、findViewByIdと比べてより早く正確にミスに気づけるようになったのが、この機能の大きな利点といえます。

改善点3:画面部品をキャッシュするようになった

はじめに各画面部品を取得するため、それらをキャッシュすることで、何度使い回してもパフォーマンスへの影響はなくなりました。


ただし、NULL安全でないという大きな問題が残っていました。複数のレイアウトファイルで共通のid文字列を使っている場合などに、アプリがクラッシュする危険性がありました。

ViewBinding

private lateinit var binding: ActivityMainBinding

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ActivityMainBinding.inflate(layoutInflator)
    setContentView(binding.root)
    binding.userName.text = "山田 太郎"
}

ViewBindingでは、1つのレイアウトファイルに付き1つのViewBindingクラスが生成されます。
ViewBindingはNULL安全になったため、存在しない部品が指定される問題を完全に克服しました。また複数のレイアウトファイルでidが重複していても、どのレイアウトファイルに属する部品なのかが明確になり、開発する上で読みやすくもなっています。
さらに、kotlin Android Extensionsで改善された型安全・レイアウト変更への追従・画面部品のキャッシュの利点も引き継いでいます。
findViewByIdで挙げた4つの問題はViewBindingで全て解消されたため、開発効率や品質を高める上で高い効果を発揮していると感じています。