RORK LABEN
PRICE — Rork Max の価格帯は月200ドルから1,800ドルまで幅があります。作業量の多い開発者やチーム向けの帯が上に伸びている形ですFREE — 無料枠は週5プロンプト程度です。試すには足りますが、継続的に作るなら有料前提の設計になっていますSHIP — App Store への公開はビルド・証明書・申請まで自動化されており、Mac も Xcode も持たずに iOS アプリを出せますSIM — ブラウザ配信のシミュレータで、実際の Apple 環境で動く画面を手元のブラウザから確認できますNATIVE — HealthKit、ARKit と LiDAR、NFC、Dynamic Island、Metal による3D まで届きます。React Native では触れない領域ですFUNDING — 2026年4月9日発表のシードで Left Lane Capital 主導の1,500万ドルを調達し、アプリビルダーの Paperline を買収していますPRICE — Rork Max の価格帯は月200ドルから1,800ドルまで幅があります。作業量の多い開発者やチーム向けの帯が上に伸びている形ですFREE — 無料枠は週5プロンプト程度です。試すには足りますが、継続的に作るなら有料前提の設計になっていますSHIP — App Store への公開はビルド・証明書・申請まで自動化されており、Mac も Xcode も持たずに iOS アプリを出せますSIM — ブラウザ配信のシミュレータで、実際の Apple 環境で動く画面を手元のブラウザから確認できますNATIVE — HealthKit、ARKit と LiDAR、NFC、Dynamic Island、Metal による3D まで届きます。React Native では触れない領域ですFUNDING — 2026年4月9日発表のシードで Left Lane Capital 主導の1,500万ドルを調達し、アプリビルダーの Paperline を買収しています
記事一覧/アプリ開発
アプリ開発/2026-05-29中級

Android 壁紙アプリ v2.1.0 公開後の保守 — Crashlytics と Claude in Chrome で回す日々の運用

Beautiful 4K/HDR Wallpapers v2.1.0 と Ukiyo-e Wallpapers v1.8.0 の段階公開後、Crashlytics 監視と新規クラッシュ対応を Claude in Chrome に任せる運用に切り替えました。RecyclerView の防御的コピーや Glide の desugar など、実機で踏んだ落とし穴を保守目線でまとめます。

Android44保守3Crashlytics13段階公開3RecyclerView3Glide2個人開発195Claude in Chrome4

審査通過の通知が届いた翌朝、Play Console の段階公開スライダーを 5% に動かしました。Beautiful 4K/HDR Wallpapers v2.1.0(versionCode 49)と、Ukiyo-e Wallpapers v1.8.0(versionCode 41)。押した瞬間から、Crashlytics のタブを閉じられなくなります。

新しい機能を書いている時間より、公開したあとに数字を見ている時間のほうが長い週があります。壊れていないことを確認し続ける作業には終わりがなく、しかも誰にも気づかれません。個人開発で複数本を並行して抱えていると、この時間が静かに一日を削っていきます。

公開から2週間で実際に踏んだ落とし穴と、毎朝の監視をどこまで Claude in Chrome に預けたか。ここでは、手元の実機で再現し、修正まで通したものだけを保守目線で書き残します。

公開直後の最初の48時間が一番怖い

Android の段階公開(Phased Rollout)は、5% → 25% → 50% → 100% の4段階で1段階あたり24〜48時間というのが私の標準です。一番神経を使うのは最初の5%が当たっている48時間で、ここで Crashlytics の Crash-free users が99.7%を下回ったり、ANR が0.20%を超えたりすると、即座に halt(段階公開比率を0%に戻す)判断をします。

v2.0.0 を公開したときは、ここで2回ほど halt して旧版に戻しました。何が起きていたかというと、WallpaperPagerAdapterThumbnailViewPagerAdapter のスクロール中に IndexOutOfBoundsException が頻発し、28日間で50ユーザー以上・56イベント以上のクラッシュが計上されました。

原因はリストへの参照を直接保持していたことで、データソース更新中に RecyclerView がアクセスして競合する状態でした。mList を防御的コピー化する1行修正で v2.1.0 にて根治しました。

