Android面接の質問:画面再作成とプロセス終了で状態を検証する
Quick Overview
Android面接の質問をチケット画面の状態で練習。ViewModelの保持とSavedStateHandleからの復元を区別し、E41・B8・H7が戻っても予約を再確認する判断を説明します。Robolectricの3テスト23項目と、端末上で未実施のプロセス終了検証を分けます。
Android面接で「画面を回転しても入力が残りますか」と聞かれたら、残る値だけでなく、どのオブジェクトが残り、どれが作り直されるかまで説明してください。回転で残った状態が、プロセス終了後にも残るとは限りません。さらに、保存した選択が復元されても、外部の予約や価格がまだ有効とは限りません。
この記事では、公演E41の座席B8を選び、仮押さえH7を持つチケット購入画面を考えます。画面再作成、保存状態からの復元、別公演への移動を区別し、戻った画面で何を再確認するかを決めます。Design Android MVVM API Architectureを練習先にすると、状態の所有者から回答を組み立てられます。
証拠の区分: Android Developersの説明を公式の仕様・設計指針として参照します。Qiitaの2022年の個人報告は、候補者一人の経験として扱い、現在の企業共通の選考手順には広げません。本稿の公演、座席、仮押さえ、時刻は架空です。Robolectricで期待値を確かめた23項目の確認と、Android端末上でまだ実行していないプロセス終了の検証を分けて示します。

