📝

個人的 Android アプリ開発ガイドライン

2021/10/20に公開

ある程度規模のある Android アプリを開発する際に自分ならこう選定するみたいな技術要件的なものを羅列した個人メモみたいな感じのアレです。
クロスプラットフォームにいい思い出がないのですが、Flutter はとても優秀なフレームワークだと思っていますし尊敬しています。

フレームワーク

推奨

Android アプリを開発するには主に以下の手段が挙げられます。

モバイルアプリエンジニア人口の中でも Android アプリエンジニアは少ないです。色々理由はあると思いますが現状の日本のモバイルアプリの OS シェア的に iOS が優勢な事も大きく関係していそうかなと思っています。そのためわざわざ Android アプリを開発するなら iOS と一緒に開発できる様に Flutter を選択すると言うのは1つの選択肢として有力です。

しかし Flutter や ReactNative を選択するのであればクロスプラットフォームとして両 OS 一緒に開発・管理するべきで、Android だけ・iOS だけを Flutter などで開発する事は正直ないのかなと思っています。

クロスプラットフォーム最大のメリットはドメインロジック共通化による保守・運用のしやすさや実装期間の短縮、各プラットフォーム間のコミュニケーションコストの削減等があると思います。そこでわざわざどっちかの OS だけを Flutter で開発すると言うのは結局管理が別々になり上のメリットが何も傍受できていない状態です。

そして Android においてはメモリ使用率やアプリサイズなどにおいて余計なフレームワークが少ない分ネイティブの方がパフォーマンスが高いです。(ホットリロードなど Flutter の方がビルド時間が気にならなこともありますが)

例えば Android だけを Flutter で実装するとなると、せっかくのクロスプラットフォームのメリットを捨てた上でアプリのパフォーマンスを落とすと言う結果になり実装においても ユーザへの価値提供 の観点からしても Android ネイティブに劣ります。

Flutter でのレイアウトも Jetpack Compose がリリースされたこともあり、同じ宣言的 UI であっても複雑な画面になるとステート管理や画面構築に関する記述など Android ネイティブの方が楽になることもあります。(そもそも WebView な React Native はパフォーマンスが低いため Re)

いろんな会社の事情を聞いてきて iOS アプリエンジニアの人員確保はできるけど Android アプリエンジニアはなかなか集まらないと言うのが今のモバイルアプリ開発のマーケット状況になっている様です。Android 実装に限定して話すと、確かに Flutter に比べて Android ネイティブの学習コストは高いと思います。ですが Jetpack のおかげで最近は大変なライフサイクルの対応だったり Compose で宣言的 UI で画面実装できたりと Flutter との学習コストの差は縮まってきています。

また、Flutter だと plugin はあるにせよネイティブコンポーネントを触る時に bridge する必要があったりするのでフレームワークが対応してくれるのを待つ必要があり迅速な対応につながらず小回りが効かない事もあります。結局 Flutter をするにも ReactNative をするにも Android ネイティブの知識は必要になりますし、そもそもフレームワークのメンテナンスが途切れたらそこでそのアプリの寿命となってしまいます。(Flutter は Google がメンテしてるのでそうそうないと思いますが...)

なので Only Android Flutter をするならその勉強時間 + α でネイティブ Android の勉強をする方がより実装時の小回りが効いてきたりより高いパフォーマンスのアプリをユーザに提供でき長期的に見ると Android アプリが市場から衰退するまでは困ることがありません。最近は Google の Codelab も充実してきており、初心者が入りやすくなってきています。また、Kotlin は JVM 上で動きますし最悪 Java で書けるので多くのソフトウェアエンジニアが Android のコンテキストについて少し勉強するだけで参入できると思います。(特に新卒研修や OJT などで Java から入るエンジニアは多いと思います。)

上記は Flutter と Android ネイティブを比較していますが、WebView 上に描画される ReactNative はパフォーマンスの観点から Only Android な開発をするのは悪手だと考えているので例に挙げていません。

だから僕は Android だけを開発するのであれば Android ネイティブで開発する方がいい と思っています。

ここから以下は Kotlin Android を前提として記載していきます。

SdkVersion

  • minSdkVersion
    サービスへのアクセスが 5% 以下になった API レベルはサポートを終了します。Android は毎年新しいバージョンがリリースされていますが、サポートするOSが多くなると考慮する事項が増加し開発効率が低下するためです。

  • targetSdkVersion
    なるべく最新の API レベルに追従します。毎年リリース時に必要な target の条件が更新されるため、直前にあたふたして間に合わなくなるリスクをなくすためなるべく早く対応すると言う意味でも最新の API レベルに対応しておきたいのが理由です。

  • compileSdkVersion
    常に最新の API レベルに追従します。最新の API レベルのコンポーネントに触れなければいけないケースは出てきます。

アーキテクチャ

推奨

  • MVVM

