1page.title=実行時の変更の処理 2page.tags=アクティビティ,ライフサイクル 3@jd:body 4 5<div id="qv-wrapper"> 6<div id="qv"> 7 8 <h2>本書の内容</h2> 9 <ol> 10 <li><a href="#RetainingAnObject">構成の変更中にオブジェクトを保持する</a></li> 11 <li><a href="#HandlingTheChange">構成の変更を自分で処理する</a> 12 </ol> 13 14 <h2>関連ドキュメント</h2> 15 <ol> 16 <li><a href="providing-resources.html">リソースの提供</a></li> 17 <li><a href="accessing-resources.html">リソースへのアクセス</a></li> 18 <li><a href="http://android-developers.blogspot.com/2009/02/faster-screen-orientation-change.html">Faster 19 Screen Orientation Change</a></li> 20 </ol> 21</div> 22</div> 23 24<p>端末の構成の中には、実行時に変化するものがあります(画面の向き、キーボードの可用性、言語など)。 25そのような変更が発生すると、Android は実行中の {@link android.app.Activity} を再起動します({@link android.app.Activity#onDestroy()} が呼び出され、その後に{@link 26android.app.Activity#onCreate(Bundle) onCreate()} が呼び出されます)。 27 28再起動の動作は、新しい端末構成に一致する代替リソースを使用してアプリケーションを自動的にリロードすることで、アプリケーションを新しい構成に適応させることを目的としています。 29 30</p> 31 32<p>再起動を適切に処理するには、アクティビティが通常の<a href="{@docRoot}guide/components/activities.html#Lifecycle">アクティビティのライフサイクル</a>を通じて事前の状態を格納することが重要です。その場合、アプリケーションの状態に関するデータを保存できるように、Android はアクティビティを破棄する前に {@link android.app.Activity#onSaveInstanceState(Bundle) onSaveInstanceState()} を呼び出します。 33 34 35 36その後、{@link android.app.Activity#onCreate(Bundle) onCreate()} か {@link 37android.app.Activity#onRestoreInstanceState(Bundle) onRestoreInstanceState()} の際に状態を格納できます。 38</p> 39 40<p>アプリケーション自体がそのままの状態で再起動することをテストするには、アプリケーションでさまざまなタスクを実行中に、構成の変更(画面の向きの変更など)を呼び出す必要があります。 41 42構成の変更を処理したり、ユーザーが電話を着信し、アプリケーション プロセスが破棄されるほど時間が経過してからアプリケーションに戻ったりするような場合には、ユーザーデータや状態を失うことなくいつでもアプリケーションを再起動できるようにする必要があります。 43 44 45アクティビティの状態を格納する方法については、「<a href="{@docRoot}guide/components/activities.html#Lifecycle">アクティビティのライフサイクル</a>」をご覧ください。</p> 46 47<p>ただし、場合によっては、アプリケーションを再起動すると、大量のデータの復元にコストがかかり、操作性が悪くなることがあります。 48そのような場合、次の 2 つの方法で処理できます。 49</p> 50 51<ol type="a"> 52 <li><a href="#RetainingAnObject">構成の変更中にオブジェクトを保持する</a> 53 <p>構成の変更時にアクティビティを再起動できますが、ステートフル オブジェクトがアクティビティの新しいインスタンスに移動します。 54</p> 55 56 </li> 57 <li><a href="#HandlingTheChange">構成の変更を自分で処理する</a> 58 <p>特定の構成変更の際に、システムがアクティビティを再起動しないようにしますが、必要に応じてアクティビティをアップデートできるように、構成が変更された場合には、コールバックを受け取ります。 59 60</p> 61 </li> 62</ol> 63 64 65<h2 id="RetainingAnObject">構成の変更中にオブジェクトを保持する</h2> 66 67<p>アクティビティの再起動で大量のデータの復元、ネットワーク接続の再構築、他の負荷のかかる操作の実行が必要になる場合、構成変更のために完全な再起動を実行すると、操作性が悪くなってしまうことがあります。 68 69さらに、システムの {@link 70android.app.Activity#onSaveInstanceState(Bundle) onSaveInstanceState()} コールバックによって保存される {@link android.os.Bundle} を使用して、アクティビティの状態を完全に復元できない場合もあります。—大きなオブジェクト(ビットマップなど)を扱うためのものではなく、内部のデータをシリアル化してから逆シリアル化を行うため、多くのメモリを消費することになり、構成の変更に時間がかかってしまいます。 71 72 73そのような場合、構成の変更によるアクティビティの再起動の際に、{@link 74android.app.Fragment} を保持することで、再初期化の負担を軽減できます。 75このフラグメントには、保持しておきたいステートフル オブジェクトへの参照を含めることができます。 76</p> 77 78<p>構成変更により Android システムがアクティビティをシャットダウンするとき、保持するためのマークを設定しておくと、アクティビティのフラグメントが破棄されません。 79ステートフル オブジェクトを保持するために、アクティビティにこのようなフラグメントを追加しておくことができます。 80</p> 81 82<p>実行時の構成変更の際に、フラグメントにステートフル オブジェクトを保持するには:</p> 83 84<ol> 85 <li>{@link android.app.Fragment} クラスを拡張し、ステートフル オブジェクトへの参照を宣言します。 86</li> 87 <li>フラグメントを作成するときに、{@link android.app.Fragment#setRetainInstance(boolean)} を呼び出します。 88 </li> 89 <li>フラグメントをアクティビティに追加します。</li> 90 <li>アクティビティの再起動時にフラグメントを取得するには、{@link android.app.FragmentManager} を使用します。 91</li> 92</ol> 93 94<p>次は、フラグメントの定義の例です。</p> 95 96<pre> 97public class RetainedFragment extends Fragment { 98 99 // data object we want to retain 100 private MyDataObject data; 101 102 // this method is only called once for this fragment 103 @Override 104 public void onCreate(Bundle savedInstanceState) { 105 super.onCreate(savedInstanceState); 106 // retain this fragment 107 setRetainInstance(true); 108 } 109 110 public void setData(MyDataObject data) { 111 this.data = data; 112 } 113 114 public MyDataObject getData() { 115 return data; 116 } 117} 118</pre> 119 120<p class="caution"><strong>警告:</strong> オブジェクトの格納が可能な間は、{@link 121android.graphics.drawable.Drawable}、{@link android.widget.Adapter}、{@link android.view.View}、さらには {@link android.content.Context} に関連付けられているその他のオブジェクトのように、{@link android.app.Activity} に結び付いたオブジェクトを渡さないようにしてください。 122 123オブジェクトを渡すと、元のアクティビティ インスタンスのビューやリソースがすべて漏えいしてしまいます 124(リソースが漏えいすると、アプリケーションはリソースを保持しているがガーベジコレクションを実行できず、大量のメモリが失われてしまうことがあります)。 125 126</p> 127 128<p>次に、{@link android.app.FragmentManager} を使用して、フラグメントをアクティビティに追加します。実行時に構成を変更する際にアクティビティを再び起動すると、フラグメントからデータ オブジェクトを取得できます。 129 130次は、アクティビティの定義の例です。</p> 131 132<pre> 133public class MyActivity extends Activity { 134 135 private RetainedFragment dataFragment; 136 137 @Override 138 public void onCreate(Bundle savedInstanceState) { 139 super.onCreate(savedInstanceState); 140 setContentView(R.layout.main); 141 142 // find the retained fragment on activity restarts 143 FragmentManager fm = getFragmentManager(); 144 dataFragment = (DataFragment) fm.findFragmentByTag(“data”); 145 146 // create the fragment and data the first time 147 if (dataFragment == null) { 148 // add the fragment 149 dataFragment = new DataFragment(); 150 fm.beginTransaction().add(dataFragment, “data”).commit(); 151 // load the data from the web 152 dataFragment.setData(loadMyData()); 153 } 154 155 // the data is available in dataFragment.getData() 156 ... 157 } 158 159 @Override 160 public void onDestroy() { 161 super.onDestroy(); 162 // store the data in the fragment 163 dataFragment.setData(collectMyLoadedData()); 164 } 165} 166</pre> 167 168<p>この例では、{@link android.app.Activity#onCreate(Bundle) onCreate()} がフラグメントを追加するか、フラグメントへの参照を復元します。さらに、{@link android.app.Activity#onCreate(Bundle) onCreate()} がステートフル オブジェクトをフラグメント インスタンスの内部に格納し、{@link android.app.Activity#onDestroy() onDestroy()} が保持しているフラグメント インスタンス内部のステートフル オブジェクトを更新します。 169 170 171 172</p> 173 174 175 176 177 178<h2 id="HandlingTheChange">構成の変更を自分で処理する</h2> 179 180<p>アプリケーションに特定の構成の変更の際にリソースを更新する必要がなく、パフォーマンスの制限によりアクティビティの再起動を回避する必要がある場合は、構成の変更をアクティビティ自身が処理することを宣言します。そうすることで、システムによってアクティビティが再起動されなくなります。 181<em></em> 182 183</p> 184 185<p class="note"><strong>注:</strong> 構成の変更を自分で処理すると、変更がシステムによって自動的に適用されることがないため、代替リソースの使用が非常に難しくなります。 186 187この手法は、構成の変更による再起動を回避する際の最後の手段として検討する手法です。ほとんどの場合、アプリケーションでの使用は推奨されません。 188</p> 189 190<p>アクティビティで構成の変更を処理することを宣言するには、マニフェスト ファイルの該当する <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> 要素を編集し、処理する構成を表す値とともに <a href="{@docRoot}guide/topics/manifest/activity-element.html#config">{@code 191android:configChanges}</a> 属性を配置します。 192 193使用可能な値は、<a href="{@docRoot}guide/topics/manifest/activity-element.html#config">{@code 194android:configChanges}</a> 属性のドキュメントに一覧が記載されています(一般的には、画面の向きを変更した場合の再起動を回避するには {@code "orientation"} の値を、キーボードの可用性を変更した場合の再起動を回避するには {@code "keyboardHidden"} の値を使用します)。 195 196パイプ記号の {@code |} 文字を使用して区切ることで、属性内に複数の構成値を宣言できます。 197</p> 198 199<p>たとえば、次のマニフェスト コードでは、画面の向きとキーボードの可用性の変更の両方を処理するアクティビティを宣言しています。 200</p> 201 202<pre> 203<activity android:name=".MyActivity" 204 android:configChanges="orientation|keyboardHidden" 205 android:label="@string/app_name"> 206</pre> 207 208<p>これで、これらのいずれかの構成が変更された場合でも、{@code MyActivity} が再起動することはなくなりました。代わりに、{@code MyActivity} が {@link 209android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()} への呼び出しを取得します。 210このメソッドには、新しい端末構成を指定する {@link android.content.res.Configuration} オブジェクトが渡されます。 211 212{@link android.content.res.Configuration} のフィールドを読み込むことで新しい構成を判断し、インターフェースに使用するリソースを更新して適切に変更できます。 213 214このメソッドが呼び出されると、アクティビティの {@link android.content.res.Resources} オブジェクトが更新され、新しい構成に基づいたリソースを返します。それにより、システムがアクティビティを再起動しなくても、UI の要素を簡単にリセットできます。 215 216 217</p> 218 219<p class="caution"><strong>警告:</strong> Android 3.2(API レベル 13)以降では、端末の画面の向きを縦から横に切り替えると、<strong>「画面サイズ」も変更されます</strong>。 220 221そのため、API レベル 13 以降で開発を行う場合(<a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code minSdkVersion}</a> 属性と <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code targetSdkVersion}</a> 属性により宣言)、画面の向きによる実行時の再起動を回避するには、{@code "screenSize"} 値と一緒に {@code 222"orientation"} 値を使用する必要があります。 223 224つまり、{@code 225android:configChanges="orientation|screenSize"} を宣言する必要があります。ただし、アプリケーションのターゲットが API レベル 12 以前の場合は、常にアクティビティ自身がこの構成の変更を処理します(Android 3.2 以降の端末で実行している場合でも、この構成の変更によりアクティビティが再起動されることはありません)。 226 227</p> 228 229<p>たとえば、次の {@link 230android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()} の実装により現在の端末の画面の向きがチェックされます。 231</p> 232 233<pre> 234@Override 235public void onConfigurationChanged(Configuration newConfig) { 236 super.onConfigurationChanged(newConfig); 237 238 // Checks the orientation of the screen 239 if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { 240 Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show(); 241 } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){ 242 Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show(); 243 } 244} 245</pre> 246 247<p>{@link android.content.res.Configuration} オブジェクトは、変更された構成を除く、現在のすべての構成を表します。 248ほとんどの場合、構成の変更内容を正確に把握する必要はなく、代替の構成を提供するすべてのリソースを処理中の構成に割り当て直します。 249 250たとえば、現在は {@link 251android.content.res.Resources} オブジェクトが更新されたため、任意の {@link android.widget.ImageView} を {@link android.widget.ImageView#setImageResource(int) 252setImageResource()} でリセットでき、新しい構成には最適なリソースが使用されます(「<a href="providing-resources.html#AlternateResources">リソースの提供</a>」をご覧ください)。 253 254</p> 255 256<p>{@link 257android.content.res.Configuration} フィールドの値は、{@link android.content.res.Configuration} クラスの特定の定数に一致する整数であることにご注意ください。 258それぞれのフィールドで使用する定数に関するドキュメントについては、{@link 259android.content.res.Configuration} リファレンスの該当するフィールドをご覧ください。 260</p> 261 262<p class="note"><strong>メモ:</strong> 構成の変更をアクティビティで処理することを宣言すると、代替を提供するすべての要素をリセットする操作が必要になります。 263画面の向きの変更を処理するアクティビティを宣言しており、縦向きと横向きで切り替わる画像がある場合、{@link 264android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()} の際にそれぞれの要素に各リソースを割り当て直す必要があります。 265 266</p> 267 268<p>これらの構成の変更に基づいてアプリケーションを更新する必要がない場合は、代わりに <em></em>{@link 269android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()} を実装できません。 270このような場合、構成の変更前に使用していたすべてのリソースがそのまま使用され、アクティビティの再起動を回避した状態となります。 271 272ただし、アプリケーションはいつでもシャットダウンして以前のままの状態で再起動できる状態にしておく必要があるため、通常のアクティビティのライフサイクルでは、状態を保持しない手段としてこの手法を使用しないようにします。 273 274それは、アプリケーションの再起動を回避できない他の構成の変更があるだけでなく、ユーザーがアプリケーションを離れ、ユーザーがアプリケーションに戻る前に破棄されてしまうようなイベントを処理する必要があるためです。 275 276 277</p> 278 279<p>アクティビティで処理できる構成の変更についての詳細は、<a href="{@docRoot}guide/topics/manifest/activity-element.html#config">{@code 280android:configChanges}</a> のドキュメントと{@link android.content.res.Configuration} クラスをご覧ください。 281</p> 282