Androidで構成変更に対処する

前置き

Configuration Change. 直訳すると「構成変更」ですね。例えば画面の向き(screen orientation)の変化は構成変更の代表例です。他にも、ロケールの変更やフォントサイズの変更などがあります。

普通、構成変更が起きると、Activityインスタンスは再生成されます。Androidのこの挙動を知ったとき、多くの開発者は軽い衝撃を受けるのではないでしょうか。本記事では、この挙動に関連するトピックについてまとめます。

本文

Activityをシームレスに再生成させる

理想は、Activityインスタンスの再生成をユーザに気付かれないことでしょう。そのためには、再生成前のActivityの状態を引き継ぐ必要があります。このためには、Activity#onSaveInstanceState()を使います。

protected void onSaveInstanceState(Bundle outState)

このメソッドは、ActivityがRUNNINGからPAUSEDに遷移するとき、Activity#onPause()の前に呼ばれます(Activityの状態遷移参照)。引数に指定されたBundleオブジェクトに状態を保存しておきます。

Activityを再起動前の状態に戻すタイミングは2つあります。

protected void onCreate(Bundle savedInstanceState)
protected void onRestoreInstanceState(Bundle savedInstanceState)

どちらのタイミングでも、引数に指定されたBundleオブジェクトから再生成前の状態を取り出すことができます。呼ばれるタイミングが微妙に違いますが、どちらを使っても構いません。

状態をBundleに格納できない場合

Bundleに保存できる情報は、intやbooleanなどのプリミティブ型かParcelableに限られます。例えば、ユーザが編集中のイメージ、サーバと接続済みのIOオブジェクト、実行中のAsyncTaskオブジェクトなどをBundleに保存することはできません。しかし、こういったオブジェクトがActivityの再生成により失われてしまうのは避けたいところです。

このようなケースでは、Activity#onRetainNonConfigurationInstance()を使います。

public Object onRetainNonConfigurationInstance()

このメソッドはonStop()とonDestroy()の間に呼ばれます。これをオーバーライドして、Activity再生成後も使いたいオブジェクトを返すようにします。onRetainNonConfigurationInstance()が呼ばれた場合、onDestroy()の直後にActivityが再生成されることが保証されています。また、onDestroy()からActivity再生成の間に、システムから当該スレッドへのコールバックは起きません(例えば、Acitityの切り替え途中にAsyncTask#onPostExecuteが呼ばれることはない)。

onRetainNonConfigurationInstance()が返したオブジェクトは、Activity再生成後に取り出すことができます。そのタイミングはActivity#onCreate()かonStart()のみで、Activity#getLastNonConfigurationInstance()を使います。

public Object getLastNonConfigurationInstance()

この仕組みを使う場合は、メモリリークを起こさないように注意しましょう。onRetainNonConfigurationInstance()が返すオブジェクトが別のオブジェクトを(たとえ間接的にでも)参照している場合、参照されたオブジェクトたちはGCされません。特に、再生成前のActivityやContextが参照されている場合、大きなメモリリークになります。

また、この仕組みを使って、文字列やアイコンイメージのようなリソース(つまり構成に依存するオブジェクト)を退避&復帰するのは意味がない、というより不適切でしょう。

マニフェストのandroid:configChanges

android:configChangesは、マニフェストで<activity>に付ける属性です。ここで指定した構成に関しては、構成変更が起きてもActivityインスタンスは再生成されません。その代わり、アプリ側でonConfigurationChaged()をハンドリングする必要があります。

値は、以下の中から論理和(|)で選択します。

定義
mccIMSI(加入者情報)の国コード(MCC)が変わった。新しいSIMが装着された。
mncIMSI(加入者情報)のネットワークコード(MNC)が変わった。新しいSIMが装着された。
localeユーザが言語設定を変えた。
touchscreenタッチスクリーンが変わった。普通、ありえない。
keyboardキーボードが変わった。外部キーボードをつないだとか。
keyboardHiddenキーボードのaccessibilityが変わった。スライドキーボードを引き出したとか。
navigationナビゲーション形式(トラックボールやダイヤルパッド)が変わった。普通、ありえない。
orientation画面の向きが変わった。ユーザが端末方向を変えた。
screenLayout画面レイアウトが変わった。デュアルディスプレイの一方がアクティブになったとか。
fontScaleユーザが標準フォントのサイズを変えた。
uiModeユーザインターフェイスモード(API Level8で導入)が変わった。ドライブモードやナイトモードに変えた。

この表に含まれない構成の変更に関しては、Activityインスタンスの再生成を抑止することができません。また、Activityインスタンスの再生成が、構成変更以外の要因によって起きることもあります(音声着信とか)。よって、android:configChangesの使用有無に関係なく、onSaveInstanceState()などを使ってActivity再生成をシームレスに制御することはgood practiceと言えるでしょう。

Last modified:2011/07/11 17:50:41
Keyword(s):
References:[Androidアプリ開発]
This page is frozen.