// Before: 共有参照
public void setItems(List<Wallpaper> list) {
    this.mList = list;
    notifyDataSetChanged();
}
 
// After: 防御的コピー
public void setItems(List<Wallpaper> list) {
    this.mList = new ArrayList<>(list);
    notifyDataSetChanged();
}

壁紙アプリを長く保守してきて手元に残った結論は、RecyclerView のクラッシュは大抵このデータソース共有参照に行き着く、という地味なものです。派手な原因はめったに出てきません。呼び出し側が渡したリストを後から clear() して詰め直す——それだけで、スクロール中のアダプタは足元を掬われます。

halt は「戻す」ではない、という誤解を解く

段階公開を止めたとき、私は最初「これで元に戻った」と思っていました。実際には違います。公開比率を 0% にしても、すでに v2.1.0 を受け取った端末が旧版へ戻ることはありません。halt でできるのは「これ以上広げない」だけです。

つまり 5% の段階で 10 万人規模のユーザーがいるアプリなら、halt した時点で数千人が壊れた版を持ったまま取り残されます。ここを取り違えると、halt して安心した数時間のあいだにレビュー欄が荒れます。私はこの誤解のせいで、v2.0.0 のとき初動を半日遅らせました。

だから halt の直後にやるのは、旧版に戻す操作ではなく、versionCode を上げた修正版を最短で出すことです。判断の閾値は次のように固定しています。数字を毎回考え直すと、深夜の自分が必ず甘い方に倒れるからです。

指標halt する線次にやること
Crash-free users99.7% 未満比率を 0% にし、当日中に hotfix の versionCode を用意する
ANR 率0.20% 超比率は据え置き、Play Console の ANR クラスタを先に読む(誤検知が多い)
単一クラッシュの影響ユーザー5% 配信中に10人超halt。同一スタックなら再現条件を特定してから直す
起動3秒以内のクラッシュ1件でも新規発生即 halt。早期クラッシュは体感被害が最も大きい

最後の行だけ、他より明らかに厳しくしてあります。起動直後に落ちるアプリは、ユーザーから見れば「開かないアプリ」で、原因の軽重とは無関係にアンインストールされます。Glide の desugar 漏れで踏んだのが、まさにこの種類でした。

minSdk 23 と Glide 5.0.5 の desugar 必須化

v2.0.0 を公開した直後、Android 6.0.1(API 23)を使っているユーザー全員が起動3秒以内にクラッシュするという報告が Crashlytics に上がりました。7日間で12イベント・4ユーザー、Crashlytics 側は「早期クラッシュ」タグを付けてきました。

原因は Glide 5.0.5 が内部で Java 8 の java.util.function.Supplier を使っており、minSdk 23 では desugar が必須になることでした。build.gradle の依存に coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5' は入っていたのですが、compileOptions { coreLibraryDesugaringEnabled true } のフラグが立っていませんでした。

android {
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_17
        targetCompatibility JavaVersion.VERSION_17
        coreLibraryDesugaringEnabled true  // ← これが落ちていた
    }
}
 
dependencies {
    coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5'
}

ライブラリの依存ツリーが Java 8 API を要求する場合、desugar の依存追加だけでは不十分で、compileOptions のフラグ有効化が必要だ、というのが地味だけど大事な学びでした。

Play Store の density split で消えたリソース

Google Pixel + Android 12 の一部ユーザーで、起動時に Resources$NotFoundException が出るというクラッシュが Crashlytics に上がってきました。MainActivity.onCreate:120 で参照している background_silver の Drawable が見つからないという内容です。

調べた結果、background_silver.jpgdrawable-xxhdpi/drawable-xxxhdpi/ にしか配置しておらず、Play Store の Android App Bundle が密度ごとに分割配信した際、低密度バケットの端末に届く APK にこのリソースが含まれていない、という事故でした。

修正は drawable-nodpi/ に同ファイルを配置することです。drawable-nodpi/ はどの密度の split APK にも含まれるため、確実に存在させたいリソースの置き場として安全です。

