📖 27分で読める

【個人開発 #4】PWAを"本物のアプリ"にしたい — Star Hopper Android化の全記録


💡 この記事について AIアシスタント(OpenClaw/スピカ)との会話を元にした学習記録です。 内容は変更される可能性があるため、実際の開発では公式ドキュメントを確認の上、ご自身の判断でお願いします。

🌟 Star Hopper とは? スマホを夜空にかざすと「あの明るい星は何?」がわかる星座ナビアプリ。明るい星を手がかりに次の星座を見つけていく「スターホッピング」をガイドしてくれます。現在 Google Play でテスト中🚀

Star Hopper スプラッシュ画面Star Hopper スターホッピングガイド

この記事で分かること:

  • PWAからAndroidアプリ(Google Play配布)にする方法
  • Capacitorの仕組みと選んだ理由
  • Kotlinでネイティブセンサープラグインを自作する流れ
  • ビルド時にハマった「JDKバージョン問題」と「署名の壁」

PWAじゃダメなの?問題

セブ
セブ
スピカ、ずっとWebアプリとして作ってた Star Hopper なんだけど…Google Playに出してみたくなったんだよね。
スピカ
スピカ AI
Star Hopper、PWAでもインストールできるのに、なんでわざわざ?
セブ
セブ
ちなみにPWAって何?って人もいるかもしれないから補足してもらっていい?
スピカ
スピカ AI
もちろん。PWAはProgressive Web Appの略で、Googleが2015年頃に提唱した仕組み。ブラウザで動くWebサイトなんだけど、ホーム画面に追加するとアプリみたいに使える。オフラインでも動いたり、プッシュ通知も出せたりするの。
スピカ
スピカ AI
インストール不要で使えるのが強みだけど、ハードウェアへのアクセスには限界があるんだよね。
セブ
セブ
そうそう、それがさ、カメラを空にかざして「あの明るい星、何?」ってやりたいんだけど、コンパスの方角がズレるんだよね。南を向いてるのに西って表示されたり。なんで?
スピカ
スピカ AI
あー、Web APIの deviceorientation イベントね。ブラウザによって実装バラバラだし、キャリブレーションもできないから精度は出にくいよね。
セブ
セブ
そうそう。星空アプリで方角がズレるって致命的じゃん。あと、Google Playに出てるだけで「ちゃんとしたアプリ」感があるっていうか…。
スピカ
スピカ AI
なるほど。じゃあ整理すると、PWAとネイティブの比較はこうなるよ:
項目PWA(ブラウザ)ネイティブアプリ
コンパス精度△ ブラウザ依存◎ ハードウェア直接
カメラアクセス○ 基本OK◎ フル制御
重い処理△ JSエンジン経由◎ ネイティブ速度
ストア配布× 不可◎ Google Play
既存コードの活用◎ そのまま△ 書き直し?
セブ
セブ
最後の行がポイントで。何ヶ月もかけて作ったコードを全部書き直すのは無理なんだよね。
スピカ
スピカ AI
そこで出てくるのが Capacitor だね。

React NativeでもFlutterでもない選択肢

セブ
セブ
で、スピカに「Androidアプリ作りたいんだけど、どうしたらいい?」って相談したら、最初に出てきたのがFlutterだったんだよね。
スピカ
スピカ AI
Flutterは Google が作ったクロスプラットフォームのフレームワークで、Android と iOS のアプリを1つのコードベースから同時に作れるの。人気も高いし、最初の選択肢としてはおすすめしやすかったんだよね。
セブ
セブ
でも調べてみたら、Flutter使うと今まで書いたコードが全部使えなくなるって分かって。React Nativeっていうのもあったけど、それも同じ。なんで?
スピカ
スピカ AI
FlutterはGoogleが作った Dart っていう専用言語で全部書き直す仕組みだし、React Nativeは React(JSX) が前提なの。どっちも「そのフレームワークのルールに従って最初から作る」必要があるから、セブが素のJavaScriptで書いた Star Hopper のコードをそのまま持っていけないんだよね。
セブ
セブ
えー、じゃあ何ヶ月も書いたコード、捨てるしかないの?って思ったんだけど、スピカに相談したら**「それならCapacitorがいいよ」**って。
スピカ
スピカ AI
そこでCapacitor。これはIonic社が作ったオープンソースのツールで、もともとApache Cordova(昔のPhoneGap)っていう「Webアプリをネイティブに変換する」技術があったんだけど、その後継として2019年に登場した。Cordovaの課題だったパフォーマンスやメンテナンス性を改善して、モダンなWeb技術にちゃんと対応してるのがポイント。
セブ
セブ
へー、こういう「Webアプリをアプリにする」って発想、昔からあったんだ。
スピカ
スピカ AI
そう。で、Capacitorは「既存のWebアプリをそのままAndroidアプリにする」ためのツール。仕組みはシンプル:
🌐 既存のWebアプリ(HTML/CSS/JS)
     ↓ そのまま
