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} を使用して、アクティビティの状態を完全に復元できない場合もあります。&mdash;大きなオブジェクト(ビットマップなど)を扱うためのものではなく、内部のデータをシリアル化してから逆シリアル化を行うため、多くのメモリを消費することになり、構成の変更に時間がかかってしまいます。
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    &#64;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    &#64;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    &#64;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 &lt;activity&gt;}</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&lt;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&#64;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