app/src/main/res/
├── drawable-xxhdpi/background_silver.jpg
├── drawable-xxxhdpi/background_silver.jpg
└── drawable-nodpi/background_silver.jpg  ← これで全密度に届く

App Bundle 時代の Android では、密度別ディレクトリと nodpi のどちらに置くべきかという判断が、デザイン部門と運用部門の境界に落ちがちです。私は「確実に必要なリソースは nodpi、画質を優先したいものは密度別」というルールに揃えてからは、この種の事故は出なくなりました。

Claude in Chrome に任せている Crashlytics 監視

公開直後の48時間は人間が張り付くしかないのですが、それ以降は新規クラッシュの一次トリアージを Claude in Chrome に毎朝任せています。

具体的にやらせていることはシンプルで、Firebase Console の Crashlytics タブを開いて、前日新規発生したクラッシュを以下の条件で並べ替えてもらいます。

  • Crash-free users が 99.7% を下回ったクラッシュ
  • 影響ユーザー数が10人を超えたクラッシュ
  • 「Active」ステータスで「Untouched」のクラッシュ

ここまでを Claude in Chrome に出してもらった後、本当に対応が必要なクラッシュかどうかを私が判断します。スタックトレースの読み取りまでは AI に頼みますが、修正パッチを書くかどうかは、影響範囲と修正コストを見て自分で決めています。

毎朝投げているプロンプトは、ほぼこの形で固定しています。指示を短く書くと Console の既定フィルタに流されるので、確認を明示的に一手挟ませているのが要点です。

Firebase Console の Crashlytics を開いてください。
アプリは Beautiful 4K/HDR Wallpapers、期間は「過去24時間」。
 
1. まず、いま画面で有効になっているフィルタ(期間・バージョン・
   ステータス)をそのまま読み上げてください。想定と違う場合は
   修正してから次へ進んでください。
2. Status = Active かつ Untouched の新規クラッシュだけを、
   影響ユーザー数の多い順に列挙してください。
3. 各件について「影響ユーザー数 / イベント数 / 先頭スタックフレーム /
   端末とOSの偏り」の4項目だけを表にしてください。
4. 推測での原因分析や修正案は書かないでください。

3 で項目を4つに固定しているのは、出力が長くなるほど私が読まなくなるからです。4 でわざわざ原因分析を禁じているのは、もっともらしい推測が混ざると、スタックトレースを自分で読む手間を省いてしまうためでした。トリアージで欲しいのは解釈ではなく、素の観測値です。

ハマりどころとしては、Firebase Console の UI が時々レイアウト変更されるため、上の 1 番——「いまどのフィルタが効いているか」の読み上げ——を省けません。これがないと、期間フィルタが「過去7日間」のまま残っていて、既知のクラッシュが毎朝「新規」として再登場します。二度ほど同じ件を追いかけ直して、この一手を足しました。

ad-free 状態の合成判定という設計の話

v2.0.0 公開後の保守で、一番神経を使ったのが ad-free 状態の合成判定でした。Globals.SHOW_AD の真実は、2つの独立した永続ストアから合成されます。

  • BillingManager — 永久課金やサブスクリプションの状態
  • AdFreeManager — リワード広告視聴で得た期間限定 ad-free の状態

ここで setAdFree(false) を直接呼ぶコードを1箇所でも書くと、BillingManager 側で ad-free を持っているユーザーが意図せず広告ありに戻る事故が起きます。鉄則は、常に合成判定(isAdFree || isRewardAdFree)を経由することです。

過去には BillingManager.restorePurchases() に論理逆転バグがあり、ad-free のままサブスク失効しても false に戻すパスが存在しないという事故もありました。inappsubs の両クエリを並行実行し、両方完了してから合成判定する finalEvaluation パターンに刷新し、Activity.onResume()restorePurchases() を呼ぶように変更したことで、起動中の失効も復帰時に検出できるようになりました。

