page.title=開発の基礎 @jd:body

主なクラス

  1. {@link android.app.Activity}
  2. {@link android.app.Service}
  3. {@link android.content.BroadcastReceiver}
  4. {@link android.content.ContentProvider}
  5. {@link android.content.Intent}

このドキュメントの内容

  1. アプリケーションのコンポーネント
    1. コンポーネントのアクティブ化: インテント
    2. コンポーネントの終了
    3. マニフェスト ファイル
    4. インテント フィルタ
  2. アクティビティとタスク
    1. 親和性と新しいタスク
    2. 起動モード
    3. スタックのクリア
    4. タスクの開始
  3. プロセスとスレッド
    1. プロセス
    2. スレッド
    3. リモート プロシージャ コール
    4. スレッドセーフなメソッド
  4. コンポーネントのライフサイクル
    1. アクティビティのライフサイクル
    2. サービスのライフサイクル
    3. ブロードキャスト レシーバのライフサイクル
    4. プロセスとライフサイクル

Android アプリケーションはすべて Java プログラミング言語で記述します。コンパイル済みの Java コード(およびそのアプリケーションに必要なすべてのデータやリソース ファイル)は、aapt ツールを使用して Android パッケージにバンドルします。Android パッケージは、拡張子が {@code .apk} のアーカイブ ファイルです。ユーザーは、このファイルをデバイスにダウンロードして利用します。つまり、Android パッケージは、アプリケーションをモバイル デバイスに配布およびインストールするための媒体として機能します。1 つの {@code .apk} ファイルに含まれているすべてのコードが、1 つのアプリケーションと見なされます。

各 Android アプリケーションは、以下に示すさまざまな方法で他のアプリケーションから隔離されています:

2 つのアプリケーションで同じユーザー ID を共有することもできます。その場合は、それぞれのアプリケーションのファイルを相互に認識できます。システム リソースを節約するため、同じ ID のアプリケーションで同じ VM を共有し、同じ Linux プロセスで実行することも可能です。

アプリケーションのコンポーネント

Android の大きな特長の 1 つは、許可されていれば、あるアプリケーションから別のアプリケーションの要素を利用できる点です。たとえば、開発中のアプリケーションで画像の一覧をスクロール表示したい場合、他のアプリケーションで開発済みの適切なスクローラがあり、その利用が許可されていれば、独自に開発しなくてもそのスクローラを利用できます。アプリケーションに他のアプリケーションのコードを組み込んだり、リンクを設定したりする必要はありません。必要になった時点で、他のアプリケーションの一部分を開始するだけです。

この仕組みが機能するには、アプリケーション プロセスの一部分を必要に応じて開始でき、その部分の Java オブジェクトをインスタンス化できなくてはなりません。そのため、Android アプリケーションには、他のシステムで動作するアプリケーションでよく使用されるような、アプリケーション全体にアクセスするための単一のエントリ ポイント(たとえば {@code main()} 関数)はありません。代わりに、システムが必要に応じてインスタンス化して実行できるコンポーネントで構成されます。コンポーネントには以下の 4 つのタイプがあります:

アクティビティ
アクティビティは、ユーザーが 1 つの操作を集中的に行うための視覚的なユーザー インターフェースを表します。たとえば、ユーザーが選択できるメニュー アイテムの一覧を表示するアクティビティや、写真をキャプション付きで表示するアクティビティなどが考えられます。SMS アプリケーションなら、あるアクティビティでメッセージを送信する連絡先の一覧を表示し、別のアクティビティで選択した連絡先へのメッセージを入力し、その他のアクティビティで古いメッセージを参照したり設定を変更したりできます。これらのアクティビティを組み合わせて全体としてのユーザー インターフェースを形成しますが、それぞれのアクティビティは相互に独立しています。各アクティビティは、{@link android.app.Activity} 基本クラスのサブクラスとして実装されます。

アプリケーションは、1 つのアクティビティで構成することも、上記のSMS アプリケーションのように複数のアクティビティで構成することもできます。どのようなアクティビティがいくつ必要になるかは、アプリケーションやその設計に応じて異なります。通常は、アクティビティのうちのいずれかを最初のアクティビティとして指定し、ユーザーがアプリケーションを起動したときに表示します。あるアクティビティから別のアクティビティに移動するには、現在のアクティビティから次のアクティビティを開始します。

各アクティビティには、それを表示するためのデフォルトのウィンドウが割り当てられます。通常はウィンドウを画面全体に表示しますが、画面より小さいウィンドウを他のウィンドウの前面に表示することもできます。アクティビティに、新たなウィンドウを追加することも可能です。たとえば、アクティビティの途中でユーザーの応答を要求するポップアップ ダイアログを表示したり、ユーザーが画面上の特定のアイテムを選択したときに別ウィンドウで重要な情報を表示したりできます。

ウィンドウの視覚的なコンテンツは、ビュー({@link android.view.View} 基本クラスの派生オブジェクト)の階層として提供されます。各ビューは、ウィンドウ内の特定の矩形領域を制御します。親ビューは、その子となるビューで構成され、それらの子ビューのレイアウトを決定します。リーフ ビュー(階層の最下位に位置するビュー)は、そのビューが制御する矩形領域に表示され、その領域でのユーザーのアクションに対して応答します。つまり、ビューはアクティビティとユーザーが対話する場所です。たとえば、ビューに小さな画像を表示し、ユーザーがその画像をタップしたら何らかのアクションを開始することもできます。Android には、ボタン、テキスト フィールド、スクロール バー、メニュー アイテム、チェックボックスなど、さまざまなビューがあらかじめ用意されています。

