先日、宣言型UIへの移行方法に関する記事を書きました。
Androidアプリ開発も、「Jetpack Compose」で最近のトレンドである宣言的実装に加わりつつあります。
Androidスマホで動作するアプリを、ネイティブAndroidアプリと呼びます。
これを制作する際に、もっと効率化しようと生み出されたのが「Jetpack Compose」です。
Googleによって制作されたこの新しい宣言型UIツールキットが、急速に普及しています。
実際、昨年の「Android Dev Summit」で発表されたように、Androidアプリで人気上位1,000のアプリのうち、160が既にJetpack Composeを使用しています。
従来のXML Viewとは対照的に、Jetpack Composeでは、UIがどのように見え、どのように振る舞うべきかを記述する「Composable Function」を使用して、UIを構築できることが特徴です。
Jetpack Composeを使用する主な利点は、より簡潔で理解しやすいUIコードを書くことができることです。
これは、保守性の向上と開発期間の短縮につながります。
Jetpack Composeを使用する主なデメリットは、まだ生まれて間もない新しいライブラリであるため、使用できるエコシステム(OSバージョン)が限られていることです。
つまり、従来のXML Viewによる開発で使えていたライブラリやツール、リソースの数が、Jetpack Composeだと比較的少ないということです。
それでも、Jetpack Composeを学ぶことは、大きな意味を持ち、挑戦に値すると私たちは信じています。
ここでは、あなたが始めるにあたり、私たちが役に立つと思ったいくつかのヒントを紹介します。
Jetpack Composeの使用開始方法
Jetpack Composeで作業するための推奨IDEは、Android Studioです。
Android Studioをダウンロードしインストールすると、新しいプロジェクトを作成するオプションが表示されます。
新しいJetpack Composeアプリケーションを作成するには、空のComposeアクティビティ(Material v2を使用)、または空のComposeアクティビティ(Material3)(昨年の時点でバージョン1.0であるMaterial v3を使用)のいずれかを選択する必要があります。
このスクリーンショットの右上に、両方のオプションが表示されています。
これは、最も手軽にJetpack Composeを始める方法であるといえます。少し試したいという場合におすすめです。
既存のAndroidアプリケーションにJetpack Composeを有効にしたい場合は、次のように修正を行いましょう。
1. アプリのbuild.gradleファイルに、以下のビルド設定を追加します。
2. Compose BOM(部品表)とComposeの依存関係のサブセットを依存関係に追加します。
Jetpack ComposeのUIはどのように構築されているのでしょうか?
Jetpack Composeでは、ビュー(レイアウト要素)の階層を定義するためにComposablesを使用しています。
そして追加されたComposablesに視覚的な外観や動作の変更を適用するためにmodifierを使用します。
コンポーザブルファンクション
Composable関数(または単にComposableといいます)は、「@Composable」でアノテーションするだけで、Composable関数の中に入れ子構造でUIを定義することができます。
UIを定義するために、Composableの階層を返す普通のKotlin関数でもあります。
ここでは、以下のUI要素を定義した簡単なComposableを見てみましょう。
- ユーザーの写真
- ユーザーの名前
- 電話番号
Row Composableは、レイアウト用のComposableで、要素の配置の仕方を定義します。その中に定義されている要素(ここではImageとColumn)を横並びに表示させることを意味します。
Image Composableは、ユーザー画像を描画するための要素です。
次に、Column Composableは、Row Composableと同じレイアウト用のComposableです。入れ子になっている子要素を上から下に向かって縦方向に配置します。
Columnの子には2つのText要素がありますね。ここでは、ユーザー名と電話番号が描画されることを意味しています。
モディファイア
Modifierは、追加されたComposableの外観や動作を変更するために使用されます。
Modifierは、Composableのサイズ(幅、高さ)、パディング、背景などのUI要素を変更したいときに使用します。
また、修飾子は連続して書き連ねることができるため、1行で複数のビジュアルプロパティを変更することができます。
以下の例では、ContactRowのパディングと最大幅を設定しています。
Jetpack Composeでデータを扱う方法
Jetpack Composeアプリ内でデータを保持する方法は3つあります。MutableState、LiveData、そしてStateFlowです。
ミュータントステート
Jetpack Composeでは、オブジェクトをメモリに格納するremember APIと、状態変数を宣言するmutableStateOfを使用することで、状態管理を実現することができます。変更可能(代入可能)なオブジェクトと、変更不可能(代入不可)なオブジェクトの両方を保存することができます。
mutableStateOfは、観測可能なMutableState<T>を作成します。
値を変更すると、それを読み取るすべてのComposable関数の再コンポジション(再描画)が実行されます。
MutableStateオブジェクトを宣言するには、以下の3つの方法があります。
- val mutableState = remember { mutableStateOf(0) }
- var value by remember { mutableStateOf(false) }
- val (value, setValue) = remember { mutableStateOf(“Hello, Compose!”) }
ライブデータ
LiveDataは、与えられたライフサイクル内で観察できるデータホルダークラスです。
つまり、Activity、Fragment、ServiceなどのAndroidアプリコンポーネントのライフサイクルと連携することができます。
これによってLiveDataは、ライフサイクルがアクティブな状態にあるオブザーバのみを更新し、アプリ内でメモリ・リークが発生しないようにすることができます。
LiveDataを使った作業の例を見てみましょう。
1. とあるデータを保持するためにLiveDataクラスのインスタンスを作成する必要がありました。これは通常ViewModelクラス内で行われます(ある時点で値を更新したい場合はMutableLiveDataを使用します)。
2. collectAsStateメソッドを呼び出すことで、Composable内の値を取得することができます。
3. userNameの値を更新するには(これも通常はViewModelで行います)、そのvalueプロパティに新しい値を設定する関数を作成します。
4. Composeファイルでは、新しい関数をviewModel.updateUserName(“…”)として使用することになります。
Jetpack Composeのベストプラクティス
公式のベストプラクティス文書とは別に、あなたのコードベースをより安全で働きやすくするためのヒントをいくつか紹介します。
コード編成
プロジェクト構成についてどうあるべきか、各開発者・各組織によって意見は異なります。
「正しい」「間違っている」ということはありません。
さすがに全ファイルを1つのディレクトリに置くのは間違っていますが😅。ここでは、プロジェクトが成長するにつれて変更し、進化させることができるプロジェクト構造の例を示します。
「神」ファイルを作らないようにする
1つのファイルに大量の機能を盛り込んだクラスを通称「神ファイル(神クラス)」といいます。
プロジェクト内で全てを司る唯一無二の存在だから、ということから神と呼ばれています。
名前は荘厳ですが、これは典型的なアンチパターンです。
そのファイルに関連するすべてのコードが含まれているわけです。
UI、ドメイン、ビジネスロジック、ユーティリティ関数などなど…すべてを1つのファイルにまとめるのは簡単かもしれませんが、機能を追加するにつれて保守することが難しくなっていきます。
この問題は、Jetpack Composeアプリで適切なアーキテクチャを使用することで解決できます。
使用できるアーキテクチャは複数あり、すべて独自の長所と短所があります。Jetpack Composeで最も一般的なものはMVVM(Model-View-ViewModel)で、Jetpack ComposeがファーストクラスのViewModelの実装を持っているアーキテクチャです。
MVVMに忠実であること
これまでの例でお分かりのように、Jetpack ComposeはファーストクラスのViewModelを実装しています。
MVVM(Model-View-ViewModel)とは、ビジネスロジックをUIから分離するように構成されたソフトウェアのデザインパターンです。
つまり、UI側では状態更新の処理などを実行せず、ユーザーアクション(タップ操作など)を送ることでViewModelにそれを実行させる考え方が基盤となっています。
例題でそれを探ってみましょう。
先ほどのMutableStateFlowの例では簡略化しすぎましたが、実際のプロジェクトでは、ViewModelからMutableStateFlowを公開することはなく、StateFlowを公開するだけにするのが一般的です。
そのためには、privateなMutableStateFlow変数と、asStateFlow()メソッドを呼び出してMutableなフローを返すpublicなStateFlow変数を定義しなければなりません。
(オブジェクト指向でいう、カプセル化です)
この簡単な変更で、UIが状態を変更できないようにしています。
それでは、どのようにして状態を変更するのでしょうか?
それは、ViewModelから状態変更専用の関数を呼び出せるようにすることです。
これでUIは、観察できる不変のStateFlowと、その値を更新する関数を持つことになります。
ビジネスロジックはViewModelの中にあり、Composableは状態変化に適宜対応し、ユーザーアクションをViewModelに送信することだけを担います。
これで処理を分割し、それぞれの役割が明確になりました。
1000のフローを作らない
というわけで、StateFlowの実装方法をご紹介しました。UIで必要なすべてのState変数に対して同じことを繰り返すのでしょうか…?もちろんそれは避けた方がいいです。
そのために、すべてのState変数を保持するデータクラスを作成し、それを使用する1つのフローを作成することができます。
それでは、これを次の例でご紹介していきます。
例えば、ユーザーの電話番号、電子メール、住所を保持したいとします。
HomeScreenStateというデータクラスを作り、それらの値をすべて保持することができるようにします。
そして、ViewModelをリファクタリングして、Stringの代わりに新しいHomeScreenStateを使用するようにします。
そして、viewModel.uiState.userNameによって、すべての値をComposableで使用することができます。
もし、これらの値をすべて更新できるようにしたいのであれば、ViewModelにそれぞれの値を表す関数を作成します。
本番でのエラーやパフォーマンスにも目を光らせよう
Jetpack Composeに慣れてきたら、エラーとパフォーマンスの監視ツールは、学習コストを軽減させ、アプリにバグがないことを確認するために本当に役立ちます。
Jetpack Composeは開発者が楽に開発できるようにするために、多くの処理を担います。
宣言型ツールキットとして、開発者はUIを記述するために少ないコードを書き、Jetpack Composeは残りの部分を補完します。
しかし、それは多くのコードを抽象化し、エラーを特定することを難しくしていることと同じなのです。
Sentryは、より良いJetpack Composeアプリを構築するのに役立つ、すぐに使えるインテグレーションを提供しています。
この統合は、トランザクションとパンくずを使用して、トラブルシューティングの時間を短縮するための正確なコンテキストを提供します。
問題が発生した理由、問題を引き起こした正確なコード行、実行したデバイスのハードウェアとソフトウェアの情報など、多くのコンテキストで、アプリが本番で経験しているすべての問題やクラッシュを監視することができます。
結論
ここまでで多くのことをご紹介してきたので、簡単に振り返ってみます。
Jetpack Composeの新規プロジェクトの作成方法、Jetpack ComposeはComposablesとModifiersを使用してView階層を定義し、ビジュアル変更を適用することを学びました。
Jetpack Composeのデータは、MutableState、LiveData、StateFlowのいずれかで処理することができ、値が変更されるとそれを観察するComposableが再描画することで、UIを動的に対応できます。
また、プロジェクト構造を整理する方法、保守性の高いComposableやViewModelの書き方についても学びました。
比較的新しい技術とはいえ、Jetpack Composeのエコシステムは着実に成長しています。
Jetpack Composeアプリを簡単に作成できるライブラリが、今後さらに多く登場していくことでしょう。
Lyft、Twitter、Airbnb、Square、Reddit、Firefoxなどの企業が信頼を置いているため、より多くの開発者がそれに追随し、Jetpack Compose用のアプリやライブラリ、リソースを作成することになると予想されます。
Sentryは、アプリケーションコードの健全性を監視するために不可欠です。
エラートラッキングからパフォーマンスモニタリングまで、開発者は、フロントエンドからバックエンドまで、アプリケーションをより明確に把握し、より迅速に解決し、継続的に学習することができます。
Sentryは、世界中の350万人以上の開発者と85,000以上の組織に愛され、Disney、Peloton、Cloudflare、Eventbrite、Slack、Supercell、Rockstar Gamesといった世界で最も有名な企業の多くにコードレベルの観測機能を提供しています。
毎月、インターネット上で最も人気のある製品から数十億の例外を処理しています。
IchizokuはSentryと提携し、日本でSentry製品の導入支援、テクニカルサポート、ベストプラクティスの共有を行なっています。Ichizokuが提供するSentryの日本語サイトについてはこちらをご覧ください。またご導入についての相談はこちらのフォームからお気軽にお問い合わせください。