Intent は、別のアプリ コンポーネントにアクションをリクエストするために使用できるメッセージング オブジェクトです。インテントは、いくつかの方法でコンポーネント間の通信を容易にしますが、基本的なユースケースは次の 3 つです。
- アクティビティを開始する
Activityは、アプリ内の 1 つの画面を表します。IntentをstartActivity()に渡すことで、Activityの新しいインスタンスを開始できます。Intentは、開始するアクティビティを記述し、必要なデータを保持します。アクティビティの終了時に結果を受け取る場合は、
startActivityForResult()を呼び出します。アクティビティは、アクティビティのonActivityResult()コールバックで、結果を別のIntentオブジェクトとして受け取ります。詳細については、アクティビティ ガイドをご覧ください。 - サービスの開始
Serviceは、ユーザー インターフェースなしでバックグラウンドでオペレーションを実行するコンポーネントです。Android 5.0(API レベル 21)以降では、JobSchedulerを使用してサービスを開始できます。JobSchedulerの詳細については、API-reference documentationをご覧ください。Android 5.0(API レベル 21)より前のバージョンでは、
Serviceクラスのメソッドを使用してサービスを開始できます。IntentをstartService()に渡すことで、1 回限りのオペレーション(ファイルのダウンロードなど)を実行するサービスを開始できます。Intentは、開始するサービスを記述し、必要なデータを保持します。サービスがクライアント サーバー インターフェースで設計されている場合は、
IntentをbindService()に渡すことで、別のコンポーネントからサービスにバインドできます。詳細については、Service ガイドをご覧ください。 - ブロードキャストの配信
ブロードキャストは、どのアプリでも受信できるメッセージです。システムは、システム起動時やデバイスの充電開始時など、システム イベントに対してさまざまなブロードキャストを配信します。
IntentをsendBroadcast()またはsendOrderedBroadcast()に渡すことで、他のアプリにブロードキャストを配信できます。
このページの残りの部分では、インテントの仕組みと使用方法について説明します。 関連情報については、他のアプリと連携するとコンテンツを共有するをご覧ください。
インテントの種類
インテントには次の 2 種類があります。
- 明示的インテントは、完全な
ComponentNameを指定することで、どのアプリのどのコンポーネントがインテントを満たすかを指定します。通常、明示的インテントは、起動するアクティビティやサービスのクラス名がわかっているため、独自のアプリでコンポーネントを起動するために使用します。たとえば、ユーザーの操作に応じてアプリ内で新しいアクティビティを開始したり、バックグラウンドでファイルをダウンロードするサービスを開始したりできます。 - 暗黙的インテントは、特定のコンポーネントを指定するのではなく、実行する一般的なアクションを宣言します。これにより、別のアプリのコンポーネントがそのアクションを処理できるようになります。たとえば、地図上の場所をユーザーに表示する場合は、暗黙的インテントを使用して、地図上の指定された場所を表示するよう、別の対応アプリにリクエストできます。
図 1 は、アクティビティの開始時にインテントがどのように使用されるかを示しています。Intent オブジェクトが特定のアクティビティ コンポーネントを明示的に指定している場合、システムはそのコンポーネントをすぐに起動します。
図 1. 暗黙的インテントがシステムを介して配信され、別のアクティビティを開始する仕組み: [1] アクティビティ A がアクションの説明を含む Intent を作成し、startActivity() に渡します。[2] Android システムは、インテントに一致するインテント フィルタをすべてのアプリから検索します。一致が見つかると、[3] システムは、一致するアクティビティ(アクティビティ B)の onCreate() メソッドを呼び出して Intent を渡すことで、そのアクティビティを開始します。
暗黙的インテントを使用すると、Android システムは、インテントの内容をデバイス上の他のアプリのマニフェスト ファイルで宣言されているインテント フィルタと比較して、起動する適切なコンポーネントを見つけます。インテントがインテント フィルタに一致すると、システムはそのコンポーネントを起動し、Intent オブジェクトを配信します。複数のインテント フィルタが一致する場合、システムはダイアログを表示して、ユーザーが使用するアプリを選択できるようにします。
インテント フィルタは、アプリのマニフェスト ファイル内の式で、コンポーネントが受信するインテントのタイプを指定します。たとえば、アクティビティのインテント フィルタを宣言すると、他のアプリが特定の種類のインテントを使用してアクティビティを直接開始できるようになります。同様に、アクティビティにインテント フィルタを宣言しない場合、そのアクティビティは明示的インテントでのみ開始できます。
注意: アプリの安全性を確保するため、Service を開始する際は常に明示的インテントを使用し、サービスにインテント フィルタを宣言しないでください。暗黙的インテントを使用してサービスを開始すると、どのサービスがインテントに応答するのかを把握できず、ユーザーにはどのサービスが開始するのかがわからないため、セキュリティ上の危険が伴います。Android 5.0(API レベル 21)以降では、暗黙的インテントを使用して bindService() を呼び出すと、システムから例外がスローされます。
インテントの構築
Intent オブジェクトは、Android システムが起動するコンポーネントを決定するために使用する情報(インテントを受け取る正確なコンポーネント名やコンポーネント カテゴリなど)と、受信側のコンポーネントがアクションを適切に実行するために使用する情報(実行するアクションやアクションの対象となるデータなど)を保持します。
Intent に含まれる主な情報は次のとおりです。
- コンポーネント名
- 起動するコンポーネントの名前。
これは省略可能ですが、インテントを明示的にする重要な情報です。つまり、インテントはコンポーネント名で定義されたアプリ コンポーネントにのみ配信される必要があります。コンポーネント名がない場合、インテントは暗黙的となり、システムは他のインテント情報(アクション、データ、カテゴリなど。以下を参照)に基づいて、インテントを受け取るコンポーネントを決定します。アプリ内の特定のコンポーネントを起動する必要がある場合は、コンポーネント名を指定する必要があります。
注:
Serviceを開始するときは、常にコンポーネント名を指定してください。そうしないと、どのサービスがインテントに応答するのかを把握できず、ユーザーもどのサービスが開始するのかがわかりません。IntentのこのフィールドはComponentNameオブジェクトです。このオブジェクトは、ターゲット コンポーネントの完全修飾クラス名(アプリのパッケージ名を含む)を使用して指定できます(例:com.example.ExampleActivity)。コンポーネント名は、setComponent()、setClass()、setClassName()、またはIntentコンストラクタで設定できます。 - 操作
- 実行する一般的なアクション(表示や選択など)を指定する文字列。
ブロードキャスト インテントの場合、これは発生したアクションであり、報告されます。アクションによって、インテントの残りの部分(特に、データとエクストラに含まれる情報)の構造が大きく決まります。
アプリ内のインテントで使用する(または他のアプリがアプリ内のコンポーネントを呼び出すために使用する)独自のアクションを指定できますが、通常は
Intentクラスまたは他のフレームワーク クラスで定義されたアクション定数を指定します。アクティビティを開始する一般的なアクションを以下に示します。ACTION_VIEW- アクティビティがユーザーに表示できる情報(ギャラリー アプリで表示する写真や、地図アプリで表示する住所など)がある場合は、
startActivity()を指定したインテントでこのアクションを使用します。 ACTION_SEND- 共有インテントとも呼ばれます。ユーザーが別のアプリ(メールアプリやソーシャルで共有アプリなど)を介して共有できるデータがある場合は、
startActivity()を指定したインテントでこれを使用する必要があります。
一般的なアクションを定義するその他の定数については、
Intentクラスのリファレンスをご覧ください。その他のアクションは、Android フレームワークの別の場所で定義されています。たとえば、システムの設定アプリで特定の画面を開くアクションは、Settingsで定義されています。インテントのアクションは、
setAction()またはIntentコンストラクタで指定できます。独自のアクションを定義する場合は、次の例に示すように、アプリのパッケージ名をプレフィックスとして含める必要があります。
Kotlin
const val ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL"
Java
static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
- データ
- 処理対象のデータを参照する URI(
Uriオブジェクト)や、そのデータの MIME タイプ。提供されるデータのタイプは、通常、インテントのアクションによって決まります。たとえば、アクションがACTION_EDITの場合、データには編集するドキュメントの URI を含める必要があります。インテントを作成する際は、URI に加えて、データのタイプ(MIME タイプ)を指定することが重要になる場合がよくあります。たとえば、画像を表示できるアクティビティは、URI 形式が似ていても、音声ファイルを再生できない可能性があります。データの MIME タイプを指定すると、Android システムがインテントを受信する最適なコンポーネントを見つけるのに役立ちます。ただし、MIME タイプは URI から推測できる場合もあります。特に、データが
content:URI の場合です。content:URI は、データがデバイス上にあり、ContentProviderによって制御されていることを示します。これにより、データの MIME タイプがシステムに表示されます。データ URI のみを設定するには、
setData()を呼び出します。MIME タイプのみを設定するには、setType()を呼び出します。必要に応じて、setDataAndType()を使用して両方を明示的に設定できます。注意: URI と MIME タイプの両方を設定する場合は、
setData()とsetType()を呼び出さないでください。これらは互いの値を無効にするためです。URI と MIME タイプの両方を設定するには、常にsetDataAndType()を使用します。 - カテゴリ
- インテントを処理するコンポーネントの種類に関する追加情報を含む文字列。インテントには任意の数のカテゴリの説明を配置できますが、ほとんどのインテントではカテゴリは必要ありません。一般的なカテゴリは次のとおりです。
CATEGORY_BROWSABLE- ターゲット アクティビティは、ウェブブラウザによって起動され、リンクで参照されるデータ(画像やメール メッセージなど)を表示できます。
CATEGORY_LAUNCHER- アクティビティはタスクの最初のアクティビティであり、システムのアプリケーション ランチャーに表示されます。
カテゴリの全リストについては、
Intentクラスの説明をご覧ください。addCategory()でカテゴリを指定できます。
上記のプロパティ(コンポーネント名、アクション、データ、カテゴリ)は、インテントの特性を定義するものです。Android システムは、これらのプロパティを読み取ることで、起動するアプリ コンポーネントを解決できます。ただし、インテントには、アプリ コンポーネントへの解決方法に影響しない追加情報を含めることができます。インテントには、次の情報も指定できます。
- エクストラ
- リクエストされたアクションを実行するために必要な追加情報を保持する Key-Value ペア。一部のアクションが特定の種類のデータ URI を使用するのと同様に、一部のアクションは特定のエクストラも使用します。
さまざまな
putExtra()メソッドを使用して追加データを追加できます。各メソッドは、キー名と値の 2 つのパラメータを受け入れます。すべての追加データを含むBundleオブジェクトを作成し、putExtras()を使用してIntentにBundleを挿入することもできます。たとえば、
ACTION_SENDを使用してメールを送信するインテントを作成する場合、EXTRA_EMAILキーで to 受信者を指定し、EXTRA_SUBJECTキーで subject を指定できます。Intentクラスは、標準化されたデータ型に対して多くのEXTRA_*定数を指定します。独自の追加キーを宣言する必要がある場合(アプリが受信するインテントの場合)は、次の例に示すように、アプリのパッケージ名を接頭辞として含める必要があります。Kotlin
const val EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS"
Java
static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";
注意: 別のアプリが受信することを想定しているインテントを送信する場合は、
ParcelableデータやSerializableデータを使用しないでください。アプリがBundleオブジェクト内のデータにアクセスしようとしたときに、パーセル化またはシリアル化されたクラスにアクセスできない場合、システムはRuntimeExceptionを発生させます。 - フラグ
- フラグは、インテントのメタデータとして機能する
Intentクラスで定義されます。フラグは、アクティビティを起動する方法(アクティビティが属するタスクなど)と、起動後の処理方法(最近のアクティビティのリストに属するかどうかなど)を Android システムに指示します。詳細については、
setFlags()メソッドをご覧ください。
明示的インテントの例
明示的インテントは、アプリ内の特定のアクティビティやサービスなど、特定のアプリ コンポーネントを起動するために使用します。明示的インテントを作成するには、Intent オブジェクトのコンポーネント名を定義します。他のすべてのインテント プロパティは省略可能です。
たとえば、ウェブからファイルをダウンロードするように設計された DownloadService という名前のサービスをアプリに構築した場合は、次のコードを使用して開始できます。
Kotlin
// Executed in an Activity, so 'this' is theContext// The fileUrl is a string URL, such as "http://www.example.com/image.png" val downloadIntent = Intent(this, DownloadService::class.java).apply { data =Uri.parse(fileUrl) } startService(downloadIntent)
Java
// Executed in an Activity, so 'this' is theContext// The fileUrl is a string URL, such as "http://www.example.com/image.png" Intent downloadIntent = new Intent(this, DownloadService.class); downloadIntent.setData(Uri.parse(fileUrl)); startService(downloadIntent);
Intent(Context, Class) コンストラクタは、アプリの Context とコンポーネントの Class オブジェクトを提供します。このインテントは、アプリの DownloadService クラスを明示的に起動します。
サービスの構築と開始の詳細については、サービスのガイドをご覧ください。
暗黙的インテントの例
暗黙的インテントは、アクションを実行できるデバイス上の任意のアプリを呼び出すことができるアクションを指定します。暗黙的インテントは、アプリがアクションを実行できないものの、他のアプリが実行できる可能性があり、ユーザーに使用するアプリを選択させたい場合に便利です。
たとえば、ユーザーが他のユーザーと共有するコンテンツがある場合は、ACTION_SEND アクションを使用してインテントを作成し、共有するコンテンツを指定するエクストラを追加します。そのインテントで startActivity() を呼び出すと、ユーザーはコンテンツを共有するアプリを選択できます。
Kotlin
// Create the text message with a string. val sendIntent = Intent().apply { action = Intent.ACTION_SEND putExtra(Intent.EXTRA_TEXT, textMessage) type = "text/plain" } // Try to invoke the intent. try { startActivity(sendIntent) } catch (e: ActivityNotFoundException) { // Define what your app should do if no activity can handle the intent. }
Java
// Create the text message with a string. Intent sendIntent = new Intent(); sendIntent.setAction(Intent.ACTION_SEND); sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage); sendIntent.setType("text/plain"); // Try to invoke the intent. try { startActivity(sendIntent); } catch (ActivityNotFoundException e) { // Define what your app should do if no activity can handle the intent. }
startActivity() が呼び出されると、システムはインストールされているすべてのアプリを調べて、この種のインテント(ACTION_SEND アクションを含むインテントで、「text/plain」データを保持するインテント)を処理できるアプリを特定します。インテントを処理できるアプリが 1 つしかない場合は、そのアプリがすぐに開き、インテントが渡されます。他のアプリで処理できない場合、アプリは発生した ActivityNotFoundException を捕捉できます。複数のアクティビティがインテントを受け入れる場合、図 2 に示すようなダイアログが表示され、ユーザーは使用するアプリを選択できます。
他のアプリの起動について詳しくは、ユーザーを別のアプリに誘導するをご覧ください。
図 2. チューザ ダイアログ
アプリチューザを強制的に表示する
暗黙的インテントに応答するアプリが複数ある場合、ユーザーは使用するアプリを選択し、そのアプリをアクションのデフォルトに設定できます。デフォルトを選択する機能は、ユーザーが毎回同じアプリを使用する可能性が高いアクションを実行する場合に便利です。たとえば、ウェブページを開く場合、ユーザーは通常、1 つのウェブブラウザのみを使用します。
ただし、複数のアプリがインテントに応答でき、ユーザーが毎回異なるアプリを使用する可能性がある場合は、選択ダイアログを明示的に表示する必要があります。選択ダイアログは、アクションに使用するアプリを選択するようユーザーに求めます(ユーザーはアクションのデフォルト アプリを選択できません)。たとえば、アプリが ACTION_SEND アクションで「共有」を実行する場合、ユーザーは現在の状況に応じて別のアプリを使用して共有したいことがあるため、図 2 のように、常に選択ツール ダイアログを使用する必要があります。
選択ツールを表示するには、次の例に示すように、createChooser() を使用して Intent を作成し、startActivity() に渡します。
この例では、createChooser() メソッドに渡されたインテントに応答するアプリのリストがダイアログに表示され、指定されたテキストがダイアログのタイトルとして使用されます。
Kotlin
val sendIntent = Intent(Intent.ACTION_SEND) ... // Always use string resources for UI text. // This says something like "Share this photo with" val title: String = resources.getString(R.string.chooser_title) // Create intent to show the chooser dialog val chooser: Intent = Intent.createChooser(sendIntent, title) // Verify the original intent will resolve to at least one activity if (sendIntent.resolveActivity(packageManager) != null) { startActivity(chooser) }
Java
Intent sendIntent = new Intent(Intent.ACTION_SEND); ... // Always use string resources for UI text. // This says something like "Share this photo with" String title = getResources().getString(R.string.chooser_title); // Create intent to show the chooser dialog Intent chooser = Intent.createChooser(sendIntent, title); // Verify the original intent will resolve to at least one activity if (sendIntent.resolveActivity(getPackageManager()) != null) { startActivity(chooser); }
インテントの安全でない起動を検出する
アプリは、アプリ内のコンポーネント間を移動するため、または別のアプリに代わってアクションを実行するために、インテントを起動することがあります。プラットフォームのセキュリティを強化するため、Android 12(API レベル 31)以降には、アプリがインテントの安全でない起動を実行した場合に警告するデバッグ機能が用意されています。たとえば、アプリがネストされたインテント(別のインテントのエクストラとして渡されるインテント)を安全でない方法で起動する可能性があります。
アプリが以下のアクションの両方を実行した場合、システムによってインテントの安全でない起動が検出され、StrictMode 違反が発生します。
- 配信されたインテントのエクストラから、ネストされたインテントを取り出した。
- そのネストされたインテントを使用して(たとえば
startActivity()、startService()、またはbindService()にインテントを渡して)、アプリ コンポーネントを直ちに開始した。
この状況を特定してアプリに変更を加える方法について詳しくは、Medium の Android Nesting Intents に関するブログ投稿をご覧ください。
インテントの安全でない起動を確認する
アプリでの安全でないインテントの起動を確認するには、VmPolicy を構成する際に detectUnsafeIntentLaunch() を呼び出します。次のコード スニペットをご覧ください。アプリが StrictMode 違反を検出した場合は、機密情報の漏洩を防ぐためにアプリの実行を停止することをおすすめします。
Kotlin
fun onCreate() { StrictMode.setVmPolicy(VmPolicy.Builder() // Other StrictMode checks that you've previously added. // ... .detectUnsafeIntentLaunch() .penaltyLog() // Consider also adding penaltyDeath() .build()) }
Java
protected void onCreate() { StrictMode.setVmPolicy(new VmPolicy.Builder() // Other StrictMode checks that you've previously added. // ... .detectUnsafeIntentLaunch() .penaltyLog() // Consider also adding penaltyDeath() .build()); }
より厳格なインテントの使い方
安全でないインテントの起動や StrictMode 違反の可能性を最小限に抑えるには、以下のベスト プラクティスを実践してください。
インテント内の必須のエクストラのみをコピーして、必要なサニタイズと検証を行います。アプリは、あるインテントから、新しいコンポーネントの起動に使用される別のインテントに、エクストラをコピーすることがあります。これは、アプリが putExtras(Intent) または putExtras(Bundle) を呼び出したときに発生します。アプリがこれらのオペレーションのいずれかを実行する場合、受信コンポーネントで想定されるエクストラのみをコピーします。コピーを受信するインテントがエクスポートされていないコンポーネントを起動する場合は、エクストラをサニタイズして検証してから、コンポーネントを起動するインテントにコピーします。
アプリのコンポーネントを不必要にエクスポートしないでください。たとえば、内部のネストされたインテントを使用してアプリ コンポーネントを起動する場合は、そのコンポーネントの android:exported 属性を false に設定します。
ネストされたインテントの代わりに PendingIntent を使用します。そうすることで、別のアプリが Intent に含まれる PendingIntent をアンパーセルする際に、その別のアプリがアプリの ID を使用して PendingIntent を起動できるようになります。この構成により、別のアプリは、エクスポートされていないコンポーネントを含むアプリ内の任意のコンポーネントを安全に起動できます。
図 2 の図は、システムが(クライアント)アプリから別の(サービス)アプリに制御を渡し、アプリに戻す方法を示しています。
- アプリが、別のアプリのアクティビティを呼び出すインテントを作成します。そのインテント内に、
PendingIntentオブジェクトをエクストラとして追加します。このペンディング インテントは、アプリ内のコンポーネントを呼び出します。このコンポーネントはエクスポートされません。 - アプリのインテントを受信すると、他のアプリはネストされた
PendingIntentオブジェクトを抽出します。 - もう一方のアプリが
PendingIntentオブジェクトでsend()メソッドを呼び出します。 - 制御がアプリに戻ると、システムはアプリのコンテキストを使用してペンディング インテントを呼び出します。
図 2. ネストされたペンディング インテントを使用する場合のアプリ間通信の図。
暗黙的インテントの受信
アプリが受信できる暗黙的インテントをアドバタイズするには、マニフェスト ファイルの <intent-filter> 要素を使用して、アプリ コンポーネントごとに 1 つ以上のインテント フィルタを宣言します。各インテント フィルタは、インテントのアクション、データ、カテゴリに基づいて、受け入れるインテントのタイプを指定します。システムは、インテントがインテント フィルタのいずれかを通過できる場合にのみ、暗黙的インテントをアプリ コンポーネントに配信します。
注: 明示的インテントは、コンポーネントが宣言するインテント フィルタに関係なく、常にターゲットに配信されます。
アプリ コンポーネントは、実行できる一意のジョブごとに個別のフィルタを宣言する必要があります。たとえば、写真アプリの 1 つのアクティビティに、画像を表示するフィルタと画像を編集するフィルタの 2 つのフィルタを設定できます。アクティビティが開始されると、Intent を検査し、Intent の情報に基づいて動作を決定します(エディタ コントロールを表示するかどうかなど)。
各インテント フィルタは、アプリのマニフェスト ファイル内の <intent-filter> 要素で定義され、対応するアプリ コンポーネント(<activity> 要素など)にネストされます。
<intent-filter> 要素を含む各アプリ コンポーネントで、android:exported の値を明示的に設定します。この属性は、アプリ コンポーネントが他のアプリからアクセス可能かどうかを示します。インテント フィルタに LAUNCHER カテゴリが含まれているアクティビティなど、状況によっては、この属性を true に設定すると便利です。それ以外の場合は、この属性を false に設定する方が安全です。
警告: アプリのアクティビティ、サービス、ブロードキャスト レシーバがインテント フィルタを使用しており、android:exported の値を明示的に設定していない場合、Android 12 以降を搭載したデバイスにアプリをインストールすることはできません。
<intent-filter> 内で、次の 3 つの要素のうち 1 つ以上を使用して、受け入れるインテントのタイプを指定できます。
<action>name属性で、受け入れられるインテント アクションを宣言します。値は、クラス定数ではなく、アクションのリテラル文字列値にする必要があります。<data>- データ URI のさまざまな側面(
scheme、host、port、path)と MIME タイプを指定する 1 つ以上の属性を使用して、受け入れ可能なデータのタイプを宣言します。 <category>name属性で、受け入れられるインテント カテゴリを宣言します。値は、クラス定数ではなく、アクションのリテラル文字列値にする必要があります。注: 暗黙的インテントを受け取るには、インテント フィルタ内に
CATEGORY_DEFAULTカテゴリを含める必要があります。startActivity()メソッドとstartActivityForResult()メソッドは、CATEGORY_DEFAULTカテゴリを宣言しているものとして、すべてのインテントを処理します。インテント フィルタ内でこのカテゴリを宣言していない場合、暗黙的インテントはアクティビティに変換されません。
たとえば、データ型がテキストの場合に ACTION_SEND インテントを受信するインテント フィルタを含むアクティビティ宣言は次のようになります。
<activity android:name="ShareActivity" android:exported="false"> <intent-filter> <action android:name="android.intent.action.SEND"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="text/plain"/> </intent-filter> </activity>
<action>、<data>、<category> の複数のインスタンスを含むフィルタを作成できます。その場合は、コンポーネントがそれらのフィルタ要素のすべての組み合わせを処理できることを確認する必要があります。
複数の種類のインテントを処理するものの、アクション、データ、カテゴリタイプの特定の組み合わせでのみ処理する場合は、複数のインテント フィルタを作成する必要があります。
暗黙的インテントは、インテントを 3 つの要素のそれぞれと比較することで、フィルタに対してテストされます。インテントがコンポーネントに配信されるには、3 つのテストすべてに合格する必要があります。1 つでも一致しない場合、Android システムはインテントをコンポーネントに配信しません。ただし、コンポーネントには複数のインテント フィルタを設定できるため、コンポーネントのフィルタの 1 つを通過しないインテントが、別のフィルタを通過する可能性があります。システムがインテントを解決する方法について詳しくは、後述のインテントの解決をご覧ください。
注意: インテント フィルタを使用しても、他のアプリがコンポーネントを起動するのを防ぐことはできません。インテント フィルタは、特定の種類の暗黙的インテントにのみ応答するようにコンポーネントを制限しますが、デベロッパーがコンポーネント名を決定すると、別のアプリが明示的インテントを使用してアプリ コンポーネントを起動する可能性があります。自分のアプリのみがコンポーネントを起動できるようにすることが重要な場合は、マニフェストでインテント フィルタを宣言しないでください。代わりに、そのコンポーネントの exported 属性を "false" に設定します。
同様に、別のアプリの Service を誤って実行しないように、常に明示的インテントを使用して独自のサービスを開始します。
注: すべてのアクティビティについて、マニフェスト ファイルでインテント フィルタを宣言する必要があります。ただし、ブロードキャスト レシーバのフィルタは、registerReceiver() を呼び出すことで動的に登録できます。その後、unregisterReceiver() を使用してレシーバーの登録を解除できます。これにより、アプリの実行中に、指定した期間のみ特定のブロードキャストをリッスンできます。
フィルタの使用例
インテント フィルタの動作をいくつか示すため、ソーシャル共有アプリのマニフェスト ファイルの例を以下に示します。
<activity android:name="MainActivity" android:exported="true"> <!-- This activity is the main entry, should appear in app launcher --> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name="ShareActivity" android:exported="false"> <!-- This activity handles "SEND" actions with text data --> <intent-filter> <action android:name="android.intent.action.SEND"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="text/plain"/> </intent-filter> <!-- This activity also handles "SEND" and "SEND_MULTIPLE" with media data --> <intent-filter> <action android:name="android.intent.action.SEND"/> <action android:name="android.intent.action.SEND_MULTIPLE"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="application/vnd.google.panorama360+jpg"/> <data android:mimeType="image/*"/> <data android:mimeType="video/*"/> </intent-filter> </activity>
最初のアクティビティ MainActivity は、アプリのメイン エントリ ポイントです。ユーザーがランチャー アイコンを使用してアプリを最初に起動したときに開くアクティビティです。
ACTION_MAINアクションは、これがメインのエントリ ポイントであり、インテント データを想定していないことを示します。CATEGORY_LAUNCHERカテゴリは、このアクティビティのアイコンをシステムのアプリ ランチャーに配置する必要があることを示します。<activity>要素でiconを使用してアイコンが指定されていない場合、システムは<application>要素のアイコンを使用します。
アクティビティをアプリ ランチャーに表示するには、この 2 つをペアにする必要があります。
2 つ目のアクティビティ ShareActivity は、テキストとメディア コンテンツの共有を容易にすることを目的としています。ユーザーは MainActivity から移動してこのアクティビティに入ることもできますが、2 つのインテント フィルタのいずれかに一致する暗黙的インテントを発行する別のアプリから ShareActivity に直接入ることもできます。
注: MIME タイプ application/vnd.google.panorama360+jpg は、パノラマ写真を指定する特別なデータタイプです。Google パノラマ API で処理できます。
インテントと他のアプリのインテント フィルタを一致させる
別のアプリが Android 13(API レベル 33)以降をターゲットとしている場合、そのアプリは、インテントがそのアプリの <intent-filter> 要素のアクションとカテゴリに一致する場合にのみ、アプリのインテントを処理できます。一致するものが見つからない場合、システムは ActivityNotFoundException をスローします。送信側アプリはこの例外を処理する必要があります。
同様に、Android 13 以上をターゲットとするようにアプリをアップデートした場合、外部アプリから発信されるすべてのインテントは、アプリで宣言された <intent-filter> 要素のアクションとカテゴリに一致する場合にのみ、アプリのエクスポート済みコンポーネントに配信されます。この動作は、送信側アプリのターゲット SDK バージョンに関係なく発生します。
次の場合、インテント マッチングは適用されません。
- インテント フィルタを宣言していないコンポーネントに配信されるインテント。
- 同じアプリからのインテント。
- システムからのインテント、つまり「システム UID」(uid=1000)から送信されるインテント。システムアプリとしては、
system_serverと、android:sharedUserIdをandroid.uid.systemに設定するアプリがあります。 - ルートからのインテント。
詳しくは、インテント マッチングをご覧ください。
ペンディング インテントを使用する
PendingIntent オブジェクトは、Intent オブジェクトのラッパーです。PendingIntent の主な目的は、含まれている Intent を、アプリ独自のプロセスから実行されたかのように使用する権限を外部アプリに付与することです。
ペンディング インテントの主なユースケースは次のとおりです。
- ユーザーが Notification でアクションを実行したときに実行されるインテントを宣言する(Android システムの
NotificationManagerがIntentを実行します)。 - ユーザーがアプリ ウィジェットでアクションを実行したときに実行されるインテントを宣言する(ホーム画面アプリが
Intentを実行します)。 - 将来の指定した時刻に実行されるインテントを宣言する(Android システムの
AlarmManagerがIntentを実行します)。
各 Intent オブジェクトが特定のタイプのアプリ コンポーネント(Activity、Service、BroadcastReceiver のいずれか)によって処理されるように設計されているのと同様に、PendingIntent も同じ考慮事項に基づいて作成する必要があります。ペンディング インテントを使用する場合、アプリは startActivity() などの呼び出しでインテントを実行しません。代わりに、それぞれの作成メソッドを呼び出して PendingIntent を作成するときに、目的のコンポーネント タイプを宣言する必要があります。
PendingIntent.getActivity():Activityを開始するIntent。Serviceを開始するIntentのPendingIntent.getService()。BroadcastReceiverを開始するIntentのPendingIntent.getBroadcast()。
アプリが他のアプリからペンディング インテントを受信しない限り、PendingIntent を作成する上記の方法が、必要となる唯一の PendingIntent メソッドになる可能性があります。
各メソッドは、現在のアプリの Context、ラップする Intent、インテントの使用方法(インテントを複数回使用できるかどうかなど)を指定する 1 つ以上のフラグを受け取ります。
ペンディング インテントの使用方法について詳しくは、通知やアプリ ウィジェットの API ガイドなど、それぞれのユースケースに関するドキュメントをご覧ください。
可変性を指定する
Android 12 以降をターゲットとするアプリでは、アプリが作成する各 PendingIntent オブジェクトの可変性を指定する必要があります。特定の PendingIntent オブジェクトが可変または不変であることを宣言するには、それぞれ PendingIntent.FLAG_MUTABLE フラグまたは PendingIntent.FLAG_IMMUTABLE フラグを使用します。
アプリが可変性フラグを設定せずに PendingIntent オブジェクトを作成しようとすると、システムは IllegalArgumentException をスローし、Logcat に次のメッセージが表示されます。
PACKAGE_NAME: Targeting S+ (version 31 and above) requires that one of \
FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent.
Strongly consider using FLAG_IMMUTABLE, only use FLAG_MUTABLE if \
some functionality depends on the PendingIntent being mutable, e.g. if \
it needs to be used with inline replies or bubbles.
可能な限り不変のペンディング インテントを作成する
ほとんどの場合において、アプリでは不変の PendingIntent オブジェクトを作成するようにしてください(次のコード スニペットを参照)。PendingIntent オブジェクトが不変の場合、他のアプリはインテントを呼び出した結果を調整するためにインテントを変更できません。
Kotlin
val pendingIntent = PendingIntent.getActivity(applicationContext, REQUEST_CODE, intent, /* flags */ PendingIntent.FLAG_IMMUTABLE)
Java
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), REQUEST_CODE, intent, /* flags */ PendingIntent.FLAG_IMMUTABLE);
ただし、特定のユースケースでは、代わりに可変の PendingIntent オブジェクトが必要になります。
- 通知でのダイレクト返信アクションをサポートする。ダイレクト返信では、返信に関連付けられた PendingIntent オブジェクトのクリップデータを変更する必要があります。通常、この変更をリクエストするには、
fillIn()メソッドにフラグとしてFILL_IN_CLIP_DATAを渡します。 CarAppExtenderのインスタンスを使用して、通知を Android Auto フレームワークに関連付ける。PendingIntentのインスタンスを使用して、会話をバブルに配置する。変更可能なPendingIntentオブジェクトを使用すると、システムはFLAG_ACTIVITY_MULTIPLE_TASKやFLAG_ACTIVITY_NEW_DOCUMENTなどの正しいフラグを適用できます。requestLocationUpdates()または同様の API を呼び出して、デバイスの位置情報をリクエストする。変更可能なPendingIntentオブジェクトを使用すると、システムは位置情報のライフサイクル イベントを表すインテント エクストラを追加できます。これらのイベントには、場所の変更やプロバイダが利用可能になることが含まれます。AlarmManagerを使用してアラームをスケジュールする。 変更可能なPendingIntentオブジェクトを使用すると、システムはEXTRA_ALARM_COUNTインテント エクストラを追加できます。このエクストラは、繰り返しアラームがトリガーされた回数を表します。このエクストラを含めることで、インテントは、デバイスがスリープ状態のときなど、繰り返しアラームが複数回トリガーされたかどうかをアプリに正確に通知できます。
アプリが変更可能な PendingIntent オブジェクトを作成する場合は、明示的インテントを使用して ComponentName を入力することを強くおすすめします。そうすれば、別のアプリが PendingIntent を呼び出してアプリに制御を戻すたびに、常にアプリ内の同じコンポーネントが開始されます。
ペンディング インテント内で明示的インテントを使用する
他のアプリがアプリのペンディング インテントを使用する方法をより明確に定義するには、常にペンディング インテントを明示的インテントでラップします。このベスト プラクティスを実践するには、次の操作を行います。
- ベース インテントのアクション、パッケージ、コンポーネントの各フィールドが設定されていることを確認します。
-
Android 6.0(API レベル 23)で追加された
FLAG_IMMUTABLEを使用して、ペンディング インテントを作成します。このフラグにより、PendingIntentを受け取るアプリが未入力のプロパティに入力できなくなります。アプリのminSdkVersionが22以下の場合は、次のコードを使用して安全性と互換性を同時に提供できます。if (Build.VERSION.SDK_INT >= 23) { // Create a PendingIntent using FLAG_IMMUTABLE. } else { // Existing code that creates a PendingIntent. }
インテントの解決
システムは、アクティビティを開始する暗黙的インテントを受け取ると、次の 3 つの側面に基づいてインテント フィルタと比較し、インテントに最適なアクティビティを検索します。
- アクション。
- データ(URI とデータタイプの両方)。
- カテゴリ。
以降のセクションでは、アプリのマニフェスト ファイルのインテント フィルタ宣言に従って、インテントが適切なコンポーネントにどのようにマッチングされるかについて説明します。
アクション テスト
受け入れ可能なインテント アクションを指定するには、次の例に示すように、インテント フィルタで 0 個以上の <action> 要素を宣言します。
<intent-filter> <action android:name="android.intent.action.EDIT" /> <action android:name="android.intent.action.VIEW" /> ... </intent-filter>
このフィルタを通過するには、Intent で指定されたアクションが、フィルタにリストされているアクションのいずれかと一致する必要があります。
フィルタにアクションがリストされていない場合、インテントが一致するものがないため、すべてのインテントがテストに失敗します。ただし、Intent でアクションが指定されていない場合は、フィルタに少なくとも 1 つのアクションが含まれていれば、テストに合格します。
カテゴリテスト
受け入れ可能なインテント カテゴリを指定するには、インテント フィルタで 0 個以上の <category> 要素を宣言します。次に例を示します。
<intent-filter> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> ... </intent-filter>
インテントがカテゴリテストに合格するには、Intent 内のすべてのカテゴリがフィルタ内のカテゴリと一致する必要があります。逆は必要ありません。インテント フィルタは、Intent で指定されているよりも多くのカテゴリを宣言でき、Intent は引き続き渡されます。したがって、カテゴリのないインテントは、フィルタで宣言されているカテゴリに関係なく、常にこのテストに合格します。
注:
Android は、startActivity() と startActivityForResult() に渡されるすべての暗黙的インテントに CATEGORY_DEFAULT カテゴリを自動的に適用します。
アクティビティで暗黙的インテントを受け取るには、前述の <intent-filter> の例のように、インテント フィルタに "android.intent.category.DEFAULT" のカテゴリを含める必要があります。
データテスト
受け入れ可能なインテント データを指定するために、インテント フィルタは、次の例に示すように、0 個以上の <data> 要素を宣言できます。
<intent-filter> <data android:mimeType="video/mpeg" android:scheme="http" ... /> <data android:mimeType="audio/mpeg" android:scheme="http" ... /> ... </intent-filter>
各 <data> 要素では、URI 構造とデータ型(MIME メディアタイプ)を指定できます。URI の各部分は、scheme、host、port、path の個別の属性です。
<scheme>://<host>:<port>/<path>
次の例は、これらの属性に指定できる値を示しています。
content://com.example.project:200/folder/subfolder/etc
この URI では、スキームは content、ホストは com.example.project、ポートは 200、パスは folder/subfolder/etc です。
これらの属性はそれぞれ <data> 要素では省略可能ですが、次のような線形依存関係があります。
- スキームが指定されていない場合、ホストは無視されます。
- ホストが指定されていない場合、ポートは無視されます。
- スキームとホストの両方が指定されていない場合、パスは無視されます。
インテントの URI がフィルタの URI 仕様と比較される場合、フィルタに含まれる URI の部分のみが比較されます。次に例を示します。
- フィルタでスキームのみが指定されている場合、そのスキームを持つすべての URI がフィルタに一致します。
- フィルタでスキームとオーソリティが指定され、パスが指定されていない場合、パスに関係なく、同じスキームとオーソリティを持つすべての URI がフィルタを通過します。
- フィルタでスキーム、オーソリティ、パスが指定されている場合、同じスキーム、オーソリティ、パスを持つ URI のみがフィルタを通過します。
注: パス仕様にワイルドカードのアスタリスク(*)を含めて、パス名の一部のみが一致するように指定できます。
データテストでは、インテント内の URI と MIME タイプの両方を、フィルタで指定された URI と MIME タイプと比較します。ルールは次のとおりです。
- URI も MIME タイプも含まないインテントは、フィルタで URI も MIME タイプも指定されていない場合にのみ、テストに合格します。
- URI を含み、MIME タイプを含まない(明示的でも URI から推測可能でもない)インテントは、URI がフィルタの URI 形式と一致し、フィルタが同様に MIME タイプを指定していない場合にのみ、テストに合格します。
- MIME タイプを含むが URI を含まないインテントは、フィルタが同じ MIME タイプをリストし、URI 形式を指定していない場合にのみ、テストに合格します。
- URI と MIME タイプ(明示的または URI から推測可能)の両方を含むインテントは、そのタイプがフィルタにリストされているタイプと一致する場合にのみ、テストの MIME タイプ部分に合格します。URI がフィルタ内の URI と一致する場合、または
content:URI かfile:URI があり、フィルタで URI が指定されていない場合、テストの URI 部分に合格します。つまり、フィルタに MIME タイプのみがリストされている場合、コンポーネントはcontent:とfile:のデータをサポートしていると想定されます。
注: インテントで URI または MIME タイプが指定されている場合、<intent-filter> に <data> 要素がないと、データテストは失敗します。
最後のルール(d)は、コンポーネントがファイルまたはコンテンツ プロバイダからローカルデータを取得できるという期待を反映しています。そのため、フィルタではデータ型のみをリストし、content: スキームと file: スキームを明示的に指定する必要はありません。次の例は、<data> 要素が、コンポーネントがコンテンツ プロバイダから画像データを取得して表示できることを Android に伝える一般的なケースを示しています。
<intent-filter> <data android:mimeType="image/*" /> ... </intent-filter>
利用可能なデータのほとんどはコンテンツ プロバイダによって提供されるため、URI ではなくデータタイプを指定するフィルタが最も一般的です。
もう 1 つの一般的な構成は、スキームとデータタイプを含むフィルタです。たとえば、次のような <data> 要素は、コンポーネントがアクションを実行するためにネットワークから動画データを取得できることを Android に伝えます。
<intent-filter> <data android:scheme="http" android:mimeType="video/*" /> ... </intent-filter>
インテント マッチング
インテントは、インテント フィルタと照合され、アクティブ化するターゲット コンポーネントを検出するだけでなく、デバイス上のコンポーネント セットに関する情報を検出します。たとえば、Google Home アプリは、ACTION_MAIN アクションと CATEGORY_LAUNCHER カテゴリを指定するインテント フィルタを持つすべてのアクティビティを検索して、アプリ ランチャーにデータを入力します。IntentFilter クラスのドキュメントに記載されているように、インテントのアクションとカテゴリがフィルタと一致する場合にのみ、一致が成功します。
アプリは、Google Home アプリと同様の方法でインテント マッチングを使用できます。PackageManager には、特定のインテントを受け入れることができるすべてのコンポーネントを返す query...() メソッドのセットと、インテントに応答する最適なコンポーネントを決定する resolve...() メソッドの同様のシリーズがあります。たとえば、queryIntentActivities() は、引数として渡されたインテントを実行できるすべてのアクティビティのリストを返し、queryIntentServices() は、同様のサービスのリストを返します。どちらのメソッドもコンポーネントを有効にするのではなく、応答可能なコンポーネントを一覧表示するだけです。ブロードキャスト レシーバにも同様のメソッド queryBroadcastReceivers() があります。