ビューの階層は、{@link android.app.Activity#setContentView Activity.setContentView()} メソッドを使用してアクティビティのウィンドウ内に配置します。コンテンツ ビューは、階層のルートとなる View オブジェクトです(ビューおよびその階層について詳しくはUser Interface のドキュメントをご覧ください)。

サービス
サービスは、視覚的なユーザー インターフェースを持たず、バックグラウンドにおいて明確な終了期限がなくで実行されます。たとえば、ユーザーが他の操作をしている間 BGM を再生するサービス、ネットワーク経由でデータをフェッチするサービス、何かを計算してその結果をアクティビティに提供するサービスなどが考えられます。各サービスは、{@link android.app.Service} 基本クラスの拡張です。

典型的な例としては、プレイリストの曲を再生するメディア プレーヤーが挙げられます。プレーヤー アプリケーションは、ユーザーが曲を選んで再生するための 1 つ以上のアクティビティで構成することが予想されますが、ユーザーはプレーヤーを離れて別の操作に移った後も曲を聞いていたいと考えられることから、曲の再生自体をアクティビティで処理するわけにはいきません。音楽の再生を続けるには、メディア プレーヤー アクティビティから、バックグラウンドで実行するサービスを開始します。音楽再生サービスは、それを開始したアクティビティが画面上に見えなくなった後もそのまま実行されます。

また、実行中のサービスに接続(バインド)することもできます(実行されていない場合はそのサービスを開始することも可能です)。接続中は、サービスが公開しているインターフェースを使ってサービスと対話できます。音楽再生サービスであれは、このインターフェースを使って一時停止、巻き戻し、停止、再生の再開などの操作を実行できるようにします。

アクティビティや他のコンポーネントと同様に、サービスもアプリケーション プロセスのメイン スレッドで実行します。したがって、サービスによって他のコンポーネントやユーザー インターフェースの実行を妨げられることはなく、時間がかかるタスク(たとえば曲の再生)については、通常は別のスレッドを生成して処理します。詳しくは、プロセスとスレッドをご覧ください。

ブロードキャスト レシーバ
ブロードキャスト レシーバは、ブロードキャストの連絡を受信してそれに対処するだけのコンポーネントです。ブロードキャストの多くが元々はシステム コードで、たとえばタイム ゾーンが変更されたこと、電池の残量が少なくなったこと、写真が撮影されたこと、ユーザーが言語設定を変更したことなどを連絡するために使用します。アプリケーションでも、たとえば何らかのデータがデバイスにダウンロードされて利用できるようになったことを、他のアプリケーションにブロードキャストで知らせることができます。

アプリケーションでは、重要と思われるすべての連絡に応答できるよう、ブロードキャスト レシーバをいくつでも設定できます。すべてのレシーバは、{@link android.content.BroadcastReceiver} 基本クラスの拡張です。

ブロードキャスト レシーバがユーザー インターフェースを表示することはありません。ただし、受信した情報への応答としてアクティビティを開始したり、{@link android.app.NotificationManager} を使用してユーザーにアラートを送信したりすることはあります。通知の際には、バックライトを点滅させる、バイブレーションを起動する、音を鳴らすなど、さまざまな方法でユーザーの注意を喚起できます。通常は、ステータス バーに永続アイコンを表示し、ユーザーがこれを開いてメッセージを取得できるようにします。

コンテンツ プロバイダ
コンテンツ プロバイダは、アプリケーションのデータを他のアプリケーションでも利用できるようにします。データは、ファイル システムや SQLite データベースなど、一般に利用できる方法で格納されていれば使用できます。コンテンツ プロバイダは、{@link android.content.ContentProvider} 基本クラスの拡張です。プロバイダが制御する型のデータを、他のアプリケーションから取得および格納するための標準メソッド セットを実装しています。ただし、これらのメソッドをアプリケーションから直接呼び出すことはできません。代わりに、{@link android.content.ContentResolver} オブジェクトのメソッドを呼び出します。ContentResolver は、すべてのプロバイダと通信でき、プロバイダと連携して関係のあるすべてのプロセス間通信を管理します。

コンテンツ プロバイダの使用方法について詳しくは、Content Providersのドキュメントをご覧ください。

Android では、特定のコンポーネントで処理すべきリクエストがあると、そのコンポーネントのアプリケーション プロセスが実行中かどうかを確認(必要に応じてプロセスを開始)し、そのコンポーネントの適切なインスタンスが利用可能かどうかを確認(必要に応じてインスタンスを作成)します。

コンポーネントのアクティブ化: インテント

コンテンツ プロバイダは、ContentResolver からのリクエストの対象になるとアクティブ化されます。それ以外の 3 つのコンポーネント(アクティビティ、サービス、ブロードキャスト レシーバ)は、インテントと呼ばれる非同期メッセージによってアクティブ化されます。インテントは、メッセージのコンテンツを保持する {@link android.content.Intent} オブジェクトです。アクティビティやサービスの場合の Intent オブジェクトの主な役割は、リクエストされているアクションを指名し、その対象となるデータの URI を指定することです。たとえば、ユーザーに画像を表示するためのリクエストや、ユーザーにテキストを編集させるリクエストをアクティビティに伝達できます。ブロードキャスト レシーバの場合は、Intent オブジェクトがこれから通知を行うアクションを指名します。たとえば、カメラのボタンが押されたことを、関係のあるブロードキャスト レシーバに通知できます。

以下に示すように、コンポーネントのタイプごとに別々のアクティブ化メソッドが用意されています:

インテント メッセージについて詳しくは、Intents and Intent Filters をご覧ください。

コンポーネントの終了

コンテンツ プロバイダは、ContentResolver からのリクエストに応答している間のみアクティブになります。ブロードキャスト レシーバは、ブロードキャスト メッセージに応答している間のみアクティブになります。つまり、これらのコンポーネントを明示的に終了させる必要はありません。

一方、アクティビティはユーザー インターフェースを提供します。長い時間をかけてユーザーと会話するためのものであり、待機状態の間も、会話が続いてきる限りはアクティブなままになっている可能性があります。同様に、サービスも長い間実行されたままになる可能性があります。Android には、アクティビティとサービスを以下のような規則的な方法で終了させるためのメソッドが用意されています:

コンポーネントが、既に利用されていない場合や、Android がよりアクティブな他のコンポーネントにメモリを割り当てる必要がある場合は、システムがコンポーネントを終了させることもあります。このような状況およびその影響については、コンポーネントのライフサイクルで詳しく説明します。

マニフェスト ファイル

アプリケーション コンポーネントを開始するには、Android がそのコンポーネントの存在を認識している必要があります。アプリケーションのコンポーネントは、マニフェスト ファイルで宣言します。このファイルは、アプリケーションのコード、ファイル、リソースなどとともに Android パッケージ({@code .apk} ファイル)にバンドルされます。

マニフェストは構造化された XML ファイルで、どのアプリケーションでも常に AndroidManifest.xml という名前になります。アプリケーション コンポーネントの宣言以外にも、アプリケーションをリンクさせる必要のあるライブラリ(デフォルトの Android ライブラリを除く)の指定や、アプリケーションに付与されるべき権限の指定などにも使用します。

しかし、マニフェストの最も重要な役割は、アプリケーションのコンポーネントに関する情報を Android に提供することです。たとえば、アクティビティを次のように宣言できます:

<?xml version="1.0" encoding="utf-8"?>
<manifest . . . >
    <application . . . >
        <activity android:name="com.example.project.FreneticActivity"
                  android:icon="@drawable/small_pic.png"
                  android:label="@string/freneticLabel" 
                  . . .  >
        </activity>
        . . .
    </application>
</manifest>

<activity> 要素の {@code name} 属性は、そのアクティビティを実装する {@link android.app.Activity} サブクラスを指名します。{@code icon} および {@code label} 属性には、ユーザーに対して表示するアイコンやラベルが保持されているリソース ファイルを指定します。

その他のコンポーネントも、サービスは <service> 要素、ブロードキャスト レシーバは <receiver> 要素、コンテンツ プロバイダは <provider> 要素を使用して同じような方法で宣言します。マニフェストに宣言されていないアクティビティ、サービス、およびコンテンツ プロバイダは、システムから認識できないため実行されることはありません。ただし、ブロードキャスト レシーバの場合は、マニフェストで宣言する方法と、コード内で {@link android.content.BroadcastReceiver} オブジェクトとして動的に作成し、{@link android.content.Context#registerReceiver Context.registerReceiver()} を呼び出してシステムに登録する方法があります。

マニフェスト ファイルの作成方法について詳しくは、The AndroidManifest.xml Fileをご覧ください。

インテント フィルタ

Intent オブジェクトでは、対象とするコンポーネントを明示的に指名できます。明示的に指名されている場合、Android はマニフェスト ファイル内の宣言に基づいてコンポーネントを特定してアクティブにします。一方、明示的に指名されていない場合は、そのインテントに応答する上で最適なコンポーネントが選択されます。方法としては、Intent オブジェクトを、その対象となりうるコンポーネントのインテント フィルタと照合します。コンポーネントのインテント フィルタは、そのコンポーネントで処理できるインテントの種類を示します。これもコンポーネントに関する重要な情報の 1 つなので、マニフェスト ファイルで宣言します。次に、上に示した例を拡張して 2 つのインテント フィルタを追加したアクティビティを示します:

<?xml version="1.0" encoding="utf-8"?>
<manifest . . . >
    <application . . . >
        <activity android:name="com.example.project.FreneticActivity"
                  android:icon="@drawable/small_pic.png"
                  android:label="@string/freneticLabel" 
                  . . .  >
            <intent-filter . . . >
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter . . . >
                <action android:name="com.example.project.BOUNCE" />
                <data android:mimeType="image/jpeg" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
        . . .
    </application>
</manifest>

この例の 1 つ目のフィルタは、アクション「{@code android.intent.action.MAIN}」とカテゴリ「{@code android.intent.category.LAUNCHER}」を組み合わせた一般的なフィルタです。このフィルタは、アプリケーション ランチャ(ユーザーがデバイス上で起動できるアプリケーションを一覧表示した画面)に、このアクティビティを表示する必要があることを示しています。つまり、このアクティビティはアプリケーションへのエントリ ポイントとして機能し、ユーザーがランチャでそのアプリケーションを選択したときに最初に表示されるということです。

2 つ目のフィルタでは、アクティビティが特定のタイプのデータに対して実行できるアクションを宣言しています。

コンポーネントにはインテント フィルタをいくつでも指定でき、それぞれのフィルタで別々の機能を宣言できます。フィルタが 1 つも指定されていないコンポーネントは、そのコンポーネントが対象として明示的に指名されているインテントでのみアクティブにできます。

コード内で作成して登録したブロードキャスト レシーバの場合、インテント フィルタは {@link android.content.IntentFilter} オブジェクトとして直接インスタンス化されます。それ以外の全てのフィルタは、マニフェストで設定します。

インテント フィルタについて詳しくは、Intents and Intent Filters をご覧ください。

アクティビティとタスク

既に説明したように、あるアクティビティから別のアクティビティを開始することができます。これには、別のアプリケーションで定義されているアクティビティも含まれます。たとえば、ユーザーに特定の場所の地図を表示するとします。そのためのアクティビティは既に存在しているので、現在のアクティビティで必要な情報を Intent オブジェクトに格納して {@code startActivity()} に渡すだけで、マップ ビューアに地図を表示できます。ユーザーが [戻る] キーを押すと、画面に元のアクティビティが再表示されます。

この場合、マップ ビューアは別のアプリケーションで定義されており、そのアプリケーションのプロセスで実行されていますが、ユーザーにとってはマップ ビューアが元のアプリケーションの一部であるかのように感じられます。Android では、両方のアクティビティを同じタスクに組み込むことで、このようなユーザー エクスペリエンスを実現できます。簡単に言えば、ユーザーが 1 つの「アプリケーション」と感じるものがタスクです。関連するアクティビティをスタックにまとめたものがタスクです。スタック内のルート アクティビティは、タスクを開始するアクティビティです。通常であれば、ユーザーがアプリケーション ランチャで選択するアクティビティがこれに相当します。スタックの最上位にあるアクティビティは、ユーザーのアクションの焦点となっている実行中のアクティビティです。あるアクティビティから別のアクティビティを開始すると、そのアクティビティが新たにスタックにプッシュされて実行中のアクティビティになります。1 つ前のアクティビティはスタック内に残されています。ユーザーが [[]戻る] キーを押すと、現在のアクティビティがスタックからポップされ、1 つ前のアクティビティが実行中のアクティビティとして再開されます。

スタックはオブジェクトを保持します。したがって、同じ Activity サブクラスのインスタンス(たとえばマップ インスタンス)を複数開くと、それぞれのインスタンスが別々のエントリになります。スタック内のアクティビティは、プッシュまたはポップされるのみで再配置されることはありません。

タスクはアクティビティのスタックであり、マニフェスト ファイル内のクラスや要素ではありません。したがって、アクティビティと無関係にタスクの値を設定することはできません。タスクの値は、ルート アクティビティでまとめて設定します。たとえば、次のセクションでは「タスクの親和性」について説明しますが、値はタスクのルート アクティビティの親和性のセットから読み込まれます。

タスク内のアクティビティは、1 つのユニットとして一緒に移動します。タスク全体(アクティビティ スタック全体)をフォアグラウンドに移動したり、バックグラウンドに移動したりできます。たとえば、現在のタスクは 4 つのアクティビティからなるスタックで、現在のアクティビティの下にアクティビティが 3 つあるとします。ここで、ユーザーが [ホーム] キーを押してアプリケーション ランチャに移動し、新しいアプリケーション(実際には新しいタスク)を選択したとします。すると、現在のタスクはバックグラウンドに移動し、新しいタスクのルート アクティビティが表示されます。しばらくして、ユーザーがホーム画面に戻り 1 つ前のアプリケーション(タスク)を選択すると、そのタスクがスタック内の 4 つのアクティビティとともにフォアグラウンドに移動します。ここでユーザーが [戻る] キーを押しても、中断したばかりのアプリケーション(1 つ前のタスクのルート アクティビティ)は表示されません。代わりに、スタックの最上位のアクティビティがポップされ、同じタスクの 1 つ前のアクティビティが表示されます。

アクティビティとタスクの動作としては、ここで説明した動作がデフォルトです。ただし、この動作のほとんどの要素は変更可能です。タスクとアクティビティの関連付けやタスク内でのアクティビティの動作は、アクティビティを開始した Intent オブジェクトのフラグ セットと、マニフェストに指定されているアクティビティの <activity> 要素の属性セットとの相互作用によって決まります。リクエスト側と応答側の両方が動作に影響を及ぼします。

この点において、主に使用する Intent フラグは以下のとおりです:

{@code FLAG_ACTIVITY_NEW_TASK}
{@code FLAG_ACTIVITY_CLEAR_TOP}
{@code FLAG_ACTIVITY_RESET_TASK_IF_NEEDED}
{@code FLAG_ACTIVITY_SINGLE_TOP}

また、主に使用する {@code <activity>} 属性は以下のとおりです:

{@code taskAffinity}
{@code launchMode}
{@code allowTaskReparenting}
{@code clearTaskOnLaunch}
{@code alwaysRetainTaskState}
{@code finishOnTaskLaunch}

以降のセクションでは、これらのフラグや属性の役割、相互作用の仕組み、使用する際の留意事項などについて説明します。

親和性と新しいタスク

デフォルトでは、アプリケーション内のすべてのアクティビティは相互に親和性があり、すべてのアクティビティができる限り同じタスクに属そうとします。ただし、{@code <activity>} 要素の {@code taskAffinity} 属性を使用して、アクティビティごとに個別の親和性を設定することもできます。つまり、別々のアプリケーションで定義されているアクティビティで親和性を共有したり、同じアプリケーションで定義されているアクティビティに別々の親和性を割り当てたりできるということです。親和性が作用する状況は 2 つあります。1 つはアクティビティを起動する Intent オブジェクトに {@code FLAG_ACTIVITY_NEW_TASK} フラグが含まれている場合、もう 1 つはアクティビティの {@code allowTaskReparenting} 属性が "{@code true}" に設定されている場合です。

{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} フラグ
既に説明したとおり、新しいアクティビティは、デフォルトでは {@code startActivity()} を呼び出したアクティビティのタスクの一部として起動します。つまり、呼び出し側のアクティビティと同じスタックにプッシュされるということです。しかし、{@code startActivity()} に渡された Intent オブジェクトに {@code FLAG_ACTIVITY_NEW_TASK} フラグが含まれている場合、システムはその新しいアクティビティを別のタスクに収容しようとします。フラグの名前からも判断できますが、ほとんどの場合は新しいタスクが開始されます。ただし常にそうなるとは限りません。既存のタスクに新しいアクティビティと同じ親和性が割り当てられている場合、そのアクティビティはそのタスクの一部として起動します。そうでない場合には、新しいタスクが開始されます。
allowTaskReparenting 属性
{@code allowTaskReparenting} 属性が "{@code true}" に設定されているアクティビティは、そのアクティビティと親和性のあるタスクがフォアグラウンドに移ったときに、アクティビティを開始したタスクから親和性のあるタスクに移動できます。たとえば、旅行アプリケーションの一部として、選択された都市の天気予報を表示するアクティビティが定義されているとします。このアクティビティには、同じアプリケーション内の他のアクティビティと同じ親和性(デフォルトの親和性)が割り当てられていますが、その親の割り当てを変更することも可能です。あるアクティビティが天気予報アクティビティを開始すると、その時点では開始側のアクティビティと同じタスクに属した状態になります。しかし、次に旅行アプリケーションがフォアグラウンドに移ると、天気予報アクティビティの割り当てが変更され、旅行アプリケーションのタスクの一部として表示されます。

ユーザーから見て複数の「アプリケーション」が 1 つの {@code .apk} ファイルに含まれている場合は、それぞれのアプリケーションに関連付けられているアクティビティに別々の親和性を割り当てることをおすすめします。

起動モード

launchMode 属性の {@code <activity>} 要素には、以下の 4 種類の起動モードを割り当てることができます:

"{@code standard}"(デフォルト モード)
"{@code singleTop}"
"{@code singleTask}"
"{@code singleInstance}"

これらのモードは、それぞれが以下の 4 つの点で異なります:

既存のアクティビティで新しいインテントを処理することになった場合は、{@link android.app.Activity#onNewIntent onNewIntent()} の呼び出しによって Intent オブジェクトがアクティビティに渡されます(最初にアクティビティを開始したインテント オブジェクトは {@link android.app.Activity#getIntent getIntent()} を呼び出して取得できます)。

なお、新しいインテントを処理するためにアクティビティの新しいインスタンスが作成された場合、ユーザーは [[]戻る] キーを押して 1 つ前の状態(1 つ前のアクティビティ)に戻ることができます。しかし、アクティビティの既存のインスタンスで新しいインテントを処理する場合は、[[]戻る] キーを押しても、新しいインテントが届く前にそのインスタンスで処理していた作業に戻ることはできません。

起動モードについて詳しくは、<activity> 要素の説明をご覧ください。

スタックのクリア

ユーザーがタスクを長時間放置すると、タスクのルート アクティビティを除くすべてのアクティビティがクリアされます。ユーザーがタスクに戻ると、タスクは以前のように表示されますが、残っているのは最初のアクティビティだけです。つまり、一定の時間が経過していればユーザーは以前の作業を放棄していて、新しい作業をするためにそのタスクに戻ってきたと考えるわけです。

これがデフォルトです。この動作を変更したい場合は、以下のアクティビティ属性を使用します:

alwaysRetainTaskState 属性
タスクのルート アクティビティでこの属性を "{@code true}" に設定すると、上で説明したデフォルトの動作は発生しません。長時間経過しても、タスク内のすべてのアクティビティはそのまま残されます。
clearTaskOnLaunch 属性
タスクのルート アクティビティでこの属性を "{@code true}" に設定した場合、ユーザーがいったんタスクを離れると、戻ったときにはルートを含むすべてのアクティビティがクリアされています。つまり、{@code alwaysRetainTaskState} の正反対の動作になります。ユーザーが一瞬でもタスクを離れると、最初の状態からやり直すことになります。
finishOnTaskLaunch 属性
この属性は {@code clearTaskOnLaunch} に似ていますが、タスク全体ではなく単一のアクティビティに作用します。また、ルート アクティビティを含むどのアクティビティもクリアの対象となりえます。この属性が "{@code true}" に設定されたアクティビティは、現在のセッションの間のみタスクの一部を形成します。ユーザーがいったんそのタスクから離れてから、再度タスクに戻ると、このアクティビティはクリアされています

アクティビティをスタックから削除する方法は他にもあります。Intent オブジェクトに {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP FLAG_ACTIVITY_CLEAR_TOP} フラグが含まれており、そのインテントを処理すべきタイプのアクティビティのインスタンスが対象タスクのスタック内に存在する場合は、そのインスタンスがスタックの最上位になってインテントに応答できるよう、それより上位のアクティビティはすべてクリアされます。指定されたアクティビティの起動モードが "{@code standard}" である場合は、そのアクティビティもスタックから削除され、新しいインスタンスが起動してインテントを処理します。起動モード "{@code standard}" では、新しいインテントを処理する際、常に新しいインスタンスが作成されるためです。

{@code FLAG_ACTIVITY_CLEAR_TOP} は、ほとんどの場合 {@code FLAG_ACTIVITY_NEW_TASK} と組み合わせて使用します。これらのフラグを組み合わせると、別のタスクに既に存在しているアクティビティを探し、それをインテントに応答できる位置に配置できます。

タスクの開始

アクティビティをタスクのエントリ ポイントとして設定するには、アクションとして "{@code android.intent.action.MAIN}"、カテゴリとして "{@code android.intent.category.LAUNCHER}" を指定したインテント フィルタをアクティビティに追加します(このタイプのフィルタの例については、インテント フィルタをご覧ください)。このタイプのフィルタを追加すると、アクティビティのアイコンとラベルがアプリケーション ランチャに表示されます。これにより、ユーザーがタスクを起動するための手段を提供できるだけでなく、起動後はいつでもそのタスクに戻れるようにすることができます。

この 2 番目の機能、つまりユーザーがいったんタスクを離れても後で戻ることができるようにする点が重要です。この理由から、アクティビティに {@code MAIN} と {@code LAUNCHER} フィルタが指定されている場合は、必ずタスクが開始される起動モード("{@code singleTask}" または "{@code singleInstance}")を使用する必要があります。たとえば、このフィルタを指定しなかった場合を考えてみましょう。インテントが "{@code singleTask}" アクティビティを起動し、新しいタスクが開始され、ユーザーがしばらくの間このタスクで作業を行います。その後、ユーザーが [ホーム] キーを押したとします。ホーム画面が表示され、先ほどのタスクはバックグラウンドに移動します。しかし、このタスクはアプリケーション ランチャには表示されていないため、ユーザーがタスクに戻るための手段がありません。

{@code FLAG_ACTIVITY_NEW_TASK} フラグにも、これと同じような難しさがあります。このフラグを指定したアクティビティでは、新しいタスクを開始した後にユーザーが [ホーム] キーを押してそのタスクを離れた場合に備え、タスクに戻るための手段を用意しておく必要があります。一部のエンティティ(たとえば通知マネージャ)は、アクティビティを常に外部タスクとして開始します。エンティティの一部として開始することはないため、{@code startActivity()} に渡すインテントには必ず {@code FLAG_ACTIVITY_NEW_TASK} を指定します。外部エンティティから呼び出すことのできるアクティビティでこのフラグが使用されている可能性がある場合は、開始されたタスクにユーザーが戻るための手段を別途提供するようにしてください。

ユーザーがアクティビティに戻ることができるようにしない場合は、{@code <activity>} 要素の {@code finishOnTaskLaunch} を "{@code true}" に設定します。詳しくは、スタックのクリアをご覧ください。

プロセスとスレッド

Android では、最初のアプリケーション コンポーネントを実行する必要が生じると、そのための Linux プロセスを単一の実行スレッドで開始します。デフォルトでは、アプリケーションのすべてのコンポーネントがそのプロセスとスレッドで実行されます。

ただし、コンポーネントが他のプロセスで実行されるようにしたり、特定のプロセスに使用する追加スレッドを生成したりすることも可能です。

プロセス

コンポーネントを実行するプロセスは、マニフェスト ファイルで管理します。コンポーネントの各要素({@code <activity>}、{@code <service>}、{@code <receiver>}、および {@code <provider>})には {@code process} 属性があり、そのコンポーネントをどのプロセスで実行すべきかを指定できるようになっています。これらの属性の設定によって、それぞれのコンポーネントを専用のプロセスで実行したり、一部のコンポーネントだけでプロセスを共有したりできます。また、別々のアプリケーションのコンポーネントが、同じプロセスで実行されるように設定することもできます。この場合は、それらのアプリケーションが同じ Linux ユーザー ID を共有し、同じ認証機関によって署名されている必要があります。{@code <application>} 要素にも {@code process} 属性があり、すべてのコンポーネントに適用されるデフォルト値を設定できます。

すべてのコンポーネントは指定されたプロセスのメイン スレッドでインスタンス化され、コンポーネントに対するシステム コールはそのスレッドからディスパッチされます。1 つのインスタンスに対して、複数のスレッドが作成されることはありません。したがって、システム コールに応答するメソッド(たとえば、後ほどコンポーネント ライフサイクルで説明するライフサイクル通知や、ユーザーのアクションを報告する {@link android.view.View#onKeyDown View.onKeyDown()} のようなメソッド)は、常にそのプロセスのメイン スレッドで実行されます。つまり、コンポーネントがシステムから呼び出されたときに、プロセス内の他のコンポーネントの実行を妨げないよう、実行に時間がかかる処理や他の妨げになることの多い処理(ネットワーク処理、ループ計算など)をできる限り避ける必要があるということです。時間がかかる処理には別のスレッドを生成できます。詳しくは、次のスレッド セクションをご覧ください。

状況によっては、Android がプロセスを終了させるべきと判断する場合があります。たとえば、メモリが不足してきた場合や、他のプロセスでユーザーにすばやく応答する必要がある場合です。プロセスが終了すると、そのプロセス内で実行されているアプリケーション コンポーネントは破棄されます。それらのコンポーネントで処理する作業がもう一度発生すると、そのためのプロセスが再び開始されます。

Android では、どのプロセスを終了させるかを判断するため、ユーザーにとっての相対的な重要度を重み付けして管理します。たとえば、アクティビティがまだ画面に表示されているプロセスを終了させるよりも、アクティビティが画面に表示されていないプロセスを終了させる方が合理的です。したがって、プロセスを終了させるかどうかは、そのプロセスで実行されているコンポーネントの状態に応じて判断されるということです。コンポーネントの状態については、後ほどコンポーネントのライフサイクルで詳しく説明します。

スレッド

アプリケーションを単一のプロセスに限定したとしても、バックグラウンドでの処理にスレッドが必要になることはよくあります。ユーザー インターフェースはユーザーのアクションに対して常にすばやく応答できなければならないため、アクティビティをホストするスレッドで、ネットワーク ダウンロードのような時間のかかる処理を一緒にホストしないようにする必要があります。すぐに完了しない可能性のあるすべての処理は、別のスレッドに割り当てるようにしてください。

スレッドは、標準の Java {@link java.lang.Thread} オブジェクトを使用してコード内で作成します。Android には、スレッドを管理するための便利なクラスが数多く用意されています。たとえば、スレッド内でメッセージ ループを実行するための {@link android.os.Looper}、メッセージを処理するための {@link android.os.Handler}、メッセージ ループでスレッドを設定するための {@link android.os.HandlerThread} などがあります。

リモート プロシージャ コール

Androidは軽量な仕組みのリモート・プロシージャ・コール (RPC) を採用しています。RPC とは、メソッドをローカルで呼び出しますが、実行はリモート(別のプロセス)で行い、その結果を呼び出し側に返します。そのためには、メソッド呼び出しとそれに付随するデータをオペレーティングシステムが解釈できるレベルまで分解してから、それらをローカルのプロセスとアドレス空間からリモートのプロセスとアドレス空間に転送し、リモートで呼び出しを再構築する必要があります。戻り値は、反対方向に転送しなければなりません。Android にはこの処理を行うためのコードがすべて用意されているため、RPC インターフェースを定義して実装するだけで RPC を利用できます。

RPC インターフェースに含めることができるのはメソッドのみです。すべてのメソッドは、戻り値がない場合でも同期的に実行されます(つまり、リモート メソッドが完了するまでローカル メソッドがブロックされます)。

このメカニズムを簡単に説明すると次のようになります。まず、シンプルなインターフェース定義言語(IDL)を使用して、実装したい RPC インターフェースを宣言します。aidl ツールにより、RPC インターフェースの宣言から Java インターフェース定義が生成されます。この定義は、ローカルとリモートの両方のプロセスで使用する必要があります。定義には、次の図に示すように 2 つの内部クラスが含まれています:

RPC のメカニズム

これらの内部クラスには、IDL で宣言したインターフェースのリモート プロシージャ コールを管理するために必要なコードがすべて含まれています。どちらの内部クラスも {@link android.os.IBinder} インターフェースを実装します。一方の内部クラスは、ローカルのシステムで内部的に使用しますが、記述するコードでは無視しても構いません。もう一方の内部クラスはスタブと呼ばれ、{@link android.os.Binder} クラスを拡張します。スタブには、IPC(プロセス間通信)呼び出しを発生させるための内部コードに加え、IDL で宣言した RPC インターフェース内のメソッドの宣言が含まれます。これらのメソッドを実装するには、図に示すようにスタブをサブクラス化します。2つの内部クラスのうちの一方は、システムがローカルかつ内部的に使用するので、開発者が記述するコードでは無視してかまいません。... リモート側では、図のようにスタブをサブクラス化して、これらのメソッドを実装する必要があります。

通常、リモート プロセスはサービスで管理します。サービスなら、プロセスや他のプロセスへの接続に関する情報をシステムに伝えることができるからです。サービスには、{@code aidl} ツールで生成されたインターフェース ファイルと、RPC メソッドを実装するスタブ サブクラスの両方を持たせることになります。サービスのクライアントには、{@code aidl} ツールで生成されたインターフェース ファイルのみを持たせます。

以下に、サービスとそのクライアントの間の接続がどのように設定されるかを示します:

ここでは、説明を簡単にするため、RPC メカニズムの細かい点は省略しています。詳しくは、Designing a Remote Interface Using AIDL、および {@link android.os.IBinder IBinder} クラスの説明をご覧ください。

スレッドセーフなメソッド

状況によっては実装したメソッドが複数のスレッドから呼び出されることもあるため、スレッドセーフな記述を心掛ける必要があります。

前のセクションで説明した RPC のようにメソッドをリモートで呼び出すことができる場合は、このような状況が特に発生しやすくなります。IBinder オブジェクトに実装されているメソッドを IBinder と同じプロセスから呼び出すと、そのメソッドは呼び出し側のスレッドで実行されます。一方、別のプロセスからメソッドを呼び出した場合は、プロセスのメイン スレッドではなく、IBinder と同じプロセス内に保持されているスレッドのプールから選択されたスレッドで実行されます。たとえば、サービスの {@code onBind()} メソッドはそのサービスのプロセスのメイン スレッドから呼び出されるのに対し、{@code onBind()} から返されたオブジェクトに実装されているメソッド(たとえば RPC メソッドを実装するスタブ サブクラス)はプール内のスレッドから呼び出されます。サービスには複数のクライアントを割り当てることができるため、複数のプール スレッドを同じ IBinder に同時に割り当てることも可能です。したがって、IBinder メソッドはスレッドセーフになるように実装する必要があります。

同様に、コンテンツ プロバイダも別のプロセスからのデータ リクエストを受け取ることができます。ContentResolver および ContentProvider クラスはプロセス間通信の管理の詳細を隠蔽しますが、それらのリクエストに応答する ContentProvider メソッド({@link android.content.ContentProvider#query query()}{@link android.content.ContentProvider#insert insert()}{@link android.content.ContentProvider#delete delete()}{@link android.content.ContentProvider#update update()}、および {@link android.content.ContentProvider#getType getType()} メソッド)は、プロセスのメイン スレッドではなく、コンテンツ プロバイダのプロセス内のスレッドのプールから呼び出されます。これらのメソッドを同時に呼び出すことのできるメソッドの数に制限はありません。したがって、これらのメソッドもスレッドセーフになるように実装する必要があります。

コンポーネントのライフサイクル

アプリケーション コンポーネントにはライフサイクルがあります。ライフサイクルは、インテントへ応答することでのインスタンス化で始まり、そのインスタンスの破棄で終わります。この間、コンポーネントがアクティブなときとアクティブでないときがあり、アクティビティであればユーザーから見えるときと見えないときがあります。このセクションでは、アクティビティ、サービス、およびブロードキャスト レシーバのライフサイクルについて説明します。具体的には、それぞれがライフタイムの間に取ることのできる状態、状態の遷移を通知する方法、およびそれらの状態が、コンポーネントを実行しているプロセスが終了させられたり、インスタンスが破棄されたりする可能性への影響などについて説明します。

アクティビティのライフサイクル

アクティビティは、基本的に以下の 3 つの状態を取ります:

システムが一時停止または停止しているアクティビティをメモリから削除する場合は、アクティビティの {@link android.app.Activity#finish finish()} メソッドを呼び出して終了を要求するか、単純のそのプロセスを強制終了します。そのアクティビティをもう一度ユーザーに表示する際は、完全に再起動して以前の状態に復元する必要があります。

アクティビティがある状態から別の状態に遷移すると、以下の protected メソッドに対する呼び出しによって変更が通知されます:

{@code void onCreate(Bundle savedInstanceState)}
{@code void onStart()}
{@code void onRestart()}
{@code void onResume()}
{@code void onPause()}
{@code void onStop()}
{@code void onDestroy()}

これらのメソッドはすべて、状態が変化したときに適切な処理を行うためにオーバーライドできるフックです。オブジェクトが初めてインスタンス化されたときに初期設定を行うため、すべてのアクティビティには {@link android.app.Activity#onCreate onCreate()} を実装する必要があります。多くのアクティビティには、データの変更をコミットするための {@link android.app.Activity#onPause onPause()} も実装します。これを実装しない場合は、何らかの方法でユーザーとの対話を停止できるようにしておく必要があります。

これら 7 つのメソッドを使用すると、アクティビティのライフサイクル全体を定義できます。これらを実装することで、ネストされた 3 つのループからなるアクティビティのライフサイクルを監視できます:

次の図に、これらのループとアクティビティの遷移経路を示します。色の付いた楕円は、アクティビティが取ることのできる主な状態です。長方形は、アクティビティが状態間を遷移するときに処理を実行するために実装できるコールバック メソッドを表します。

Android のアクティビティ ライフサイクルの状態遷移図

次の表では、各メソッドについて詳しく説明し、ライフサイクル全体における位置付けを示します:

メソッド 説明 強制終了
{@link android.app.Activity#onCreate onCreate()} アクティビティが初めて作成されるときに呼び出されます。通常の静的な設定(ビューの作成、リストへのデータのバインドなど)は、すべてのこのメソッドで行う必要があります。このアクティビティの 以前の状態が保存されていた場合、このメソッドにはその状態を保持している Bundle オブジェクトが引数として(詳しくは、後述のアクティビティの状態の保存をご覧ください)。

この後には、必ず {@code onStart()} が呼び出されます。

不可 {@code onStart()}
     {@link android.app.Activity#onRestart onRestart()} アクティビティが停止した後、それをもう一度開始する直前に呼び出されます。

この後には、必ず {@code onStart()} が呼び出されます。

不可 {@code onStart()}
{@link android.app.Activity#onStart onStart()} アクティビティがユーザーから見えるようになる直前に呼び出されます。

その後、アクティビティがフォアグラウンドに表示された場合は {@code onResume()} が、他のアクティビティの後ろに隠れた場合は {@code onStop()} が呼び出されます。

不可 {@code onResume()}
または
{@code onStop()}
     {@link android.app.Activity#onResume onResume()} アクティビティがユーザーとの対話を開始する直前に呼び出されます。この時点で、アクティビティはアクティビティ スタックの最上位にあり、ユーザーからの入力はこのアクティビティに対して行われます。

この後には、必ず {@code onPause()} が呼び出されます。

不可 {@code onPause()}
{@link android.app.Activity#onPause onPause()} システムが別のアクティビティを開始しようとしているときに呼び出されます。このメソッドは、保存されていない変更を永続データにコミットする場合や、アニメーションのように CPU を大量に消費する処理を停止する場合に使用するのが一般的です。このメソッドが終了するまでは次のアクティビティが開始されたないため、できる限り短時間で実行できるようにしておく必要があります。

その後、アクティビティがフォアグラウンドに戻った場合は {@code onResume()} が、ユーザーから見えなくなった場合は {@code onStop()} が呼び出されます。

可能 {@code onResume()}
または
{@code onStop()}
{@link android.app.Activity#onStop onStop()} アクティビティがユーザーから見えなくなったときに呼び出されます。見えなくなる状況としては、アクティビティが破棄された場合や、再開された別のアクティビティ(既存か新規かを問わず)によって隠された場合が考えられます。

その後、アクティビティがユーザーとの対話に戻った場合は {@code onRestart()} が、アクティビティが完全に終了する場合は {@code onDestroy()} が呼び出されます。

可能 {@code onRestart()}
または
{@code onDestroy()}
{@link android.app.Activity#onDestroy onDestroy()} アクティビティが破棄される前に呼び出されます。これが、アクティビティが受け取る最後の呼び出しとなります。このメソッドが呼び出される状況としては、アクティビティが完了する場合({@link android.app.Activity#finish finish()} が呼び出されたとき)や、システムが領域を確保するために一時的にそのアクティビティのインスタンスを破棄する場合が考えられます。これらの 2 つの状況は、{@link android.app.Activity#isFinishing isFinishing()} メソッドを使用して識別できます。 可能 なし

表の強制終了列に注目してください。この列は、メソッドが終了した後であれば、システムがアクティビティのコードの別の行を実行することなくいつでもアクティビティを実行しているプロセスを強制終了できるかどうかを示しています。{@code onPause()}、{@code onStop()}、および {@code onDestroy()} メソッドの 3 つは「可能」となっています。1 番目に挙げた {@code onPause()} だけは、プロセスが強制終了する前に必ず呼び出されます。{@code onStop()} と {@code onDestroy()} は、必ず呼び出されるとは限りません。したがって、永続データ(たとえばユーザーによる編集)をストレージに書き込む際は {@code onPause()} を使用する必要があります。

強制終了列が「不可」になっているメソッドは、それらが呼び出された瞬間から、アクティビティを実行しているプロセスを保護して強制終了されないようにします。したがって、アクティビティが強制終了可能な状態にあるのは、たとえば {@code onPause()} が返されてから {@code onResume()} が呼び出されるまでの間ということです。その後は、もう一度 {@code onPause()} が返されるまで、強制終了できる状態には戻りません。

後述のプロセスとライフサイクルのセクションで詳しく説明しますが、ここでの定義で技術的には「強制終了可能」でないアクティビティでも、システムによって強制終了させられる可能性はありますが、他に利用できるリソースがないなど、極端に急を要する場合に限られます。

アクティビティの状態の保存

メモリ不足を補うためにユーザーではなくシステムがアクティビティを終了させた場合には,ユーザがそのアクティビティに戻ったときに、以前の状態のままであることを期待するでしょう。

アクティビティが強制終了させられる前の状態を保存しておきたい場合は、アクティビティに {@link android.app.Activity#onSaveInstanceState onSaveInstanceState()} メソッドを実装します。このメソッドは、アクティビティが破棄されやすい状態になる前(つまり {@code onPause()} が呼び出される前)に呼び出されます。その際、アクティビティの動的な状態を名前/値ペアとして記録できる {@link android.os.Bundle} オブジェクトが渡されます。アクティビティがもう一度開始されると、Bundle は {@code onCreate()} だけでなく、{@code onStart()} の後に呼び出される {@link android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} メソッドにも渡され、保存されている状態をそのどちらかまたは両方で復元できます。

{@code onSaveInstanceState()} および {@code onRestoreInstanceState()} メソッドは、これまでに説明した {@code onPause()} などとは異なり、ライフサイクル メソッドではありません。これらのメソッドは、常に呼び出されるわけではありません。たとえば、{@code onSaveInstanceState()} は、システムによってアクティビティが破棄しやすい状態にされる前には呼び出されますが、ユーザーのアクション(たとえば [[]戻る] キー)によってインスタンスが実際に破棄されるときには呼び出されません。そのような場合は、ユーザーがそのアクティビティに戻ることを想定する必要はないため、状態を保存する理由がないのです。

{@code onSaveInstanceState()} は常に呼び出されるとは限らないため、アクティビティの一時的な状態を記録する目的のみに使用し、永続データの格納には使用しないようにしてください。この目的には {@code onPause()} を使用します。

アクティビティの協調

あるアクティビティが別のアクティビティを開始すると、両方のアクティビティのライフサイクル状態が遷移します。一方が一時停止または停止し、もう一方が開始されます。場合によっては、これらの協調させる必要があります。

ライフサイクルのコールバックの順序は明確に定義されており、特に 2 つのアクティビティが同じプロセス内に存在する場合は次のようになります:

  1. 現在のアクティビティの {@code onPause()} メソッドが呼び出されます。
  2. 続いて、開始されるアクティビティの {@code onCreate()}、{@code onStart()}、および {@code onResume()} メソッドが順番に呼び出されます。
  3. その後、開始されたアクティビティが画面上で見えなくなると、その {@code onStop()} メソッドが呼び出されます。

サービスのライフサイクル

サービスは、以下の 2 つの方法で使用できます:

これら 2 つのモードは、完全に分離されているわけではありません。{@code startService()} で開始されたサービスにバインドすることも可能です。たとえば、再生する曲を指定した Intent オブジェクトで {@code startService()} を呼び出して音楽再生サービスを開始したとします。その後、たとえばユーザーがプレーヤーを操作したい場合や再生中の曲に関する情報を入手したい場合には、アクティビティから {@code bindService()} を呼び出してサービスとの接続を確立できます。このような場合、最後のバインドが閉じられるまでは、{@code stopService()} を呼び出してもサービスは停止しません。

アクティビティと同様、サービスにもライフサイクル メソッドがあり、これらを実装することでサービスの状態の変化を監視できます。ただし、protected ではなく public で、以下の 3 つしかありません:

{@code void onCreate()}
{@code void onStart(Intent intent)}
{@code void onDestroy()}

これらのメソッドを実装することで、ネストされた 2 つのループからなるサービスのライフサイクルを監視できます:

{@code onCreate()} および {@code onDestroy()} メソッドは、サービスを {@link android.content.Context#startService Context.startService()} または {@link android.content.Context#bindService Context.bindService()} のどちらで開始したかに関係なく、すべてのサービスで呼び出されます。一方、{@code onStart()} は、サービスを {@code startService()} で開始した場合のみ呼び出されます。

サービスが他からのバインドを許可している場合は、以下のコールバック メソッドを追加で実装できます:

{@code IBinder onBind(Intent intent)}
{@code boolean onUnbind(Intent intent)}
{@code void onRebind(Intent intent)}

{@link android.app.Service#onBind onBind()} コールバックには {@code bindService()} に渡された Intent オブジェクトが渡され、{@link android.app.Service#onUnbind onUnbind()} には {@code unbindService()} 渡された Intent オブジェクトが渡されます。サービスがバインドを許可している場合は、クライアントがサービスと対話する通信チャネルを {@code onBind()} で返します。{@code onUnbind()} メソッドは、サービスに新しいクライアントが接続した場合に {@link android.app.Service#onRebind onRebind()} の呼び出しを要求できます。

次の図に、サービスのコールバック メソッドを示します。なお、{@code startService()} で作成されたサービスと、{@code bindService()} で作成されたサービスを分けて記述していますが、作成された方法に関係なく,すべてのサービスはクライアントからのバインドを許可できます。したがって、どのサービスも {@code onBind()} および{@code onUnbind()} メソッドの呼び出しを受け取る可能性はあります。

サービス コールバックの状態遷移図

ブロードキャスト レシーバのライフサイクル

ブロードキャスト レシーバのコールバック メソッドは次の 1 つのみです:

{@code void onReceive(Context curContext, Intent broadcastMsg)}

レシーバにブロードキャスト メッセージが届くと、{@link android.content.BroadcastReceiver#onReceive onReceive()} メソッドが呼び出され、メッセージを保持する Intent オブジェクトが渡されます。ブロードキャスト レシーバは、このメソッドの実行中のみアクティブと見なされます。{@code onReceive()} 終了すると、ブロードキャスト レシーバはアクティブでなくなります。

ブロードキャスト レシーバがアクティブになっているプロセスは、強制終了しないよう保護されます。一方、アクティブでないコンポーネントのみからなるプロセスは、それが消費しているメモリが他のプロセスで必要になった場合は、いつでも強制終了される可能性があります。

この点は、ブロードキャスト メッセージへの応答に時間がかかるため、ユーザー インターフェースの他のコンポーネントを実行しているメイン スレッドとは別のスレッドで何らかの処理を行う必要がある場合に問題になります。{@code onReceive()} が新しいスレッドを生成して終了した場合、プロセス内に他にアクティブなアプリケーション コンポーネントがなければ、そのスレッドを含めたプロセス全体がアクティブでないと判断されて強制終了させられるおそれがあります。この問題を回避するには、{@code onReceive()} でサービスを開始し、そのサービスにジョブを実行させます。これにより、プロセス内にまだアクティブなコンポーネントがあると見なされます。

次のセクションでは、プロセスが強制終了される可能性が高くなる状況についてさらに詳しく説明します。

プロセスとライフサイクル

Android は、プロセスをできるだけ長い間維持しようとします。しかし、最終的にメモリが不足したときには、古いプロセスを削除しなければならなくなります。Android では、どのプロセスを維持し、どのプロセスを強制終了させるかを判断するため、プロセス内で実行されているコンポーネントと各コンポーネントの状態に基づいて、各プロセスを「重要度の階層」の位置づけます。まず最も重要度の低いプロセスが削除され、次は 2 番目に重要度の低いプロセス、その次に 3 番目、というように判断されます。階層は 5 つのレベルで構成されます。以下では、重要度の高いものから順に説明します:

  1. フォアグラウンド プロセスは、ユーザーがその時点で行っている作業に必要なプロセスです。以下のいずれかの条件を満たしているプロセスは、フォアグラウンド プロセスと見なされます:

    同時に存在するフォアグラウンド プロセスは少数に限られています。フォアグラウンド プロセスは、メモリが極端に不足し、すべてのフォアグラウンド プロセスの実行を継続できない場合の最終手段として強制終了させられます。通常、その時点でデバイスはメモリ ページングの状態に達しており、ユーザー インターフェースを応答可能な状態に維持するためには、フォアグラウンド プロセスの一部を強制終了させなければならない状況に陥っています。

  2. 可視プロセスは、フォアグラウンド コンポーネントではないものの、ユーザーが見ている画面に影響を及ぼすことのできるプロセスです。以下のいずれかの条件を満たしているプロセスは、可視プロセスと見なされます:

    可視プロセスは、非常に重要なプロセスと見なされ、すべてのフォアグラウンド プロセスの実行を維持するために必要でない限り、強制終了させられることはありません。

  3. サービス プロセスは、{@link android.content.Context#startService startService()} メソッドで開始されたサービスを実行しているプロセスのうち、より重要度の高い 2 つのレベルのどちらにも該当しないプロセスです。サービス プロセスは、ユーザーに見えるものとの直接的な関係はありませんが、たとえばバックグラウンドでの MP3 の再生、ネットワークからのデータのダウンロードなど、ユーザーが気にかけている処理であることが一般的です。したがって、すべてのフォアグラウンド プロセスと可視プロセスに加え、これらのサービス プロセスの実行を維持するだけのメモリが確保できる限り、強制終了させられることはありません。

  4. バックグラウンド プロセスは、その時点でユーザーから見えないアクティビティを保持している(Activity オブジェクトの {@link android.app.Activity#onStop onStop()} メソッドが呼び出されている)プロセスです。これらのプロセスは、ユーザー エクスペリエンスに直接的には影響しておらず、フォアグラウンド、可視、サービス プロセスからメモリが要求された場合はいつでも強制終了する可能性があります。通常は数多くのバックグラウンド プロセスが実行されているため、それらを LRU(least recently used)リストに登録し、ユーザーが一番最近見たアクティビティのプロセスが最後に強制終了するような仕組みになっています。アクティビティにライフサイクル メソッドが正しく実装されており、現在の状態が正しく保存されていれば、プロセスを強制終了してもユーザー エクスペリエンスに悪影響が及ぶことはありません。

  5. 空のプロセスは、アクティブなアプリケーション コンポーネントを保持していないプロセスです。このようなプロセスを維持しておく唯一の理由は、これをキャッシュとして使用し、次回コンポーネントを実行するときの起動時間を短くするためです。多くの場合、システムはこれらのプロセスを強制終了させて、プロセス キャッシュとその基礎となるカーネル キャッシュの間でシステム リソース全体のバランスを取ります。

各プロセスは、その時点でアクティブなコンポーネントの重要度に基づいて、そのプロセスが取りうる最も高いレベルにランク付けされます。たとえば、あるプロセスがサービスと可視アクティビティをホストしている場合、そのプロセスはサービス プロセスではなく可視プロセスとしてランク付けされます。

また、あるプロセスに他のプロセスが依存しているために、そのプロセスのランクが引き上げられる可能性もあります。他のプロセスから依存されているプロセスが、依存しているプロセスよりも低いレベルにランク付けされることはありません。たとえば、プロセス A 内のコンテンツ プロバイダにプロセス B 内のクライアントが依存している場合や、プロセス A 内のサービスがプロセス B 内のコンポーネントにバインドされている場合、プロセス A は常にプロセス B よりは重要度が高いと見なされます。

サービスを実行しているプロセスは、バックグラウンド アクティビティを実行しているプロセスよりも高くランク付けされます。したがって、時間のかかる処理を実行する場合、特にその処理がアクティビティよりも長く続くような場合は、単にスレッドを生成するのではなく、その処理用のサービスを開始することをおすすめします。たとえば、バックグラウンドで音楽を再生する場合や、カメラで撮影した写真を Web サイトにアップロードする場合などはこれに当たります。サービスを使用することで、アクティビティがどのような状況にあっても、処理の重要度として「サービス プロセス」レベル以上を維持できます。ブロードキャスト レシーバのライフサイクルのセクションでも説明しましたが、ブロードキャスト レシーバにおいてもこれと同じ理由で、処理に時間がかかる場合はスレッドではなくサービスを使用することをおすすめします。