📱 Android WebView にラッピング
     ↓ 必要な部分だけ
🔧 Kotlin/Swiftでネイティブ機能を追加
セブ
セブ
つまり、自分のアプリはそのまま動いて、コンパスとかカメラとか「ブラウザだけじゃ無理な部分」だけ追加で書くってこと?
スピカ
スピカ AI
そういうこと。Webとネイティブのいいとこ取りだね。セットアップもコマンド2つで終わるし。
Star Hopper 北斗七星から北極星へのルートガイド

☝️ 実際のStar Hopper画面。北斗七星から北極星への道筋を、ステップバイステップでガイドしてくれる。こういうリアルタイムな方位連動が、ネイティブセンサーの恩恵。

# ① プロジェクトをCapacitor対応にする(設定ファイルが生成される)
npx cap init

# ② Androidプラットフォームを追加(androidフォルダが自動生成される)
npx cap add android
セブ
セブ
え、コマンド2つだけ? これ何やってるの?
スピカ
スピカ AI
cap init は「このWebアプリをCapacitorで管理するよ」って宣言。cap add android で「Androidアプリとしてのひな形を作って」って感じ。
スピカ
スピカ AI
そう。この2コマンドで android/ フォルダが生成されて、Android Studioで開けるプロジェクトになる。あとは npx cap sync でWebアプリのビルド結果をAndroidプロジェクトにコピーするだけ。
セブ
セブ
実際やってみたら、本当にindex.htmlがAndroidアプリの中で動いた時は感動したよ。「え、これだけ?」って。

「南を向いたのに北って出る」事件 🧭

セブ
セブ
で、本題。スピカに教えてもらいながらコンパスの機能を追加したんだけど、ここからが地獄だった。
スピカ
スピカ AI
どんな問題が起きたの?
セブ
セブ
コンパス自体は動くようになったんだけど…スマホを縦に持つと方角が逆になる。南を向いてるのに「北」って出る。なにこれ?
スピカ
スピカ AI
ちなみにコンパスは TYPE_ROTATION_VECTOR っていうネイティブ専用のセンサーを使ってるの。加速度計・ジャイロ・地磁気を統合して方位を出す高精度なやつで、PWAのブラウザAPIじゃアクセスできない。だからこそKotlinで自作した意味があったんだけど…それは**「座標系リマップ」**の問題だね。
セブ
セブ
座標系…? なんで持ち方で方角が変わるの?
スピカ
スピカ AI
Androidのセンサーって、デフォルトだとスマホをテーブルに置いた状態が基準なの。でも星を見るときって縦に持って空にかざすでしょ? そうすると座標軸の向きが入れ替わって、方角がめちゃくちゃになるの。remapCoordinateSystem っていう変換処理を入れないといけない。
セブ
セブ
それって難しいの?
スピカ
スピカ AI
修正自体はたったこれだけなんだけど:
// ポートレート(縦持ち)用に座標系をリマップ
SensorManager.remapCoordinateSystem(
    rotationMatrix,
    SensorManager.AXIS_X,    // X軸はそのまま
    SensorManager.AXIS_Z,    // Y軸→Z軸に変換
    adjustedMatrix
)
スピカ
スピカ AI
たった3行なのに、これがないと南北逆転するっていうね。
セブ
セブ
3日悩んだよ…。エミュレータだとセンサーの挙動が実機と違うから、実機テストじゃないと気づけなかったかもしれない。
Star Hopper AR画面 — 方位194°(南)を正確に表示

☝️ 修正後の画面。ちゃんと「南 194°」と表示されている。たった3行の修正で、南北逆転が解消された。


JDKバージョン地獄

セブ
セブ
コードができたら次は**アプリにまとめる作業(ビルド)**なんだけど、ここでまたエラー。
スピカ
スピカ AI
何が出たの?
セブ
セブ
ビルドのボタンを押したら、なんか英語のエラーが出て止まっちゃって:
Unsupported class file major version 65
スピカ
スピカ AI
これ、ざっくり言うと**「新しい道具で作ったパーツを、古い機械が読めない」**って状態。Javaにはバージョンごとに番号があって、「version 65」= JDK 21 のこと。Capacitor 6 のライブラリが JDK 21 で作られてるのに、自分のマシンに入ってる JDK 17 だと「知らない形式です」ってエラーになるの。
セブ
セブ
つまり、道具のバージョンが古いってこと? どうすれば直るの?
スピカ
スピカ AI
JDK 21をインストールして、パソコンの設定を新しいバージョンに切り替えればOK。こんな感じ:
# JDKバージョン確認
java -version
# java version "21.0.x" が出ればOK

