Webエンジニアが半年Androidアプリ開発をやってみて感じたこと

こんにちは,アプリケーションエンジニアの id:laminne です. 現在入社二年目で,学生時代は主にWebバックエンドをTypeScriptやGoで書いてきました.半年ほど前から,それまで未経験だったAndroidアプリ開発にも取り組んでいます. 具体的には,2025年11月にリニューアルされたはてなブログのAndroidアプリ の開発を担当しています. Android専任というわけではなく,それまで行ってきたきたWeb周りの開発も継続して取り組んでいます.

普段はiOSを使っており,ユーザーとしての知識はAndroid 10くらいで止まっていました.Android 12以降でデザインシステムがMaterial 3に一新されたのもあって,エミュレータを起動したときに最新のAndroidはこんな見た目/操作感なのかとびっくりした覚えがあります.

本記事では,Webアプリケーション開発を経験してきた私が感じた,AndroidアプリのWebとの違いや,新しく触れた記述について紹介しようと思います.

初めて触れた技術

Kotlin

Androidアプリ開発と同時にKotlinを触り始めましたが,そこまで学習難易度は高くありませんでした.文法が直感的で,これまで触れてきた言語と大きく変わっているところが少なかったことと,書き始める時に読んだ チュートリアル が丁寧で読みやすかったことですんなりと理解できたと思っています.

Kotlinそのものを書いてみた所感は過去に Kotlinを書き始めた - /dev/sdr2 という記事にまとめているので,ここではAndroid開発で特につまずいた点に絞って書くことにします.

Kotlin FlowやCoroutineなど,Kotlinの独自機能の理解はかなり障壁になりました.普段書いているTypeScriptにも非同期処理の仕組みはありますが,CoroutineやFlowは考え方がかなり異なるため,概念の部分から覚える必要がありました.正直なところ,記事中で「Coroutineは全く理解できていない」と書いたときから理解度はあまり変わっておらず,今も自信を持って使いこなせているとは言えない状態です. それでもviewModelScope.launchMutableStateFlowなど,よく出てくる定番の形は必要に応じて使えるようにはなってきました.

Jetpack Compose

私が担当しているAndroidアプリではJetpack Composeを全面的に採用していますが,これもまた初めて触れる技術でした.

これまではUIライブラリとしてReactを主に触ってきましたが,Jetpack Composeとは宣言的UIライブラリという点が共通しています.例えば,ボタンをクリックした回数を表示するためのコードは,それぞれ次のようになります.

// TypeScript (React)
function Counter() {
  const [count, setCount] = useState(0);

  return (
    <>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
    </>
  );
}
// Kotlin (Jetpack Compose)
@Composable
fun Counter() {
  var count by remember { mutableStateOf(0) }

  Column {
    Text(text = "Count: $count")
    Button(onClick = { count++ }) {
      Text("Increment")
    }
  }
}

並べてみると対応関係がよくわかりますが,同時に書き味の違いもはっきりしてきます. Composeでは,@Composableアノテーションのついた関数を定義することで, それがコンポーネント(コンポーザブル)であることを定義できます. また上のサンプルコードには登場しませんが,出しわけを行うときにifwhenをそのまま書くこともできるため,条件が複雑になってもコードがかなりスッキリします.

Composeでの状態管理の方法は,Reactのそれによく似ています.
上のサンプルコードでもよく現れており,以下のような違いがあります.

  • 状態の保存
    • React: useStateを使う
    • Compose: remembermutableStateOfを使う
      • 画面回転などでActivityが破棄されると状態がリセットされてしまうので,そのときはrememberSaveableを使う
  • 状態の更新
    • React: setCountを呼ぶ
    • Compose: countを直接操作する
  • 副作用の実行
    • React: useEffect を使う
    • Compose: LauchedEffectを使う

状態の更新は特に両者の設計の違いがよく現れているところだと思っています. Reactでは値の読み出しは変数,状態の更新は関数,というように役割が分かれています. 一方Composeでは,by(移譲プロパティー)を使うことで,値の読み出しと状態更新を変数を操作するのと同じ感覚で扱うことができます.

スタイリング