個人開発で課金フローを触るときは、「Single Source of Truth が複数ある」という現実を最初に認めるところからスタートするのが、結局は近道だと思っています。

ユーザー設定 vs API デフォルトの上書きパターン

サーバー API が起動時に返す値(シャッフル、パーソナライズ広告の有効・無効など)と、ユーザーが Settings 画面で変更した値が衝突する問題があります。

私は各設定に *_USER_OVERRIDE フラグを別キーで持たせる方針で揃えました。

  • ユーザー操作からの保存は専用の *ByUser() メソッドを通す。このとき override も同時に立てる
  • システム由来の保存は override が立っていればスキップする

これによって、サーバー側でデフォルト値を変更してもユーザー設定が上書きされず、かつユーザー設定をリセットしたい場合は override を消すだけで API デフォルトに戻る、という運用が成立します。

多言語ユーザーレビュー返信のレート制限

最後に、保守の中で意外と重い作業がユーザーレビューへの返信です。4アプリ × 主要6カ国分で App Store だけで72件の代理返信を1セッションで処理したことがありますが、ここにはいくつか罠があります。

  • 1セッションで30〜40件を超える連続送信は、Google からスパム判定されるリスクがある
  • App Store 側もレート制限があり、各送信の間に約8秒の待機を入れた方が安全
  • 自動翻訳のベタ貼りは Google ポリシー違反として弾かれる可能性があるため、手書きトーンで返す
  • アフィリエイトリンク、他アプリ宣伝、SNS リンクは入れない

私は日本語・英語・繁体字中国語・イタリア語・ロシア語・韓国語・ペルシャ語・ウクライナ語・タイ語・ポーランド語・ポルトガル語(ブラジル)の11言語で返信しています。Claude in Chrome に手書きトーンで翻訳させ、各送信の間に8秒のディレイをプロンプトに明記しています。

公開後の保守は「監視 → 一次トリアージ → 修正判断」の3層

v2.0.0 から v2.1.0 までの保守を振り返って、結局やっていることは3層の繰り返しでした。

  1. 監視 — Crashlytics と Play Console の品質メトリクスを毎朝確認(Claude in Chrome に大部分を任せる)
  2. 一次トリアージ — 新規クラッシュのスタックトレースを読み、影響範囲とパターンを言語化する(AI と人間の混成)
  3. 修正判断 — 影響範囲と修正コストを見て、修正するか・段階公開を halt するか・無視するかを決める(人間が決める)

個人開発で6アプリを並行運用するためには、1の監視を人間がやるのは時間的に成立しないと感じています。AI に任せられる部分を機械的に任せて、3の修正判断に集中する時間を作る、というのが現状の落としどころです。

明日から、メディエーション設定への Liftoff・Unity Ads・InMobi の3社追加と、iOS 版のスライドショー逆移植が並行で動きます。v2.1.0 の保守は AI に任せつつ、新規開発に集中する1週間にする予定です。

シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

アプリ開発2026-05-28
壁紙アプリにスライドショーとページジャンプを実装した手順と判断
綺麗な壁紙 4K/HDR と浮世絵壁紙の Android 版に追加したスライドショーとページジャンプの実装メモです。Handler とライフサイクル、SeekBar 連携、広告との兼ね合いで悩んだ判断を残します。
アプリ開発2026-08-15
TestFlight の外部テストと段階公開、個人開発ではどちらに時間を使うか
Rork で作ったアプリを他人に配るとき、外部テスターを募集する道と段階公開に受け止めさせる道の二つがあります。個人開発の限られた時間をどちらに置くかを、配布が止まる条件と事前検証すべき三領域から整理しました。
開発ツール2026-05-24
壁紙アプリ6本に Privacy Manifest を並行投入した運用ノート — 1本目の差し戻しを残り5本にどう活かしたか
iOS 壁紙アプリ6本に Privacy Manifest を並行投入した運用ノートです。1本目で受けた差し戻しを残り5本にどう適用し、段階公開リングをどう設計したかを、個人開発12年の感覚で淡々とまとめました。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →