はじめに
Androidの技術にはトレンドがあります。開発現場で10年前まではよく使用されていた技術が、今ではほとんど使われなくなっている技術があります。この記事では、Androidで使われている技術(主にライブラリ)に焦点を当てて、技術の移り変わりを整理してみたいと思います(おそらく、かなり主観的な整理になっていると思いますが、それはご容赦ください)。今自分が使っている技術がトレンド的にはどの位置にあるのか、開発現場で使われている技術はどの位置にいるのかを把握することで、どこをどう強化していけばよいのか、その指針が見えてくると思います。読者様の一助になれば幸いです。
プログラミング言語
歴史:Java -> Kotlin(Dart)
まずは一番有名なのから。Androidのネイティブ開発として採用されているプログラミング言語は、AndroidがStartした当初はJava言語が使用されていました(NDKはC++やC言語で書けますが話が逸れるので省略)。現在はKotlinが主流になっています。Google I/O 2017辺りから急速にKotlinを使う流れになったでしょうか。開発現場によっては、Javaをまだ使用していたり、JavaとKotlinが混在していることもあることから、JavaとKotlinの両方を勉強しなければならないので、Android初心者の方には大変な時代かもしれません。後述するJetpack ComposeがKotlin前提の技術であることから、今後はKotlinが主力言語になることは間違いないので、学習の優先度としてはKotlinの方が上でしょう。ただ、開発現場で現在Javaを使っているのなら、それはそれで無駄ではないのでJavaもついでに学べる!位のモチベーションで学習するのもありだと思います。
また、クロスプラットフォーム開発として、Flutterに採用されているプログラミング言語はDartになります。
いろいろ言語があって大変ですが、これからネイティブ開発を始めるならKotlin、クロスプラットフォーム開発を始めるならDartを勉強することになります。
ただ、以前のオブジェクト指向の記事で紹介したように、オブジェクト指向やリファクタリングの勉強するにはKotlinやDartの本はほぼないと言ってよい状況です。名著がそろっているJavaも読めるようにしておくと、Android以外にも採用されているし、つぶしがきくかもしれません。アンタがKotlin版のオブジェクト指向の本を書けばいいじゃん、という話は・・・要望が多そうならチャレンジしてみたいと思います。
私のプログラミング言語に対する考え方は、まずはオブジェクト指向のプログラム言語をどれか1つに絞って、ある程度使いこなせるようになる(できればこの段階でデザインパターンをある程度修得する)。それから別の言語に移った時に、最初の言語とどう違うか、という差分だけを見ていくことで学習時間を削減できます。「〇〇言語の方が△△言語より優秀」といったツイートをよくみかけますが、そんなことを言っていないで、両方使えるようにした方が技術者としてレベルアップしますし、新たな言語が出てきても、すぐに修得できる、と思えるようになります。
画面の作り方(レイアウト)
歴史:Android View -> Jetpack Compose
つい数年前までAndroidはXMLにViewのレイアウトを記述するAndroid View(※)という方法で画面のレイアウトを作成していました。それがJetpack Composeが登場したことで状況が一変しつつあります。
※Android Viewは、Viewシステムと言ったり、単にViewと言ったり、いろいろな言い方がありますが、この記事ではAndroid Viewに統一します。
Android Viewのレイアウトとしてもトレンドがあります。
歴史:LinearLayout(FrameLayout)-> RelativeLayout -> ConstraintLayout
Android Viewのレイアウトの歴史は、View(レイアウト)の階層構造が増えれば増えるほど、描画効率が悪くなることとの戦いだったと思います。ConstraintLayoutになるまでは、かなりネスト構造に対する描画効率の悪さが叩かれていた印象があります。ConstraintLayoutが出た当初、いかにLinearLayoutやRelativeLayoutの処理が重いのかを説明した記事をよく見かけました。
Android Viewの描画効率の問題はAndroid View自身の設計が「継承関係」であることが起因しているようです。そして、この問題への対処として、Jetpack ComposeではデザインパターンのCompositeパターンで設計している為、ネストの問題は起こらないように設計されているそうです。(詳しいことは説明できません)
2023年現在はJetpack Composeに完全移行している開発現場はそれほど多くないと思いますので、Android Viewのレイアウトとしては、ConstraintLayoutを使用するよう心がけたらいいなと思います。
ただ、今後間違いなくJetpack Composeはトレンド中のトレンドになりますので、早めの学習をオススメします。
私はフリーランスですが、客先の開発現場でJetpack Composeの勉強会を開催するなど、Jetpack Composeの教え方について経験を積んでいるところです。いつか、書籍なり、動画なりでOUTPUTを出せたらなーと思っています。
Jetpack Composeを採用する上での前提条件
Jetpack Composeは最新の技術だと面白そうだから、自分の職場でもすぐにAndroid Viewからすぐに移行しようと思ったアナタ、少し落ち着いて考えてください。Jetpack Composeを採用する前に、いろいろな前提条件を整理してから採用した方がよいと思います。
<Jetpack Composeを導入する上で必要なこと>
- [must]コードはKotlin必須(Java不可)
- [must]アーキテクチャのViewの相手はViewModel(アーキテクチャはMVVMやClean Architecture)。ViewModelはJetpackのViewModelを主に想定している。MVPは不可。
- [should] 非同期処理はFlowを使用すると書きやすい(が必須ではない)
たまに、JavaでもJetpack Composeを使えるようにして欲しい、という要望を聞きますが、その為にはJavaで「ラムダ式による関数の引数の最後を()の外に出す」、といった関数の引数にまつわる仕様をJavaの仕様に取り込まないと、まともに書けないと思います。
アーキテクチャはMVVM系のアーキテクチャが必須です。Jetpack Compose自体は最初からアーキテクチャについても再設計し直した上で作られていますので、Presenterとのやり取りではなく、ViewModelとのやり取りが前提になります。(両者の違いについては省略させていただきます。説明して欲しいという要望があれば、コメントしていただけるとうれしいです)
非同期処理(別途章で扱う)については、この記事を書こうとする前は、Flowと合わせて使わないと使いずらそう→Jetpack Composeを採用するなら非同期処理はFlowにしよう!、みたいなことを書くつもりだったのですが、Flowでなくても大丈夫なようです(思い込みヨクナイ)。rxJava2、rxJava3、LiveData、FlowのいずれもJetpack ComposeのStateに変換する関数が用意されています。
非同期技術 | Jetpack ComposeのStateに変換する関数 |
---|---|
rxJava2 | subscribeAsState |
rxJava3 | subscribeAsState |
LiveData | observeAsState |
Flow(StateFlow) | collectAsState |
非同期
歴史:rxJava2 -> rxJava3 -> LiveData(Coroutine) -> Flow(Coroutine)
非同期処理はどんどん移り変わっている印象で、Flowは非同期処理の完成形のような印象を持っています。10年後はまた違う非同期処理が出てきているのかもしれませんが・・・
Kotlinコルーチンとの組み合わせで使用するのが最近のトレンドです。また、Flowを採用する場合は、LifeCycleライブラリも組み合わせての運用をしないと、意図しない区間で監視し続ける問題が発生したりと、やれることは増えているけど、制御も実は結構難しい、という印象です。Jetpack Composeだと最近collectAsStateWithLifecycleというStateFlowをStateに変換しつつ、LifeCycleも考慮した監視ができる関数が用意されるようになりました。Flow+LifeCycleの制御はしっかりマスターしたいところです。
UI ThreadとWork Thread間の非同期処理
歴史:自前でスレッド間通信処理を実装 -> Data Binding -> View Binding -> Jetpack Compose + Flow(State)
自前でスレッド間通信処理
Androidが出た当初、スレッド間通知とそれに伴う非同期処理は自前で
- スレッド生成
- スレッド間のキューイング処理
- デッドロックを起こさないように設計しながら、mutexでロック処理
といったことを行っていました。ワーカースレッドからメインスレッド(UIスレッド)にデータを送る場合は、Activity.runOnUiThread()で実装していました。
Data Binding
そこから大分飛びますが、Data Bindingが登場します。Google I/O 2015に発表された技術です。
Data BindingはXMLにコールバック関数を埋め込み、そのViewのイベントが発生したら、コールバック関数が呼ばれる仕組みです。Data Bindingが出た当時はすごい技術だと思いましたが、2023年12月現在では、「XMLに関数を埋め込むのがとにかくメンドクサイ」。XMLとJava/Kotlinのコードを行ったり来たりしなればならないし、XMLに書く関数に対してはコードのアシスト機能が効力を発揮しずらいです。
また、最近Data Bindingの難点だと思っているのが、Binding用のJavaの一時コードを吐き出す点です。これがどう悪さするのか原理を全て説明はできないのですが、Gitを使っていて、ブランチを切り替えると(クリーンせずに、ただのBuildコマンドによる)ビルドが通らなくなることがよくあります。リビルドすればビルドが通るようになるのですが、毎回リビルドするのは時間が掛かります。
View Binding
XMLにコールバック関数を埋め込むのを止め、Java or KotlinでBindingのコードを書くようにしたのがView Bindingです。
Jetpack Compose
Jetpack ComposeはそもそもAndroid Viewではなくなったので、同列に扱うのはどうかと思ったのですが、Jetpack Compose自体も「変更されたときに動く」仕組みが入っています。
DI(Dependency Injection)
歴史:Dagger2 -> Koin -> Dagger Hilt
Koinは外部ライブラリでDagger2とDagger HiltはGoogleのライブラリです。一時Koinが採用されたのはDagger2で書くのが結構大変だったからでしょうか。Koinもまだまだ現役の印象です。新規で採用するならHilt、今までKoinを使っていたならそのまま使うという感じでしょうか。Jetpack Composeに対する対応がどちらが早いかが、今後のシェアを決めるかもしれません。
ビルド
ビルドツール
歴史:kapt -> KSP
KSPはkaptよりKotlinコードの解析が2倍くらい早くなるらしいので、早めにKSPにマイグレーションしたいところです。ただしKSPに移行するには前提条件があります。
kaptにはData Bindingの機能が含まれていますが、KSPにはData Bindingの機能は含まれていません。その為KSPを採用するにはData BindingからView BindingまたはJetpack Comoposeへの移行が必須になります。
gradleのコード
歴史:Groovy -> KTS(Kotlin DSL)
最近(Android Studio Giraffleから?)Android Studioの新規プロジェクトのテンプレートはKTSが(Recommendとして)デフォルトになりました。
KTSを導入するメリットは、単にビルドスクリプトもKotlinで書けるだけでなく、プロパティのサジェスト機能が使えるようになるなど、ビルドスクリプトを書く上でメリットがあります。
ライブラリの管理
Bill of Materials(BOM)とGradle Version Catalogという技術があります。
ライブラリのVersion管理は大変なものです。あるライブラリのVeresionを上げたら、別のライブラリのVersionも上げなくてはならなくなり、でも上げすぎるとビルドが通らなくなり・・・みたいな状況がなくなるので、ライブラリのVersion管理がとても楽になります。
Gradle Version Catalogは、Android Studioの新規プロジェクトを作成する際、実験的機能として選択肢に出てくるようになりました。
Android Studio
つい最近(2023/11/30)、Android Studio Hedgehogが安定版としてリリースされました。最低限安定版(Stable版)にはアップグレードしていきたいところです。Jetbrain Toolboxを使用すると、複数のAndroid Studioのインストールが簡単にできます。このツールを活用して、Beta版やCanary版のAndroid Studioをお試しで使用するのもアリでしょう。特にJetpack Compose周りは毎回わくわくする機能が追加されますし、次のIganaではいよいよAIを活用した入力支援の機能が登場します(現状は既存の入力サジェストと競合して使いずらい印象ではあるけど)。
早めに新機能を活用していきたいですね。
設定値の保存
歴史:SharedPreferences -> Data Store(Prefrence Data Store or Proto Data Store)
Codelabに整理があります。
SharedPreferencesが長らく使われていましたが、この表を見るといろいろ足りていないところがあるように見えますので、Data Storeに早めにマイグレーションしたいところです。
ページング
歴史:? -> Paging2 -> Paging3
Paging2の前はどうなっていたかわかりません。Paging3になると、Flowを使用するのが前提となり、Adapterに対してnotifyDataChanged()を呼ぶような(やりたくない)データ更新処理から解放される為、早めに採用したいところです。私はPaging2 -> Paging3のマイグレーションを経験がありますが、使いこなすには結構ハードルは高い印象です(読み込み方法の最適化はいろいろ試してみないとそのアプリに対する最善が見えなかった・・・)。ただ、Model、ViewModel、Viewのどの層にも良い効果が得られる印象です。コストをかけるのに見合うリターンが得られると思います。
ViewPager
歴史:ViewPager -> ViewPager2
「ViewPager」はFragmentのライフサイクルを無視した制御(onResume()の呼ばれるタイミングがおかしい)など、かなり印象が悪いです。ViewPager2はViewPagerの問題をほとんど改善していると言われていますが、使ったことがないのでなんとも言えないです。
データベース
歴史:SQLiteを直接使用 -> Room
RoomはSQLiteを直接使用するよりコード量が減ってよいですね。Coroutineとの併用が吉。
さいごに
ここまで記事を読んでくださり、ありがとうございます。
長々と書いたわりに、あまり中身がない記事になってしまいましたが、いかがでしたでしょうか。
私の2024年の目標は
- Androidマイグレーション
- Jetpack Comopose
- オブジェクト指向
のいずれかをテーマに書籍または動画を作成することです。
今まで作成してきた技術同人誌もそうなのですが、記事に対するリアクションをみて、これなら需要がありそうだと思ったら、それをモチベーションにして作成します。
皆様の「リアクション」をお待ちしております。
来年も良いお年を!