スタイリングのつけ方の違いも印象的でした.ComposeではCSSではなくModifierという仕組みでスタイルをつけていきます. CSSは基本的にテキストファイルで,実際には機能しないスタイルを指定することができますが,Modifierはプログラム中に出てくる型なので,指定できないものはエディタのサジェストに出ませんし,ビルド時にエラーになりますから,書きながら間違いに気づける安心感があります.

Hilt

DIには Hilt を使っています.@Injectなどのアノテーションをつけておくと,Hiltが内部で使っているDaggerがビルド時に依存関係を解決し,依存を渡すためのコードを生成してくれます. ビルド時には KSP(Kotlin Symbol Processing) が使われており,Kotlinのコードを,Javaを介さずにKotlinのAPIから直接扱えるようになっています.以前は Kapt(Kotlin Annotation Processing Tool) という,一度Javaに変換してから処理する仕組みが使われていましたが,KSPに基盤が変わった結果,Javaへの変換によるオーバーヘッドがなくなり,高速にコード生成ができるようになっているようです.

Webとの違い

ストア

触れる前まではアプリストアは審査や様々な基準が厳しくリリースするハードルが高いものという印象がありました.
いろいろと調べていくうちに,アプリ内課金のための決済SDKなど,エコシステムがよく整備されているなという印象を受けました.

(Google Play Storeの)段階的リリース機能 が非常に便利で,コードやインフラ側の変更が必要なく,ブラウザからパーセンテージを変更するだけでリリースを調整することができる手軽さが魅力だと思っています.

ブログアプリのリニューアル後の初回リリースでは,影響を見ながら段階的にリリース範囲を広げることができ,リリース直後でも安心して様子を見守ることができました.

リリース

私はこれまで5回ほどリリース担当を経験してきました.リリース担当は以下のような手順でリリースを行うことになっています.

  1. コードはGitで管理されているので,タグを打つ
  2. リリースビルドを走らせる
  3. 生成された成果物をストアにアップロードする
  4. リリースノートなど必要事項を埋めてストアに提出する
  5. 審査が終わったら公開する

そこまで複雑な手順ではありませんが,リリースは基本月1回で慣れていないと間違いやすいです(リリースノートの変更忘れなど).
そこで,チームのドキュメント置き場にリリース手順を箇条書きでまとめておくことにしました.手順ごとにチェックボックスを作っておき,それをリリースのたびにページを複製してチェックをつけていくという感じで運用しています.

リリースの工夫

ストアにアップロードするファイルは App Bundle(.aab)というものです.これは Play Store 向けのパッケージ形式で,リリースビルド用の GitHub Actions をリリース作業時に回して生成しています.
この App Bundle を生成する GitHub Actions では,actions/upload-artifactを使ってアップロードしていました. ここで地味に困っていたのが,actions/upload-artifactはアップロード時に成果物を自動で zip 圧縮してしまう点です.ストアへの提出には aab そのものが必要なので,ダウンロードしたあとに毎回解凍する手間がありました.

2026 年 2 月にリリースされた v7 では,archive: false を指定することで単一ファイルであれば zip で圧縮せずそのままアップロードできるようになり,余計な解凍ステップを挟まずに済むようになりました.

QA

QAをリリース前にチームで実施しています.基本項目をスプレッドシートにまとめており,機能を変更/追加したときには重点項目として追加する運用です. ライブラリに破壊的な変更があったときも同様に,チェックを足すようにしています. 実施はチームで時間をとり,担当箇所を決めて進めていています. このとき言語設定やダークテーマの設定状況が偏らないように気をつけています.

QAで大きな不具合が見つかると大変です.同じ時期に変更された別の機能との兼ね合いで不具合が発生することがあったり,仕様を勘違いしたまま作ってしまい,リリースから機能が落ちてしまったりしたこともありました. スマホアプリはWebアプリケーションと違い一度リリースすると基本的にrevertしづらいので,機能が壊れた状態でリリースすることがないよう気を配っています.

終わりに

Webアプリケーション開発を中心にやってきた私にとって,この半年間は驚きや戸惑いの多いものでした. 一方で,Jetpack ComposeはReactで親しんだ宣言的UIの考え方と地続きで,比較的すんなりと理解できたと思っています.逆に,FlowやCoroutineといったKotlinが持つ言語機能についてはまだ理解しきれていないので,引き続き向き合っていかないといけないなと思っています.