画面再作成とプロセス終了では、残るものが違う
公式説明: AndroidのSave UI statesは、ViewModel、保存状態、永続ストレージを組み合わせる考え方を示しています。適切なスコープのViewModelは構成変更をまたいで残りますが、システムによるプロセス終了をまたいで同じオブジェクトが残るわけではありません。保存状態は、後で新しいオブジェクトへ値を復元するために使います。
この違いを、次のように説明できます。「回転ではActivityが再作成されても、同じ所有範囲のViewModelを引き継げます。プロセスが終われば、そのメモリ上のViewModelも失われます。戻ったときは公演や座席の識別子を保存状態から読み、必要なデータを再取得します」。
ここでいう所有範囲も確認してください。Activityに属するのか、ナビゲーション先に属するのかで、画面を離れたときの寿命が変わります。同じクラスを使っているだけでは、同じViewModelを共有していることにはなりません。
公式説明: Processes and app lifecycleは、プロセスの寿命をシステムが管理し、システムがプロセスを終了する場合にonDestroy()が呼ばれる保証はないと説明しています。重要なデータを最後に保存する場所として、onDestroy()だけに依存する回答は避けます。
チケット画面の値を、寿命と正しさで分類する
今回の画面では、「座席B8を選んでいた」という利用者の意図と、「B8が今も確保されている」という予約の状態を分けます。前者は復元したい入力、後者は予約側に確認すべき情報です。
| 値 | このケースでの置き場所・扱い | 復元後に確認すること |
|---|---|---|
| 公演ID E41 | 小さな保存状態と、現在開く公演の識別 | 保存した公演と現在の表示先が一致するか |
| 選択した座席ID B8 | ViewModel内の状態と保存状態 | 現在の公演に属する座席か |
| 仮押さえID H7 | 小さな保存状態に持つ参照 | 予約側で存在、有効期限、対応する公演・座席を再確認 |
| 価格・空席状況 | データ層から取得する情報 | 最新の条件と一致するか。保存した表示だけを確定値にしない |
| 読み込み中フラグ | 実行中の処理に対応するメモリ状態 | 新しい処理を始めるか。古いtrueをそのまま復元しない |
| 購入・支払いの結果 | 予約側の処理結果と操作の識別 | 成功を確認できるか。画面復元を成功の証拠にしない |
ケースの設計判断: 保存状態には、画面を組み立て直す最小限の識別子を入れます。仮押さえの有効性や購入結果を、端末が保存した真偽値だけで決めません。復元後は一度「未確認」に戻し、確認結果に応じて次へ進む操作を有効にします。
保存状態を永続的な購入記録の代わりにも使いません。画面やタスクがなくなっても必要な記録は、用途に応じた永続ストレージやサービス側で管理します。この練習には、そのような注文保存や決済の実装は含めていません。
ViewModelが残ることと、確認済みであることを分ける
ローカルの例はJavaのComponentActivityで書き、AndroidXのViewModelProviderとSavedStateHandleを使いました。画面レイアウトや実際の購入APIは省き、Activity・ViewModel・保存状態の関係を検証しています。Composeの描画テストではありません。
Activityの生成時には、現在の表示先を取り出してViewModelへ渡します。次は実行したクラスの抜粋です。TicketVmはSavedStateHandleを受け取るViewModelで、後述の検証用コードに含まれます。
@Override public void onCreate(Bundle saved) {
super.onCreate(saved);
vm = new ViewModelProvider(this).get(TicketVm.class);
String current = getIntent().getStringExtra("eventId");
vm.bindEvent(current == null ? "E41" : current);
}
bindEventでは、保存した公演と現在の表示先が違えば、古い座席と仮押さえを取り除きます。同じ公演なら識別子は残しますが、購入手続きへ進んでよいかは未確認に戻します。ここで購入手続きへ進む可否は、この練習の画面操作の条件です。保存した許可をそのまま使うと、期限切れの仮押さえでも進めてしまう可能性があります。サービス側の購入許可や在庫確保を保証するフラグではありません。
public void bindEvent(String current) {
if (!current.equals(eventId())) {
saved.remove("seatId"); saved.remove("holdId");
}
saved.set("eventId", current);
canContinue = false; notice = "not_checked";
}
この例では、公演IDが必ず渡されることを前提にし、ない場合だけE41を練習上の初期値にしています。実際のアプリで任意の公演へ黙って置き換える設計を勧めるものではありません。無効なリンクや削除された公演の扱いは、別に定義する必要があります。
公式説明: Saved State module for ViewModelは、SavedStateHandleを保存状態へ値を書き込み、取得する仕組みとして説明しています。通常のActivityのファクトリーと所有者を使う構成なら、この仕組みとの接続が行われます。単体でSavedStateHandleを作って値が読めただけでは、Activityの保存・復元まで検証したことにはなりません。
同じ座席が表示されても、仮押さえは期限切れになり得る
検証用の予約データは、E41 / B8 / H7で、有効期限を架空の時刻300としました。時刻299なら期限内、300以降なら期限切れという契約です。時刻301に画面が戻った場合、B8という選択は表示できても、H7が有効だとは判断できません。
検証用の確認処理は、まずcanContinueをfalseにし、仮押さえの存在、公演と座席の対応、有効期限を順に見ます。存在しなければmissing、対応が違えばmismatch、期限切れならexpiredです。通信できない状況を模したデータ源では、offlineとして次へ進めない状態にします。
| 入力と確認条件 | この練習の期待結果 | 表示・操作の意味 |
|---|---|---|
| H7、公演E41、座席B8、時刻299 | valid、canContinue=true | この確認条件では次の手続きへ進める |
| 同じ識別子、時刻300 | expired、false | 期限ちょうども無効というケースの契約 |
| 同じ識別子、時刻301 | expired、false | 保存したB8を有効な予約と取り違えない |
| H7に対して選択がB9 | mismatch、false | 別の座席へ仮押さえを流用しない |
| 前回は有効、今回の確認が通信不能 | offline、false | 前回の許可を残して進ませない |
| 別公演E42を開いた | B8とH7を破棄 | 現在の表示先へ古い選択を復元しない |
これらのデータ源は、メモリ上の検証用オブジェクトです。ネットワーク要求、サービス側の時計、予約競合、支払いを実行した結果ではありません。実運用では、有効期限の判定を端末の時計だけに委ねず、予約側の契約に合わせて確認します。
さらに、確認で有効と返った直後に状態が変わる可能性もあります。画面のフラグは予約処理そのものを原子的に保証しません。購入を確定する段階では、サービス側で条件を検証する必要があります。再表示のたびに購入処理を自動で再送する、といった副作用も、この復元処理には持たせていません。
23項目のローカル確認で、何を観測したか
Robolectric 4.17、Android API 34を対象にした実装、AndroidX Activity 1.10.1、Lifecycle 2.6.1、Java 17を使い、3つのテストで23項目のアサーションを実行し、すべて期待結果と一致しました。バージョンはこの検証環境の固定値で、最新構成の推奨一覧ではありません。
第一のテストではActivityを再作成し、Activityが別の個体になること、ViewModelが同じ個体であることを確認しました。Activityだけのフィールドは初期値へ戻り、ViewModel内の座席と読み込み中フラグは残りました。このフラグは検証で立てた真偽値であり、非同期処理が継続した証拠ではありません。一方、画面を作るときに実行する本稿のbindEventにより、確認済みの可否はリセットします。
第二のテストでは、Activityの保存コールバックで得たBundleをParcelへ書き出し、バイト列から読み戻しました。元の所有者を破棄し、新しいActivityへそのBundleを渡すと、別のViewModelにE41・B8・H7が復元されました。読み込み中フラグと購入手続きへ進む可否は初期値でした。その後、期限・通信不能などの確認処理を実行しています。
第三のテストでは、E41の保存状態を持ったままE42を表示先として新しく生成しました。現在の公演が優先され、古い座席と仮押さえが除かれることを確認しました。これは、値を復元できることに加え、復元してよい値かを判断する練習です。

実行証拠の限界: この検証はJVM上のRobolectricで行いました。AndroidのOSが実際にアプリのプロセスを終了させた結果ではありません。保存コールバックとParcelの往復、新しい所有者の作成を制御して、復元の経路を確かめています。ライブラリーの識別用リソース定数を補った、レイアウトを持たないテスト環境であり、Androidのリソース解決や実際の描画も検証対象外です。
RobolectricのActivityControllerの説明も、低水準のライフサイクル制御では呼び出し順に注意が必要で、高水準のActivityScenarioを検討するよう示しています。したがって「このテストが通ったので、すべての端末でプロセス終了後も復元できる」とは言えません。
戻る・タスク終了・保存のタイミングを同じ扱いにしない
画面から戻る操作が、単なる前面の切り替えなのか、対象のActivityやナビゲーション先を終了するのかを確認してください。状態の所有者が残る場合と、所有者自体を取り除く場合では、期待する寿命が違います。ライブラリー名だけで「Backなら必ず残る」と答えないようにします。
公式説明: SavedStateHandleの保存状態はタスクスタックと結びついており、強制停止、履歴からのタスク除去、再起動などでその状態を復元できる前提にはできません。システムが背景のプロセスを終了したあと、保存されたタスクへ戻る経路と区別します。必要な注文記録があるなら、この一時的な保存状態に依存させません。
また、保存用の値へ書けば即座にあらゆる終了から守られる、という説明も不正確です。公式の保存状態の説明には、Activityの停止に関わる保存タイミングと、停止中に書いた値を後で保存する条件が記載されています。停止中の更新まで今回の23項目で検証したわけではないため、実装するアプリで追加確認します。
Composeで実装する場合は、画面内部の状態と業務判断に使う状態を分け、Save UI state in Composeの指針に合わせます。rememberだけの状態、保存対象の状態、ViewModelが持つ状態は同じ寿命ではありません。rememberSaveableなどの保存APIで値を戻せても、仮押さえの有効性まで保証されるわけではありません。本稿のJavaテストを、Composeの復元テストと読み替えないでください。
端末でプロセス終了を検証するなら、値と個体を記録する
次は、実アプリとエミュレーターまたは端末で行う追加計画です。本稿では未実施です。回転の確認、背景でのプロセス終了、ユーザーがタスクを終了する操作を別々に試し、同じ「再起動」という名前でまとめません。
まずE41でB8を選び、ActivityとViewModelの識別用ログ、プロセスID、保存した識別子、仮押さえの確認結果を記録します。回転後にはActivityが再生成されたか、想定したViewModelが引き継がれたか、選択値がどう表示されたかを確認します。入力が残ったという見た目だけで、所有者の寿命を推測しません。
次にアプリを背景へ移し、停止と保存の経路を記録したうえで、タスクを除かずにプロセスを終了させる検証を行います。Android Debug Bridgeの公式資料はActivity Managerの操作を説明しています。背景プロセスの終了に使う操作と強制停止を取り違えず、操作後に実際にプロセスがいなくなったことを確認します。デバッガーの接続などで検証条件が変わる点も記録します。
保存されたタスクから戻り、新しいプロセス・Activity・ViewModelで識別子が復元されたかを確認します。H7を検証用予約側で期限切れにしておけば、B8が表示されても次へ進めないことを確かめられます。さらに、通信不能、公演変更、タスク除去後の新規起動を別のケースとして扱います。
合格条件は「値が全部残った」ではありません。残す意図は戻り、古い予約は未確認から再評価され、別公演の値は混ざらず、終了させた画面の一時状態へ不用意に依存しないことです。端末のAPIレベル、ライブラリー、操作、ログと期待値を残せば、相手も同じ操作をたどって、復元に失敗した経路を確かめられます。
面接では、保存場所と再確認の契約を一緒に答える
短い回答なら、次のようにまとめられます。「公演と座席、仮押さえの識別子は小さな保存状態に入れます。構成変更では適切なスコープのViewModelを引き継ぎ、プロセス終了後は別のViewModelへ値を戻します。戻ったH7は成功した予約の証拠ではないので、期限と公演・座席を確認するまで次へ進めません。画面再作成のテストと、実際のプロセス終了のテストは分けます」。
候補者報告の範囲: Qiitaの「エンジニア面接 質問集」は、2022年の個人経験として、ViewModel、非同期処理、テストに関する質問を記載しています。この情報は準備する観点の参考にはなりますが、現在の応募先で同じ質問が出ることや、今回のチケットケースの出題を裏付けません。募集要件や実際の選考形式は応募先に確認してください。
| PracHubの問題 | このケースで追加する問い |
|---|---|
| Design Android MVVM API Architecture | E41・B8・H7は誰が所有し、どのデータを再取得するか |
| Build a Compose Rating Card | 保存する入力と、保存しない実行中フラグをどう分けるか |
| Jetpack Compose Auto-Scrolling List with a Start/Stop Toggle and Hex Color Switching | 画面の寿命と、実行中の処理を再開する判断をどう分けるか |
| Design Ticket Booking Auto Release | 復元したH7が期限切れなら何を表示し、何を許可しないか |
| Design a Ticket Booking System for High-Demand On-Sales Without Overselling | 画面の選択保存とサービス側の在庫確保をどう区別するか |
後半の2問は予約システムの設計練習であり、Androidのライフサイクルそのものの問題ではありません。次はDesign Android MVVM API Architectureで、回転、保存状態からの復元、期限切れという三つの条件について、残す値と次へ進める可否の期待値を先に書いてみてください。どの結果がローカルテストで確認でき、どれが端末や予約側の検証を必要とするかまで説明できます。
Sources and Further Reading
- Android Developers — Save UI states:ViewModel、保存状態、永続ストレージの役割と保存のタイミング。
- Android Developers — Saved State module for ViewModel:SavedStateHandleとタスクに結びつく保存状態。
- Android Developers — Processes and app lifecycle:システムによるプロセス管理と終了時のコールバックの限界。
- Android Developers — Save UI state in Compose:Composeでの状態保存。本稿のローカルテストはCompose描画の検証ではありません。
- Android Developers — Android Debug Bridge:追加の端末検証で使うActivity Manager等の操作資料。
- Robolectric — ActivityController:制御されたActivityのライフサイクルテストと、その精度上の注意。
- Qiita — エンジニア面接 質問集:2022年の一人の候補者による経験。現在の一般的な選考手順の証拠ではありません。
Comments (0)