開発日記
2026年になっても、睡眠アプリを一晩中鳴らし続けるのは驚くほど難しい
朝までスマートフォンから扇風機の音を流したい。それくらいは2026年なら解決済みだと思っていました。ところが開発者として、予想以上の時間を費やすことになりました。
長年White Noise Liteを使っていました。広告があり、私には古く感じる画面でしたが、たいてい動いてくれました。それが大事でした。やがて頻繁に止まるようになりました。クラッシュだと思っていましたが、ログがないので原因は分かりません。もっと今風の画面で、録音ではなく端末で音を生成し、細かく調整できるものも欲しくなりました。そこでVeilを作り、安定性の難しさをずっと深く知りました。
iOSのバックグラウンド再生は比較的素直に実装でき、私の経験ではVeilは最初から安定しています。Androidは長い取り組みになりました。多様な機種、メモリの少ない古い端末、メーカー独自の電池管理、再生中の更新があります。自分のコードにも問題がありました。午前3時に目が覚めた人にとって、この違いはあまり意味がありません。
クラッシュ報告から始めましたが、それだけでは足りません。終了されたプロセスは例外を報告できないことがあり、生きているプロセスでも音声ストリームは無音になり得ます。フォアグラウンドサービスや再生通知も、スピーカーから音が出ている証拠にはなりません。ネイティブ側の再生記録、プロセス終了情報、メモリ測定、サービス状態、音声イベント、意図的な停止の記録を追加しました。必要な情報はプロセス終了後も残し、後の起動時に回収します。
一晩の出来事を再構成して、考えられる原因を区別したかったのです。再生はまだ意図されていたか。タイマーは終了したか。メモリは増えていたか。充電中だったか。Androidがパッケージを置き換えたか。情報は多いほど役立ちますが、それぞれが何を示すのか理解する必要があります。
最初に直す必要があったのは測定そのものでした。履歴からVeilをスワイプして閉じると、音は止まっても記録上のセッションが閉じない場合がありました。数時間後にAndroidがキャッシュされたプロセスを回収すると、再生障害として数えていました。朝の意図的な停止が「夜間の強制終了」になるのです。一方、実際の再生を中断する更新は、正常なシステム終了として除外されていました。
定義を直すだけで、音声コードをさらに直す前に障害の見え方が変わりました。それ以来、再生の改善と測定の改善を分けています。バージョン、機種群、セッション時間、終了理由ごとに定期分析し、テスト端末や重複を除きます。報告の遅延も重要です。新バージョンで短いセッションが数回成功しても、一晩持つとは分かりません。
その傾向を実機テストに生かしています。実際の端末で長時間・夜通しのテストを行い、Samsungのリモートラボも使っています。logcatの連続取得、メモリとメモリ圧迫の測定、障害時の音声・サービス状態の保存を行います。問題の瞬間を私が目撃しなくても証拠が残ります。
テスト環境自体が結果を変えることも学びました。USBは給電するので、USB接続は充電器を外した電池テストではありません。dumpsys meminfoの繰り返し実行が音の乱れを起こすことさえありました。一晩問題がなくても、その条件で再現しなかったという証拠にすぎず、全機種・全状態を保証しません。
意外だったのはAndroid Auto Backupです。Pixelのシステムログに、再生中のVeilをバックアップ時にBackupManagerが終了する様子が記録されました。全体のデータでは、2026年8月14日から9月11日にかけて93インストールで保護されたプロセスへのSIGKILLが190件あり、約3日間隔の繰り返しが目立ちました。バックアップ説を支持する傾向ですが、すべての終了原因を証明するものではありません。
クラウドからの復元に制約が生じることを受け入れ、Auto Backupを無効にしました。Androidの資料には、バックアップはアプリを終了させる一方、通常はフォアグラウンドのアプリを除外するとあります。このケースで対策を決められたのは、実機の記録があったからです。Androidのバックアップ資料
自分のコードの問題もありました。古い音声コンテキストが閉じる前に復旧が新しいものを作り、すでに問題がある中でリソースを重ねていました。終了処理を時間制限付きで待ち、復旧と再生操作を順序立てて実行するようにしました。新しい利用者の意図も優先します。修復待ちの間に停止を押したら、修復で勝手に音を戻してはいけません。
修正が別の障害を生んだこともあります。150ミリ秒の確認処理で音声コンテキストの時計が進むか調べ、動作中と申告しながら固まった状態を検出しました。しかしJavaScriptのタイマーを待つため、バックグラウンドではアプリが前面に戻るまで待機が止まることがありました。今は前面でだけ実行します。復旧経路も画面を消してテストする必要があるわけです。
効果的な変更には製品としての判断もありました。実測では広告用WebViewプロセスに約90 MBの追加常駐メモリが関連していました。以前の調査でもSDKに関連する深刻なメモリ障害が見つかっていました。まず負担を抑えようとし、最後は広告とSDKを完全に削除しました。一晩動くアプリでは信頼性を優先しました。
それでもテレメトリには、本人が停止を予想していたかという情報がありません。該当する中断後、Veilは時刻と「この停止は想定どおりでしたか?」を小さなカードで表示できます。「自分で止めましたか?」では、パートナーが止めた場合や、自分では触らず予想していた中断を拾えません。
想定内なら電池設定のトラブルシューティングに誘導しません。想定外なら中断を認め、証拠に合った案内を出します。答えは体験を直接教えてくれますが、技術的原因の断定には使いません。初めてのAndroid利用者全員に、不安な権限要求や電池設定探しをさせずに済みます。
設定は双方にとって厄介です。設定画面を開いたり、適切な場合に除外を求めたりはできますが、決めるのは利用者です。Androidの除外手続きも、全メーカーの終了処理を制御するものではありません。初再生の前に知らない設定を巡らせるより、問題の後に具体的な一歩を示すほうが役立ちます。Androidの電力管理ガイド
更新は特にもどかしい問題です。Playのアプリ別自動更新設定へ案内できますが、Veilから変更はできません。手動更新は手間を増やし、重大なセキュリティ更新にはGoogleの例外もあります。夜間の安定性を改善する更新が、その夜を中断し得るのです。Google Playの更新設定
更新後の自動復元はまだ完成していません。音声グラフの再構築に加え、タイマー状態と利用者の意図を正しく引き継ぐ必要があります。メーカー特有の傾向も、確信を持って説明するには再現が足りません。障害からは回復してほしいですが、意図して止めた人に逆らってほしくはありません。
定期分析、長時間の実機テスト、具体的な修正、直接のフィードバックを組み合わせたことが最も役立ちました。それぞれが他の見逃しを補います。今は「再生中」という表示より、その証拠に関心があります。寝る前に再生を押す人は、当然のように信頼できるべきです。実現には予想以上の作業が必要でしたが。
スマートフォンで試せる対処法をお探しなら、こちらのガイドをご覧ください:Androidで音が止まる問題の対処法。