Google が MVVM + Repository パターンを推奨しているので Jetpack ライブラリとの親和性が高く実装コストが下がると考えます。また、そのまま作成したアーキテクチャがマルチモジュール構成に落とし込みやすいので、マルチモジュール化でビルド時間の短縮や管理コストの削減等代表的な GUI アーキテクチャパターンの MVC や MVP よりメリットが多くあります。

非推奨

  • MVC

Android アプリ開発において MVC は View と Controller の概念が曖昧になるので FatActivity につながりやすくテストが書きにくく管理も大変になるため採用するべきではないと考えます。(参考)

ちょっと踏み込んで

  • MVVM + UseCase + Repository な 3層 Layred Architecture

ある程度規模のあるアプリだと UseCase を採用してレイヤーを1つ増やします。詳しくはこちらのテックブログの前半をご覧ください

レイアウト

推奨

これからアプリを作成するのであれば Jetpack Compose で書くと思います。(現時点で新規アプリを立ち上げていないので憶測です...)

従来の XMLは View とコード間の通信に DataBinding 等の機構が必要でしたが、宣言的 UI である Compose ではその必要がなく Kotlin のメリットであるシンプルかつモダンな記述でレイアウトを表現でき、差分更新などにより XML よりパフォーマンスの向上が見込めます。

まだリリースされたばかりで WevView など従来の View が必要となるケースも存在しますが、今後間違いなくスタンダードな手法となるため今から作成する場合は先に対応すると言う意味でも Jetpack Compose を採用することをオススメします。

非同期処理

推奨

Kotlin を採用しているのであれば Coroutines を採用するのがいいと考えます。

Coroutines はサードパーティライブラリではなく Kotlin の言語機能なので運用がしやすいと言うのが一番の理由です。また、Java 時代では RxJava が採用されていたと思いますが膨大かつ複雑なオペレータ群と異なり Coroutines はシンプルな記述でラーニングコストが低いことも理由として挙げられます。そして、最近は Jetpack ライブラリが Coroutines をデフォルトでサポートしており親和性が高いので実装コストが下がります。

Java を採用しているプロジェクトであれば Coroutines は採用できないため RxJava が AsyncTaskLoader などに比べると比較的コストが低いためオススメします。

DI

推奨

依存関係が複雑にコードに入り込んでくる前に DI を導入してリユーザブルかつテスタブルな綺麗なコードにしておきたいです。そのために開発初期から DI ツールを導入することを推奨します。

Dagger Hilt は Dagger による DI のラーニングコストを増加させる主な原因となっていた複雑な設定をライブラリがサポートしてくれたことで DI を取り入れやすくなり、Android と DI のライフサイクルを合わせるためのボイラープレートが必要なくなりアプリエンジニアが DI に注力できる様になっています。ただし Dynamic Feature Module を採用している場合は Hilt による annotation-processor がうまく動かず DI できないので従来の Dagger を部分的に使用する必要があります。

Json シリアライザ

推奨

Gson や Moshi などと比べてリフレクションではないのでシリアライズのパフォーマンスが高いです。また、フル Kotlin なシリアライザなのもポイントです。

非推奨

Kotlin を採用している場合シリアライザに Gson を採用することはオススメしません。Kotlin では Java と異なり null がハッキリと区別されています。そのため、null が入り得ない場合は non-null で定義します。ですが Gson で json からパースしたときに non-null なプロパティの key がない場合例外が throw されてほしいところですがそうはならず、non-null なプロパティであるのに null が代入されます。nullability な前提が根底から覆ることになり非常に危険です。なのでもし検討しているのであれば Moshi を採用することをオススメします。

HTTP クライアント

推奨

使用実績も多く現在の Android アプリにおいての REST HTTP 通信のデファクトスタンダードであると考えています。interface と annotation によるシンプルかつ簡単な Web API 通信の実装が非常に強力です。また、そのプロジェクトに合わせてパーサーやコールアダプターを変えられるところもポイントです。一応 Jake 神の kotlin serialization のコンバータ もありますが Experimental です。

最近は GraphQL を採用する場合も増えてきているのでそのときは Apollo を採用します。現状ほかに選択肢がないと思います。schema.json とクエリからコードを自動生成してくれたりキャッシュ機構があったりするので非常に便利です。

画像ローダー

推奨

フル Kotlin で記述されており Glide や Picasso と同等の機能があるがサイズも軽く、Coroutines などもサポートされており本稿の推奨環境において非常に親和性が高いのがポイントです。

CI

推奨

Github (特に GHE) を使っている場合は Github Actions がベストでしょう。なんといっても利用料金が Github の利用に含まれているので Bitrise 等に比べてマネーコストが追加でかかりません。また、Github の様々なアクションをフックして CI を走らせることができるのも非常に魅力的です。Github 内にあるサービスなのでわざわざウィンドウを切り替えて CI の状態を見に行かなくてもタブから確認できるのも利用しやすいです。FastlaneDanger などを併用します。

<編集中>

Discussion