# JAVA_HOMEの設定(Mac)
export JAVA_HOME=$(/usr/libexec/java_home -v 21)
セブ
セブ
これで直ったんだけど、なんでこんな分かりにくいの?
スピカ
スピカ AI
ここがハマりポイントで、Capacitorの公式ドキュメントには「JDK 21が必要」ってはっきり書いてないの。エラーメッセージも「version 65がサポートされてません」だけだから、初心者には何のことかさっぱりだよね。
セブ
セブ
「JDK 21を入れてください」って一言書いてくれればいいのに…。こういうの、地味にきつい。

「署名」って何?キーストアの壁

スピカ
スピカ AI
ビルドは通った?
セブ
セブ
デバッグビルドは通ったよ。でもGoogle Playに出すにはリリースビルドが要るでしょ。そしたら「署名付きAAB」が必要だって言われて。
スピカ
スピカ AI
アプリの「身分証明書」みたいなものだね。キーストアっていうファイルに秘密鍵を入れて、それでアプリに署名する。
セブ
セブ
最初は「なんで面倒なことさせるんだ」って思ったけど、考えたら誰でも他人のアプリを勝手にアップデートできたら危険だよね。
スピカ
スピカ AI
そう。たとえば悪意のある第三者が「Star Hopperのアップデートです」って偽のバージョンをアップロードできちゃったら、ユーザーのスマホにマルウェアが入るかもしれない。署名があることで「このアップデートは確かに元の開発者が作ったものです」って証明できるの。
セブ
セブ
なるほど、ユーザーを守るための仕組みなんだな。納得。
# キーストア生成
keytool -genkeypair -v \
  -keystore starhopper-upload.jks \
  -keyalg RSA -keysize 2048 -validity 10000 \
  -alias starhopper \
  -storepass [パスワード] \
  -keypass [パスワード] \
  -dname "CN=Star Hopper, OU=Development, O=7Sapiens, L=Tokyo, C=JP"
スピカ
スピカ AI
で、build.gradle にこの署名情報を書いてあげる:
// android/app/build.gradle
android {
    signingConfigs {
        release {
            storeFile file('../starhopper-upload.jks')
            storePassword '[パスワード]'
            keyAlias 'starhopper'
            keyPassword '[パスワード]'
        }
    }
    buildTypes {
        release {
            signingConfig signingConfigs.release
        }
    }
}
セブ
セブ
これで ./gradlew bundleRelease を叩いたら、ついに署名付き .aab ファイルが出来た!
スピカ
スピカ AI
おめでとう。…ところでセブ、このキーストアファイルの管理、ちゃんと考えてる?
セブ
セブ
え、普通にプロジェクトフォルダに置いてあるけど…。
スピカ
スピカ AI
それ、なくしたらアプリのアップデートが二度とできなくなる可能性があるよ。
セブ
セブ
え、マジ?
スピカ
スピカ AI
マジ。Google Playの仕組みを整理するね。実は2種類の鍵があるの:
鍵の種類誰が持つ?役割
アプリ署名鍵Googleストアでの最終署名に使う。Googleが安全に保管
アップロード鍵開発者(自分)AABをアップロードする時の本人確認用
セブ
セブ
つまり自分が keytool で作ったのは「アップロード鍵」のほうで、最終的な署名はGoogleがやってくれると?
スピカ
スピカ AI
そう。Google Play App Signing っていう仕組みで、初回アップロード時にGoogleが「アプリ署名鍵」を自動生成して安全に保管してくれる。だからアプリの完全性はGoogleが守ってくれるの。
セブ
セブ
じゃあ自分のアップロード鍵(.jksファイル)をなくしても大丈夫ってこと?
スピカ
スピカ AI
完全にアウトではないけど、かなり面倒。アップロード鍵をなくした場合は、Play Consoleからリセット申請ができる。ただし本人確認に時間がかかるし、その間アプリのアップデートが出せない。
セブ
セブ
うわ、それは困る…。
スピカ
スピカ AI
しかもセブ、Mac miniとMacBookの2台で開発してるでしょ?
セブ
セブ
そうなんだよ。家ではMac mini、外ではMacBook。両方からビルドしたい。
スピカ
スピカ AI
その場合、同じキーストアファイル(.jks)を両方のマシンに置く必要がある。キーストアはマシンごとに別々じゃダメ。同一のファイルじゃないと「別人がアップロードしてる」とGoogleに判断されて弾かれるよ。
セブ
セブ
じゃあ .jks ファイルを2台で同期しないといけないわけか。でも Git にキーストアを入れるのはセキュリティ的にまずいよね?
スピカ
スピカ AI
その通り。.jks ファイルは絶対に Git にコミットしちゃダメ。パスワードが漏れたら誰でもアプリを署名できちゃうから。安全な共有方法は:
  • iCloud Drive / Google Drive — 暗号化された場所にバックアップ
  • USB経由でコピー — ネットワークを経由しない最安全
  • 1Password / Bitwarden — パスワードマネージャーにファイルごと保存
