-
Notifications
You must be signed in to change notification settings - Fork 354
2.02. Activity と Fragment
この章では、画面のライフサイクルの管理について解説します。
参考:Activities | Android Developers
参考:Tasks and Back Stack | Android Developers
Android の画面を作る上で重要なコンポーネントとして、下記のようなものがあります。
- Activity
- MVC の Controller に相当するものです。 画面のライフサイクルや、UI イベントの管理は Activity が受け持っています。
- Fragment
- 再利用可能な UI コンポーネントのまとまりを管理します。Activity に組み込んで使用します。 こちらも、MVC の Controller に相当する責務を持っています。 様々なデバイスをサポートする上でも重要なコンポーネントとなります。 Fragment も Activity と同様ライフサイクルを持っていますが、Activity に組み込んで使う為、Activity のライフサイクルと連動するようになっています。
- Layout・Widget
- 2.01. アプリのレイアウト作成で解説しました。
Activity にはライフサイクルが存在します。 主には、画面が呼び出されてから、必要なくなってメモリから追い出される(破棄される)までの一連の流れをライフサイクルとして扱います。 以下に上げるライフサイクルの各状態は、ライフサイクルコールバックとして、Activity クラスに同じ名前で定義されています。
- onCreate
- Activity が一番最初にとる状態です。Activity が起動し、画面を構成するまでの仕事をここで行います。XML で定義したレイアウトを読み込んだり、View コンポーネントを取り出したりする処理を実行します。
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // レイアウトを指定して、Activity がコントロールする View として扱うようにする setContentView(R.layout.activity_main); } }
- onStart(onRestart)
- Activity の画面が構築され、ユーザに見える状態です。実際にユーザが UI に触れてインタラクションを実施出来るようになるまでの仕事をここで行います。
- onResume
- Activity の画面が構築され、ユーザが UI に触れてインタラクションを実施できる状態です。
- onPause
- ユーザが Activity を離れようとしている状態です。永続化するべき情報はこのタイミングで永続化を実施します。 この状態になった Activity へユーザが戻ると、onResume の状態へと遷移します。 よって、この状態になっても、必ずこの後の状態へ順に遷移して、Activity がメモリから破棄されるとは限りません。
- onStop
- Activity がユーザから見えなくなった状態です。 この状態になった Activity にユーザが戻ろうとした場合、onRestart の状態から onStart の状態へと遷移します。
- onDestroy
- Activity がシステムによって、メモリから追い出される直前の状態です。この時点で、Activity に対するすべての参照を切っておかないと、メモリリークを起こします。 一方、システムの要求によって、アプリのプロセスごと kill された場合には、この状態になることなくアプリケーションが終了します。
Fragment にもライフサイクルが存在します。 基本的には、Activity のライフサイクルと同じ状態を持っていますが、いくつか Fragment 特有の状態も持っています。 以下に上げるライフサイクルの各状態は、ライフサイクルコールバックとして、Fragment クラスに同じ名前で定義されています。
Fragment は、Honeycomb で導入された新しいコンポーネントです。Eclipse では、android.app.Fragment と android.support.v4.app.Fragment の 2 つがコード補完によって表示されますが、2.x 系で Fragment を使用する場合は、後者のパッケージのものを利用します。
また同時に、2.x 系で Fragment を組み込んだ Activity を作成する場合は、android.app.Activity ではなく、android.support.v4.app.FragmentActivity を継承するようにします。
- onAttach
- Fragment が Activity に組み込まれた状態です。
この時点で、Fragment が Activity に対して何らかのコールバックを提供する場合、Activity が必要なインタフェースを備えているかどうかチェックしておきます。
public class MainFragment extends Fragment { private FragmentCallbacks mCallback; @Override public void onAttach(Activity activity) { super.onAttach(activity); try { mCallback = (FragmentCallbacks) activity; } catch (ClassCastException e) { // Fragment が組み込まれる先の Activity に対して、FragmentCallbacks インタフェースの実装を要求する為 // キャストに失敗した場合は、実行時例外としてプログラムのミスであることを示す throw new IllegalStateException("activity should implement FragmentCallbacks", e); } } public static interface FragmentCallbacks { public void onHogehoge(); } }
- onCreate
- Fragment を構築する状態です。Fragment がユーザに見える状態になるまで保持しておくべきコンポーネントの初期化を行います。
- onCreateView
- Fragment が持つ View を構築する状態です。XML からレイアウトを取り出し、この Fragment の View として扱うための処理を行いますが、View を持たない Fragment を作ることも可能です。
public class MainFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // LayoutInflater を利用して、レイアウトをリソースとして読み込む View view = inflater.inflate(R.layout.fragment_main, container, false); return view; } }
- onActivityCreated
- Activity の onCreate の状態の処理が終わったことを示す状態です。
- onStart
- Fragment の UI が構築され、ユーザに見える状態です。
- onResume
- Fragment の UI が構築され、ユーザとのインタラクションが出来るようになった状態です。
- onPause
- ユーザが別の画面への遷移をしようとして Fragment から離れていこうとした状態です。Activity と同じく、この時点で、永続化するべき情報を保存するようにしておきます。 ただし、この状態になったからといって、必ずしもこの後の状態へ遷移し、Fragment がメモリから破棄されるわけではありません。
- onStop
- Fragment がユーザに見えない状態です。
- onDestroyView
- Fragment が扱う View などのコンポーネントに紐付いた各種リソースを開放するための状態です。ここで Fragment への参照が View やコンポーネントに残っていると、メモリリークを起こします。 ユーザのナビゲーションや Fragment の操作等で、再びレイアウトにアタッチされる場合、onCreateView の状態へ戻ります。
- onDestroy
- Fragment が完全にメモリから破棄される直前の状態です。
- onDetach
- Fragment が Activity から切り離される状態です。
AndroidManifest は XML で記述されています。 Activity は、アプリケーションを構成するコンポーネントですので、 <application>要素の下にぶら下げる形で宣言します。
<activity>要素には、どの Activity かを示したり、Activity のラベルを示したりする属性を付与できます。 以下に、代表的な属性を示します。
<activity
android:name="string"
android:label="string"
android:exported=["true" | "false"]
android:launchMode=["multiple" | "singleTop" | "singleTask" | "singleInstance"]
android:permission="string"
android:configChanges=["mcc", "mnc", "locale", "touchscreen", "keyboard", "keyboardHidden", "navigation", "screenLayout", "fontScale", "uiMode", "orientation", "screenSize", "smallestScreenSize"]
android:screenOrientation=["unspecified" | "user" | "behind" | "landscape" | "portrait" | "reverseLandscape" | "reversePortrait" | "sensorLandscape" | "sensorPortrait" | "sensor" | "fullSensor" | "nosensor"]
android:theme="resource or theme"
android:uiOptions=["none" | "splitActionBarWhenNarrow"]
android:windowSoftInputMode=["stateUnspecified", "stateUnchanged", "stateHidden", "stateAlwaysHidden", "stateVisible", "stateAlwaysVisible", "adjustUnspecified", "adjustResize", "adjustPan"]>
</activity>
要素 | 意味 |
---|---|
android:name | Activity のクラス名。FQDN か、<application>要素で宣言したパッケージ名から後ろのパス表現で記述する |
android:label | Activity のラベル。ユーザに見えるものなので、分かりやすいものにしておく |
android:exported | 他のアプリからも呼び出すことができるようにするかどうか。デフォルトではfalseになるが、例外的に、<intent-filter>要素を子要素に持つ<activity>は、この属性がデフォルトでtrueになることに注意。特に理由がない限りは、falseを明示すること |
android:launchMode | Activity を呼び出す時の振る舞い方。タスクとバックスタックの項で説明 |
android:permission | Activity の呼び出し時に要求するパーミッション |
android:configChanges | Activity が自身で制御するコンフィギュレーション変化の一覧。複数指定が可能で、パイプで繋ぐ |
android:screenOrientation | Activity が要求する画面の向き |
android:theme | Activity に適用するテーマ |
android:uiOptions | Activity に適用する、UI にまつわる設定。現状は ActionBar の設定のみが存在している |
android:windowSoftInputMode | ソフトウェアキーボードと Activity の表示に関する調整要素。ソフトウェアキーボードを自動で表示するかどうかや、Activity の拡大・縮小についての振る舞いを決める |
アプリを起動する時、ランチャーは、当該アプリに対して、暗黙的 Intent を発行します。 この暗黙的 Intent を受け付けることの出来る Activity の宣言の例を以下に示します。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="jp.mixi.sample"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<!-- Activity に関する宣言 -->
<!-- 新しい Activity を作ったら、必ずこの宣言を追加する -->
<!-- android:name には、FQDN か、jp.mixi.sampleの続きからのパスを入れる -->
<activity
android:name="jp.mixi.sample.MainActivity"
android:label="@string/app_name" >
<!-- ランチャーから呼び出す対象とするための宣言 -->
<!-- ランチャーから呼び出さない Activity であれば、この宣言は不要 -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
<activity>要素の子要素として、<intent-filter>要素を宣言します。 この<intent-filter>要素に更に子要素として、Intent に付与される Action と Category を指定します。
ランチャーからの呼び出しでは、android.intent.action.MAIN というアクションと、android.intent.category.LAUNCHER というカテゴリが付与されますので、これを<intent-filter>要素の子要素に宣言しておきます。
Android には、タスクとバックスタックという考え方があります。 この考え方が取り扱うのは、複数の Activity の管理です。 同一アプリ内で扱う複数の Activity だけでなく、他のアプリとの連携も、タスクとバックスタックの扱う範囲です。
タスクとは、ユーザがアプリを利用して何らかの操作を行う単位です。このことから、Linux などで用いられるタスクとは異なる意味を持っています。
1つのタスクには、起動した Activity のまとまりをスタックで持つバックスタックが割当てられます。
バックスタックとは、Activity の呼び出し順を管理するスタックです。 アプリを起動すると、最初にランチャーから呼び出される Activity がバックスタックに積まれます(push)。
その Activity から新しい別の Activity を呼び出すと、その Activity がバックスタックに積まれます(push)。 新しく起動した Activity で、端末のバックキー(戻るボタン)を押すなどして前の Activity に戻ると、バックスタックに積まれていた Activity は破棄されます(pop)。
すべての Activity がバックスタックから居なくなると、アプリケーションは終了することとなります。
あるタスクのバックスタックに Activity が積まれている状態で、ユーザがホームキーで端末のホーム画面に戻った場合、タスクはバックグラウンドで待機状態となります。
この時、別のアプリを起動すると、新たな別のタスクが定義され、新しいタスクのバックスタックに起動したアプリの Activity が積まれます。 つまり、新しいタスクはフォアグラウンドで実行中の状態となります。
ここで、ユーザが再びホームキーでホーム画面に戻り、最初に利用していたアプリを起動してバックグラウンドに居たタスクをフォアグラウンドへ遷移させると、もう一方のフォアグラウンドに居たタスクがバックグラウンドへ移ります。
これが Android のマルチタスクです。
マルチタスクで多数のタスクをバックグラウンドへ移すと、その分だけメモリを消費します。メモリ残量が少なくなると、システムは、バックグラウンドに居るタスクのなかから、優先順位を見てタスクの持つ Activity の破棄を試みます。
Activity が破棄されても、破棄される前の状態に復元できるよう、Activity のライフサイクルにしたがって実装をしておくべきです。
Activity には、以下の二つのコールバックメソッドが用意されており、これらを活用して、状態の保存と復旧を行います。
- onSaveInstanceState
-
Activity が破棄される前に、状態を保存するために呼ばれるコールバックメソッドです。
引数に Bundle オブジェクト(Android 独自のコレクション・フレームワーク)が渡されるので、このオブジェクトに状態を保存します。
- onRestoreInstanceState
-
Activity が再度フォアグラウンドに遷移する際、状態を復旧するために呼ばれるコールバックメソッドです。
引数に Bundle オブジェクトが渡されます。この Bundle オブジェクトには、onSaveInstanceState() メソッドで保存した状態が入っていますので、その状態を取り出して復旧します。
Activity の onCreate() メソッドの引数にある Bundle オブジェクトも同じ役割を持っています。
同様、Fragment にも onSaveInstanceState() メソッドが定義されており、ここで状態の保存をすることができます。
一方、状態の復旧には、onCreate() や onCreateView()、onActivityCreated() メソッドの引数にある Bundle オブジェクトを利用します。
- (実習) 新しい Activity と、それに対応するレイアウトを作成してください。
- (実習) 新しく作った Activity を、アプリ起動時に表示するよう変更してください。
- (課題)
Toast#makeText(Context, int, int)
メソッドを使って、Activity のライフサイクルのコールバックメソッドが、どのような順番で実行されるかを画面に表示し、列挙してください。 - (課題) Activity が 2 つ用意されています。起動後の Activity から呼び出された Activity で、状態管理を適切に行なってください。
- (実習) 新しい Fragment と、それに対応するレイアウトを作成してください。
- (実習) 新しく作った Fragment を、既存の Activity に組み込んでください。
- (課題) 課題ディレクトリに有るプロジェクトの
activity_main.xml
から、Fragment に切り出すものを適宜選択し、Fragment 化してください。 - (課題) 課題 3 で切り出した Fragment を用いて、画面を作成してください(課題 3 と同じプロジェクトの中で実施する)。
Portions of this page are reproduced from work created and shared by the Android Open Source Project and used according to terms described in the Creative Commons 2.5 Attribution License.