セブ
セブ
自分はとりあえず iCloud Drive にバックアップして、パスワードは1Passwordに保存した。
スピカ
スピカ AI
いいね。あと .gitignore にキーストアを追加するのも忘れずに:
# .gitignore に追加
*.jks
*.keystore
セブ
セブ
キーストアひとつでこんなに考えることがあるとは思わなかった…。
スピカ
スピカ AI
個人開発だと自分しかいないから、鍵の管理 = アプリの生命線なんだよね。会社なら管理してくれる人がいるけど。

振り返り — 「PWA → Android」で見えた景色

セブ
セブ
振り返ると、Capacitorのおかげで既存のWeb資産を捨てずにAndroidアプリにできたのが一番大きいかもしれない。
スピカ
スピカ AI
数万行のコードを書き直すのと、Kotlinで200行のプラグインを追加するのと、どっちが現実的かって話だよね。
セブ
セブ
ネイティブセンサーの精度は体感で全然違った。Web APIのコンパスが「だいたい北」なのに対して、ネイティブは「北北西350度」みたいにピタッと出る。
スピカ
スピカ AI
星空アプリにはその精度が命だもんね。
Star Hopper シリウスを確認!達成画面

✨ シリウスを見つけた時の達成画面。オリオンの三つ星からたどり着くルートをガイドして、最後にこの画面が出る。星の豆知識も読める。

セブ
セブ
つまづいたポイントをまとめると:
  1. 座標系リマップ — スマホの持ち方で座標軸が変わる(3日悩んだ)
  2. JDK 21 必須 — Capacitor 6+ の暗黙の要件
  3. キーストア管理 — なくしたら終わりの秘密鍵
セブ
セブ
どれもドキュメントを丁寧に読めば回避できたかもしれないけど、実際にハマって体で覚えるのも大事なのかなと思ったりします。
スピカ
スピカ AI
何か他に気づいたことは?
セブ
セブ
あとね、今回強く感じたのが**「AIに任せるにも、ある程度の技術知識がないと的確な質問ができない」**ってこと。
スピカ
スピカ AI
どういうこと?
セブ
セブ
Star Hopperって、星図のリアルタイム描画、GPSでの現在地取得、センサーのコンパス連動…いろんな機能が同時に動くわけじゃん。星図をスムーズに動かしたい、でもGPSもセンサーも同時に処理してる中で描画が重くなる。こういう「複数の技術が絡み合う問題」って、そもそも何が原因で遅いのか・何がボトルネックになってるのかをある程度わかってないと、AIに聞いても的外れな答えしか返ってこない。
スピカ
スピカ AI
「アプリが遅いです、助けて」じゃ範囲が広すぎるもんね。「センサーのポーリング頻度が描画フレームレートとぶつかってる気がする」くらい絞れると、AIも具体的な対策を提案できる。
セブ
セブ
そうそう。AIは魔法じゃなくて「対話のパートナー」だから、こっちが投げる質問の精度で返ってくる答えの質が全然変わる。技術を知ってるほどAIを使いこなせるっていう、ちょっと矛盾してるようで大事な話だなって。
スピカ
スピカ AI
次はいよいよ Google Play にこのアプリを登録する話だね。ストアの入力項目の多さに絶望する回。
セブ
セブ
ネタバレやめて。

⚠️ 注意 この記事はOpenClaw/AIアシスタントとの会話を元にした内容です。 実際の運用は自身で確認してください。 技術情報は変わる可能性があるので、公式ドキュメントの確認を推奨します。


シリーズ目次

  1. VPSとMac Miniの違い
  2. Groq APIで音声文字起こし
  3. 喋るだけでブログ記事になる仕組み
  4. PWAを”本物のアプリ”にしたい ← 今ここ
  5. はじめてのGoogle Play — ストア掲載編(次回)