Intents et filtres d'intents

Un Intent est un objet de messagerie que vous pouvez utiliser pour demander une action à partir d'un autre composant d'application. Bien que les intents facilitent la communication entre les composants de plusieurs manières, il existe trois cas d'utilisation fondamentaux:

  • Démarrer une activité

    Un Activity représente un seul écran dans une application. Vous pouvez démarrer une nouvelle instance d'Activity en transmettant un Intent à startActivity(). L'élément Intent décrit l'activité à démarrer et contient les données nécessaires.

    Si vous souhaitez recevoir un résultat de l'activité lorsqu'elle se termine, appelez startActivityForResult(). Votre activité reçoit le résultat en tant qu'objet Intent distinct dans le rappel onActivityResult() de votre activité. Pour en savoir plus, consultez le guide Activités.

  • Démarrer un service

    Un Service est un composant qui effectue des opérations en arrière-plan sans interface utilisateur. Avec Android 5.0 (niveau d'API 21) ou version ultérieure, vous pouvez démarrer un service avec JobScheduler. Pour en savoir plus sur JobScheduler, consultez sa API-reference documentation.

    Pour les versions antérieures à Android 5.0 (niveau d'API 21), vous pouvez démarrer un service à l'aide des méthodes de la classe Service. Vous pouvez démarrer un service pour effectuer une opération unique (telle que le téléchargement d'un fichier) en transmettant un Intent à startService(). Le Intent décrit le service à démarrer et contient toutes les données nécessaires.

    Si le service est conçu avec une interface client-serveur, vous pouvez le lier à partir d'un autre composant en transmettant un Intent à bindService(). Pour en savoir plus, consultez le guide Services.

  • Diffuser une annonce

    Une annonce est un message que n'importe quelle application peut recevoir. Le système diffuse différentes diffusions d'événements système, par exemple lorsque le système démarre ou que l'appareil commence à se charger. Vous pouvez diffuser une annonce à d'autres applications en transmettant un Intent à sendBroadcast() ou sendOrderedBroadcast().

Le reste de cette page explique le fonctionnement des intents et comment les utiliser. Pour en savoir plus, consultez Interagir avec d'autres applications et Partager du contenu.

Types d'intents

Il existe deux types d'intents:

  • Les intents explicites spécifient le composant de l'application qui satisferont l'intent, en spécifiant un ComponentName complet. Vous utiliserez généralement un intent explicite pour démarrer un composant de votre propre application, car vous connaissez le nom de classe de l'activité ou du service que vous souhaitez démarrer. Par exemple, vous pouvez démarrer une nouvelle activité dans votre application en réponse à une action de l'utilisateur ou démarrer un service pour télécharger un fichier en arrière-plan.
  • Les intents implicites ne nomment pas un composant spécifique, mais déclarent une action générale à effectuer, ce qui permet à un composant d'une autre application de la gérer. Par exemple, si vous souhaitez afficher un lieu sur une carte, vous pouvez utiliser un intent implicite pour demander qu'une autre application capable d'afficher un lieu spécifié sur une carte.

La figure 1 montre comment un intent est utilisé lors du démarrage d'une activité. Lorsque l'objet Intent nomme explicitement un composant d'activité spécifique, le système lance immédiatement ce composant.

Figure 1 : Manière dont un intent implicite est transmis via le système pour démarrer une autre activité: [1] Activity A crée un Intent avec une description d'action et le transmet à startActivity(). [2] Le système Android recherche dans toutes les applications un filtre d'intent correspondant à l'intent. Lorsqu'une correspondance est trouvée, [3] le système démarre l'activité correspondante (Activité B) en appelant sa méthode onCreate() et en lui transmettant le Intent.

Lorsque vous utilisez un intent implicite, le système Android trouve le composant approprié pour commencer par comparer le contenu de l'intent aux filtres d'intent déclarés dans le fichier manifeste d'autres applications sur l'appareil. Si l'intent correspond à un filtre d'intent, le système démarre ce composant et lui envoie l'objet Intent. Si plusieurs filtres d'intent sont compatibles, le système affiche une boîte de dialogue permettant à l'utilisateur de choisir l'application à utiliser.

Un filtre d'intent est une expression du fichier manifeste d'une application qui spécifie le type d'intents que le composant souhaite recevoir. Par exemple, en déclarant un filtre d'intent pour une activité, vous permettez à d'autres applications de démarrer directement votre activité avec un certain type d'intent. De même, si vous ne déclarez pas de filtres d'intent pour une activité, celle-ci ne peut être démarrée qu'avec un intent explicite.

Attention:Pour vous assurer que votre application est sécurisée, utilisez toujours un intent explicite lorsque vous démarrez Service et ne déclarez pas de filtres d'intent pour vos services. L'utilisation d'un intent implicite pour démarrer un service représente un risque de sécurité, car vous ne pouvez pas savoir avec certitude quel service répondra à l'intent, et l'utilisateur ne peut pas voir quel service démarre. À partir d'Android 5.0 (niveau d'API 21), le système génère une exception si vous appelez bindService() avec un intent implicite.

Créer un intent

Un objet Intent contient des informations que le système Android utilise pour déterminer quel composant démarrer (telles que le nom exact du composant ou la catégorie de composant qui doit recevoir l'intent), ainsi que des informations que le composant destinataire utilise afin d'effectuer correctement l'action (telles que l'action à effectuer et les données sur lesquelles agir).

Les principales informations contenues dans un Intent sont les suivantes:

Nom du composant
Nom du composant à démarrer.

C'est facultatif, mais c'est l'information critique qui rend un intent explicite, ce qui signifie qu'il ne doit être transmis qu'au composant d'application défini par le nom du composant. Sans nom de composant, l'intent est implicite et le système décide quel composant doit recevoir l'intent en fonction des autres informations d'intent (telles que l'action, les données et la catégorie, décrites ci-dessous). Si vous devez démarrer un composant spécifique dans votre application, vous devez spécifier le nom du composant.

Remarque:Lorsque vous démarrez un Service, spécifiez toujours le nom du composant. Sinon, vous ne pourrez pas savoir avec certitude quel service répondra à l'intent, et l'utilisateur ne pourra pas voir quel service démarre.

Ce champ Intent est un objet ComponentName, que vous pouvez spécifier à l'aide d'un nom de classe complet du composant cible, y compris le nom de package de l'application, par exemple com.example.ExampleActivity. Vous pouvez définir le nom du composant avec setComponent(), setClass(), setClassName() ou avec le constructeur Intent.

Action
Chaîne spécifiant l'action générique à effectuer (view ou pick, par exemple).

Dans le cas d'un intent de diffusion, il s'agit de l'action qui a eu lieu et est signalée. L'action détermine en grande partie la façon dont le reste de l'intent est structuré, en particulier les informations contenues dans les données et les extras.

Vous pouvez spécifier vos propres actions à utiliser par les intents dans votre application (ou à utiliser par d'autres applications pour appeler des composants de votre application), mais vous spécifiez généralement des constantes d'action définies par la classe Intent ou d'autres classes du framework. Voici quelques actions courantes pour démarrer une activité:

ACTION_VIEW
Utilisez cette action dans un intent avec startActivity() lorsque vous disposez d'informations qu'une activité peut montrer à l'utilisateur, comme une photo à afficher dans une application de galerie ou une adresse à afficher dans une application de carte.
ACTION_SEND
Également connu sous le nom d'intent share, vous devez l'utiliser dans un intent avec startActivity() lorsque vous disposez de données que l'utilisateur peut partager via une autre application, comme une application de messagerie ou de partage sur les réseaux sociaux.

Consultez la documentation de référence de la classe Intent pour découvrir d'autres constantes définissant des actions génériques. D'autres actions sont définies ailleurs dans le framework Android, par exemple dans Settings pour les actions qui ouvrent des écrans spécifiques dans l'application Paramètres du système.

Vous pouvez spécifier l'action d'un intent avec setAction() ou avec un constructeur Intent.

Si vous définissez vos propres actions, veillez à inclure le nom du package de votre application en tant que préfixe, comme illustré dans l'exemple suivant:

Kotlin

const val ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL"

Java

static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
Données
L'URI (objet Uri) qui référence les données sur lesquelles effectuer une action et/ou le type MIME de ces données. Le type de données fourni est généralement dicté par l'action de l'intent. Par exemple, si l'action est ACTION_EDIT, les données doivent contenir l'URI du document à modifier.

Lors de la création d'un intent, il est souvent important de spécifier le type de données (son type MIME) en plus de son URI. Par exemple, une activité capable d'afficher des images ne pourra probablement pas lire un fichier audio, même si les formats d'URI peuvent être similaires. Spécifier le type MIME de vos données permet au système Android de trouver le meilleur composant pour recevoir votre intent. Cependant, le type MIME peut parfois être déduit de l'URI, en particulier lorsque les données sont un URI content:. Un URI content: indique que les données sont situées sur l'appareil et contrôlées par un ContentProvider, ce qui rend le type de données MIME visible par le système.

Pour ne définir que l'URI de données, appelez setData(). Pour définir uniquement le type MIME, appelez setType(). Si nécessaire, vous pouvez les définir explicitement avec setDataAndType().

Attention:Si vous souhaitez définir à la fois l'URI et le type MIME, n'appelez pas setData() et setType(), car ils annulent tous deux la valeur de l'autre. Utilisez toujours setDataAndType() pour définir à la fois l'URI et le type MIME.

Catégorie
Chaîne contenant des informations supplémentaires sur le type de composant devant gérer l'intent. Vous pouvez placer un nombre illimité de descriptions de catégories dans un intent, mais la plupart des intents n'ont pas besoin de catégorie. Voici quelques catégories courantes :
CATEGORY_BROWSABLE
L'activité cible peut être lancée par un navigateur Web pour afficher des données référencées par un lien, telles qu'une image ou un e-mail.
CATEGORY_LAUNCHER
Il s'agit de l'activité initiale d'une tâche. Elle est répertoriée dans le lanceur d'applications du système.

Consultez la description de la classe Intent pour obtenir la liste complète des catégories.

Vous pouvez spécifier une catégorie avec addCategory().

Les propriétés répertoriées ci-dessus (nom du composant, action, données et catégorie) représentent les caractéristiques fondamentales d'un intent. En lisant ces propriétés, le système Android peut déterminer le composant d'application qu'il doit démarrer. Toutefois, un intent peut transmettre des informations supplémentaires qui n'affectent pas la résolution en un composant d'application. Un intent peut également fournir les informations suivantes:

Extras
Paires clé-valeur qui contiennent les informations supplémentaires requises pour effectuer l'action demandée. Tout comme certaines actions utilisent des types d'URI de données spécifiques, d'autres utilisent des extras particuliers.

Vous pouvez ajouter des données supplémentaires à l'aide de différentes méthodes putExtra(), chacune acceptant deux paramètres: le nom de la clé et la valeur. Vous pouvez également créer un objet Bundle avec toutes les données supplémentaires, puis insérer Bundle dans Intent avec putExtras().

Par exemple, lorsque vous créez un intent pour envoyer un e-mail avec ACTION_SEND, vous pouvez spécifier le destinataire to avec la clé EXTRA_EMAIL et l'objet avec la clé EXTRA_SUBJECT.

La classe Intent spécifie de nombreuses constantes EXTRA_* pour les types de données standardisés. Si vous devez déclarer vos propres clés supplémentaires (pour les intents reçus par votre application), veillez à inclure le nom du package de votre application en tant que préfixe, comme illustré dans l'exemple suivant:

Kotlin

const val EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS"

Java

static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";

Attention: N'utilisez pas les données Parcelable ou Serializable lorsque vous envoyez un intent qu'une autre application devrait recevoir. Si une application tente d'accéder aux données d'un objet Bundle, mais n'a pas accès à la classe morcelée ou sérialisée, le système génère une erreur RuntimeException.

Indicateurs
Les options sont définies dans la classe Intent, qui fonctionnent comme des métadonnées pour l'intent. Les indicateurs peuvent indiquer au système Android comment lancer une activité (par exemple, à quelle tâche l'activité doit appartenir) et comment la traiter après son lancement (par exemple, si elle figure dans la liste des activités récentes).

Pour en savoir plus, consultez la méthode setFlags().

Exemple d'intent explicite

Un intent explicite vous permet de lancer un composant d'application spécifique, tel qu'une activité ou un service particulier dans votre application. Pour créer un intent explicite, définissez le nom du composant de l'objet Intent. Toutes les autres propriétés d'intent sont facultatives.

Par exemple, si vous avez créé un service nommé DownloadService dans votre application et conçu pour télécharger un fichier sur le Web, vous pouvez le démarrer avec le code suivant:

Kotlin

// Executed in an Activity, so 'this' is the Context
// 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 the Context
// 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);

Le constructeur Intent(Context, Class) fournit à l'application Context et au composant un objet Class. Par conséquent, cet intent lance explicitement la classe DownloadService dans l'application.

Pour en savoir plus sur la création et le démarrage d'un service, consultez le guide Services.

Exemple d'intent implicite

Un intent implicite spécifie une action pouvant appeler n'importe quelle application de l'appareil capable de l'effectuer. L'utilisation d'un intent implicite est utile lorsque votre application ne peut pas effectuer l'action, mais que d'autres applications le peuvent probablement et que vous souhaitez que l'utilisateur choisisse l'application à utiliser.

Par exemple, si vous souhaitez que l'utilisateur partage du contenu avec d'autres personnes, créez un intent avec l'action ACTION_SEND et ajoutez des éléments supplémentaires spécifiant le contenu à partager. Lorsque vous appelez startActivity() avec cet intent, l'utilisateur peut choisir une application via laquelle partager le contenu.

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.
}

Lorsque startActivity() est appelé, le système examine toutes les applications installées pour déterminer celles qui peuvent gérer ce type d'intent (intent avec l'action ACTION_SEND et contenant des données "text/plain"). Si une seule application peut le gérer, elle s'ouvre immédiatement et l'intent est attribué. Si aucune autre application ne peut le gérer, votre application peut intercepter l'ActivityNotFoundException qui se produit. Si plusieurs activités acceptent l'intent, le système affiche une boîte de dialogue telle que celle illustrée à la figure 2, afin que l'utilisateur puisse choisir l'application à utiliser.

Pour en savoir plus sur le lancement d'autres applications, consultez le guide sur l'envoi de l'utilisateur vers une autre application.

Figure 2. Boîte de dialogue de sélection.

Forcer un sélecteur d'application

Lorsque plusieurs applications répondent à votre intent implicite, l'utilisateur peut sélectionner l'application à utiliser et la définir comme choix par défaut pour l'action. La possibilité de sélectionner une valeur par défaut est utile lorsque vous effectuez une action pour laquelle l'utilisateur souhaite probablement utiliser la même application à chaque fois, par exemple lorsqu'il ouvre une page Web (les utilisateurs préfèrent souvent un seul navigateur Web).

Toutefois, si plusieurs applications peuvent répondre à l'intent et que l'utilisateur peut vouloir utiliser une application différente à chaque fois, vous devez afficher explicitement une boîte de dialogue de sélection. La boîte de dialogue du sélecteur demande à l'utilisateur de sélectionner l'application à utiliser pour l'action (il ne peut pas sélectionner d'application par défaut pour l'action). Par exemple, lorsque votre application effectue un "partage" avec l'action ACTION_SEND, les utilisateurs peuvent vouloir le partager à l'aide d'une autre application en fonction de leur situation actuelle. Vous devez donc toujours utiliser la boîte de dialogue du sélecteur, comme illustré dans la figure 2.

Pour afficher le sélecteur, créez un Intent à l'aide de createChooser() et transmettez-le à startActivity(), comme illustré dans l'exemple suivant. Cet exemple affiche une boîte de dialogue contenant la liste des applications qui répondent à l'intent transmis à la méthode createChooser() et utilise le texte fourni comme titre de la boîte de dialogue.

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);
}

Détecter les lancements d'intent non sécurisé

Votre application peut lancer des intents pour naviguer entre ses composants ou pour effectuer une action au nom d'une autre application. Pour améliorer la sécurité de la plate-forme, Android 12 (niveau d'API 31) ou version ultérieure fournit une fonctionnalité de débogage qui vous avertit si votre application effectue un lancement non sécurisé d'un intent. Par exemple, votre application peut effectuer le lancement non sécurisé d'un intent imbriqué, qui est transmis en tant qu'extra dans un autre intent.

Si votre application effectue les deux actions suivantes, le système détecte un lancement d'intent non sécurisé, et un cas de non-respect de StrictMode se produit:

  1. Votre application dissocie un intent imbriqué des extras d'un intent livré.
  2. Votre application démarre immédiatement un composant d'application à l'aide de cet intent imbriqué, par exemple en transmettant l'intent dans startActivity(), startService() ou bindService().

Pour savoir comment identifier cette situation et modifier votre application, consultez l'article de blog sur les intents d'imbrication Android sur Medium.

Rechercher les lancements d'intent non sécurisés

Pour vérifier les lancements d'intent non sécurisés dans votre application, appelez detectUnsafeIntentLaunch() lorsque vous configurez votre VmPolicy, comme indiqué dans l'extrait de code suivant. Si votre application détecte un cas de non-respect du StrictMode, vous pouvez arrêter son exécution pour protéger des informations potentiellement sensibles.

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());
}

Utiliser les intents de manière plus responsable

Pour réduire le risque de lancement d'intent non sécurisé et de non-respect du StrictMode, suivez ces bonnes pratiques.

Ne copiez que les extras essentiels des intents, et effectuez les opérations d'assainissement et de validation nécessaires. Votre application peut copier les extras d'un intent vers un autre intent utilisé pour lancer un nouveau composant. Cela se produit lorsque votre application appelle putExtras(Intent) ou putExtras(Bundle). Si votre application effectue l'une de ces opérations, ne copiez que les extras attendus par le composant récepteur. Si l'autre intent (qui reçoit la copie) lance un composant qui n'est pas exporté, nettoyez et validez les extras avant de les copier dans l'intent qui lance le composant.

N'exportez pas inutilement les composants de votre application. Par exemple, si vous avez l'intention de lancer un composant d'application à l'aide d'un intent imbriqué interne, définissez l'attribut android:exported de ce composant sur false.

Utilisez un PendingIntent au lieu d'un intent imbriqué. Ainsi, lorsqu'une autre application dissocie le PendingIntent de son élément Intent, l'autre peut lancer l'PendingIntent en utilisant l'identité de votre application. Cette configuration permet à l'autre application de lancer en toute sécurité n'importe quel composant, y compris un composant non exporté, dans votre application.

Le schéma de la figure 2 montre comment le système transmet le contrôle de votre application (cliente) à une autre application (de service), puis à votre application:

  1. Votre application crée un intent qui appelle une activité dans une autre application. Dans cet intent, vous ajoutez un objet PendingIntent en tant qu'élément supplémentaire. Cet intent en attente appelle un composant de votre application. Il n'est pas exporté.
  2. À la réception de l'intent de votre application, l'autre application extrait l'objet PendingIntent imbriqué.
  3. L'autre application appelle la méthode send() sur l'objet PendingIntent.
  4. Après avoir rendu le contrôle à votre application, le système appelle l'intent en attente à l'aide du contexte de votre application.

Figure 2. Schéma de la communication entre les applications lors de l'utilisation d'un intent en attente imbriqué.

Recevoir un intent implicite

Pour annoncer les intents implicites que votre application peut recevoir, déclarez un ou plusieurs filtres d'intent pour chacun des composants de votre application avec un élément <intent-filter> dans votre fichier manifeste. Chaque filtre d'intent spécifie le type d'intents qu'il accepte en fonction de l'action, des données et de la catégorie de l'intent. Le système ne fournit un intent implicite à votre composant d'application que s'il peut passer par l'un de vos filtres d'intent.

Remarque:Un intent explicite est toujours envoyé à sa cible, quels que soient les filtres d'intent déclarés par le composant.

Un composant d'application doit déclarer des filtres distincts pour chaque tâche unique qu'il peut effectuer. Par exemple, une activité dans une application de galerie d'images peut avoir deux filtres: un pour afficher une image et un autre pour modifier une image. Lorsque l'activité démarre, elle inspecte l'élément Intent et détermine le comportement à adopter en fonction des informations contenues dans le fichier Intent (par exemple, afficher ou non les commandes de l'éditeur).

Chaque filtre d'intent est défini par un élément <intent-filter> dans le fichier manifeste de l'application, imbriqué dans le composant d'application correspondant (tel qu'un élément <activity>).

Dans chaque composant d'application qui inclut un élément <intent-filter>, définissez explicitement une valeur pour android:exported. Cet attribut indique si d'autres applications peuvent accéder au composant d'application. Dans certaines situations, telles que les activités dont les filtres d'intent incluent la catégorie LAUNCHER, il est utile de définir cet attribut sur true. Sinon, il est préférable de définir cet attribut sur false.

Avertissement:Si une activité, un service ou un broadcast receiver de votre application utilise des filtres d'intent et ne définit pas explicitement la valeur pour android:exported, votre application ne peut pas être installée sur un appareil équipé d'Android 12 ou version ultérieure.

Dans le <intent-filter>, vous pouvez spécifier le type d'intents à accepter en utilisant un ou plusieurs de ces trois éléments:

<action>
Déclare que l'action d'intent est acceptée dans l'attribut name. La valeur doit être la valeur de chaîne littérale d'une action, et non la constante de la classe.
<data>
Déclare le type de données accepté, à l'aide d'un ou de plusieurs attributs spécifiant différents aspects de l'URI de données (scheme, host, port, path) et du type MIME.
<category>
Déclare la catégorie d'intent acceptée dans l'attribut name. La valeur doit être la valeur de chaîne littérale d'une action, et non la constante de la classe.

Remarque:Pour recevoir des intents implicites, vous devez inclure la catégorie CATEGORY_DEFAULT dans le filtre d'intent. Les méthodes startActivity() et startActivityForResult() traitent tous les intents comme s'ils avaient déclaré la catégorie CATEGORY_DEFAULT. Si vous ne déclarez pas cette catégorie dans votre filtre d'intent, aucun intent implicite n'atteindra votre activité.

Par exemple, voici une déclaration d'activité avec un filtre d'intent permettant de recevoir un intent ACTION_SEND lorsque le type de données est texte:

<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>

Vous pouvez créer un filtre qui inclut plusieurs instances de <action>, <data> ou <category>. Dans ce cas, vous devez vous assurer que le composant peut gérer n'importe quelle combinaison de ces éléments de filtre.

Lorsque vous souhaitez gérer plusieurs types d'intents, mais uniquement dans des combinaisons spécifiques d'action, de données et de type de catégorie, vous devez créer plusieurs filtres d'intent.

Un intent implicite est testé par rapport à un filtre en le comparant à chacun des trois éléments. Pour être livré au composant, l'intent doit réussir les trois tests. S'il ne parvient pas à mettre en correspondance ne serait-ce qu'un seul d'entre eux, le système Android ne transmettra pas l'intent au composant. Toutefois, étant donné qu'un composant peut comporter plusieurs filtres d'intent, un intent qui ne passe pas par l'un des filtres d'un composant peut passer par un autre filtre. Vous trouverez plus d'informations sur la manière dont le système résout les intents dans la section ci-dessous sur la résolution des intents.

Attention : L'utilisation d'un filtre d'intent n'est pas un moyen sécurisé d'empêcher d'autres applications de démarrer vos composants. Bien que les filtres d'intent limitent un composant à ne répondre qu'à certains types d'intents implicites, une autre application peut potentiellement démarrer votre composant d'application à l'aide d'un intent explicite si le développeur détermine les noms de vos composants. S'il est important que seule votre propre application puisse démarrer l'un de vos composants, ne déclarez pas de filtres d'intent dans votre fichier manifeste. Définissez plutôt l'attribut exported sur "false" pour ce composant.

De même, pour éviter d'exécuter par inadvertance le Service d'une autre application, utilisez toujours un intent explicite pour démarrer votre propre service.

Remarque:Pour toutes les activités, vous devez déclarer vos filtres d'intent dans le fichier manifeste. Toutefois, les filtres des broadcast receivers peuvent être enregistrés de manière dynamique en appelant registerReceiver(). Vous pouvez ensuite annuler l'enregistrement du récepteur auprès de unregisterReceiver(). Cela permet à votre application d'écouter des annonces spécifiques pendant une période spécifique seulement pendant son exécution.

Exemples de filtres

Pour illustrer certains comportements des filtres d'intent, voici un exemple tiré du fichier manifeste d'une application de partage sur les réseaux sociaux:

<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>

La première activité, MainActivity, est le point d'entrée principal de l'application. Il s'agit de l'activité qui s'ouvre lorsque l'utilisateur lance l'application pour la première fois avec l'icône de lanceur:

  • L'action ACTION_MAIN indique qu'il s'agit du point d'entrée principal et qu'il n'attend aucune donnée d'intent.
  • La catégorie CATEGORY_LAUNCHER indique que l'icône de cette activité doit être placée dans le lanceur d'applications du système. Si l'élément <activity> ne spécifie pas d'icône avec icon, le système utilise l'icône de l'élément <application>.

Ces deux éléments doivent être associés pour que l'activité apparaisse dans le lanceur d'applications.

La deuxième activité, ShareActivity, est destinée à faciliter le partage de texte et de contenu multimédia. Bien que les utilisateurs puissent entrer cette activité en y accédant depuis MainActivity, ils peuvent également saisir ShareActivity directement à partir d'une autre application qui émet un intent implicite correspondant à l'un des deux filtres d'intent.

Remarque:Le type MIME, application/vnd.google.panorama360+jpg, est un type de données spécial qui spécifie des photos panoramiques, que vous pouvez gérer avec les API Google panorama.

Faire correspondre des intents aux filtres d'intent d'autres applications

Si une autre application cible Android 13 (niveau d'API 33) ou une version ultérieure, elle ne peut gérer l'intent de votre application que si celui-ci correspond aux actions et aux catégories d'un élément <intent-filter> dans cette autre application. Si le système ne trouve pas de correspondance, il génère une ActivityNotFoundException. L'application émettrice doit gérer cette exception.

De même, si vous mettez à jour votre application pour qu'elle cible Android 13 ou une version ultérieure, tous les intents provenant d'applications externes ne sont transmis à un composant exporté de votre application que si cet intent correspond aux actions et aux catégories d'un élément <intent-filter> déclaré par votre application. Ce comportement se produit quelle que soit la version du SDK cible de l'application émettrice.

Dans les cas suivants, la mise en correspondance des intents n'est pas appliquée:

  • Intents envoyés aux composants qui ne déclarent aucun filtre d'intent
  • Intents provenant de la même application
  • Intents provenant du système, c'est-à-dire envoyés à partir de l'"UID du système" (uid=1000) Les applications système incluent system_server et les applications qui définissent android:sharedUserId sur android.uid.system.
  • Intents provenant de la racine.

En savoir plus sur la correspondance d'intent

Utiliser un intent en attente

Un objet PendingIntent est un wrapper autour d'un objet Intent. L'objectif principal d'un PendingIntent est d'autoriser une application étrangère à utiliser le Intent contenu comme si elle était exécutée à partir du processus de votre application.

Voici quelques cas d'utilisation principaux d'un intent en attente:

  • Déclarer un intent à exécuter lorsque l'utilisateur effectue une action avec votre notification (le NotificationManager du système Android exécute la Intent).
  • Déclarer un intent à exécuter lorsque l'utilisateur effectue une action avec votre widget d'application (l'application de l'écran d'accueil exécute Intent).
  • Déclarer un intent à exécuter à un moment précis spécifié (le AlarmManager du système Android exécute Intent)

Tout comme chaque objet Intent est conçu pour être géré par un type de composant d'application spécifique (Activity, Service ou BroadcastReceiver), un PendingIntent doit également être créé avec la même considération. Lorsque vous utilisez un intent en attente, votre application ne l'exécute pas avec un appel tel que startActivity(). À la place, vous devez déclarer le type de composant prévu lorsque vous créez l'PendingIntent en appelant la méthode de créateur correspondante:

À moins que votre application reçoive des intents en attente d'autres applications, les méthodes ci-dessus pour créer un PendingIntent sont probablement les seules méthodes PendingIntent dont vous aurez besoin.

Chaque méthode utilise le Context d'application actuel, le Intent que vous souhaitez encapsuler et un ou plusieurs indicateurs qui spécifient comment l'intent doit être utilisé (par exemple, si l'intent peut être utilisé plusieurs fois).

Pour en savoir plus sur l'utilisation des intents en attente, consultez la documentation de chacun des cas d'utilisation, par exemple dans les guides d'API Notifications et Widgets d'application.

Spécifier la mutabilité

Si votre application cible Android 12 ou une version ultérieure, vous devez spécifier la mutabilité de chaque objet PendingIntent créé par votre application. Pour déclarer qu'un objet PendingIntent donné est modifiable ou immuable, utilisez respectivement l'indicateur PendingIntent.FLAG_MUTABLE ou PendingIntent.FLAG_IMMUTABLE.

Si votre application tente de créer un objet PendingIntent sans définir aucun indicateur de mutabilité, le système génère une IllegalArgumentException et le message suivant apparaît dans 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.

Créez des intents en attente immuables dans la mesure du possible

Dans la plupart des cas, votre application doit créer des objets PendingIntent immuables, comme indiqué dans l'extrait de code suivant. Si un objet PendingIntent est immuable, les autres applications ne peuvent pas modifier l'intent pour ajuster le résultat de l'appel de l'intent.

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);

Cependant, certains cas d'utilisation nécessitent à la place des objets PendingIntent modifiables:

  • Prise en charge des actions de réponse directe dans les notifications. La réponse directe nécessite de modifier les données sur les extraits dans l'objet PendingIntent associé à la réponse. Généralement, vous demandez cette modification en transmettant FILL_IN_CLIP_DATA en tant qu'indicateur à la méthode fillIn().
  • Associer des notifications au framework Android Auto à l'aide d'instances de CarAppExtender
  • Placer des conversations dans des bulles à l'aide d'instances de PendingIntent Un objet PendingIntent modifiable permet au système d'appliquer les indicateurs appropriés, tels que FLAG_ACTIVITY_MULTIPLE_TASK et FLAG_ACTIVITY_NEW_DOCUMENT.
  • demander des informations de localisation de l'appareil en appelant requestLocationUpdates() ou des API similaires ; L'objet PendingIntent modifiable permet au système d'ajouter des extras d'intent qui représentent les événements de cycle de vie de l'établissement. Ces événements incluent un changement de lieu et la disponibilité d'un fournisseur.
  • Programmation d'alarmes avec AlarmManager L'objet PendingIntent modifiable permet au système d'ajouter l'intent supplémentaire EXTRA_ALARM_COUNT. Cet extra représente le nombre de fois où une alarme récurrente a été déclenchée. En contenant cet extra, l'intent peut indiquer avec précision à une application si une alarme récurrente a été déclenchée plusieurs fois, par exemple quand l'appareil était en veille.

Si votre application crée un objet PendingIntent modifiable, nous vous recommandons vivement d'utiliser un intent explicite et de remplir ComponentName. Ainsi, chaque fois qu'une autre application appelle PendingIntent et lui redonne le contrôle, le même composant dans votre application démarre toujours.

Utiliser des intents explicites dans des intents en attente

Pour mieux définir la manière dont d'autres applications peuvent utiliser les intents en attente de votre application, encapsulez toujours un intent en attente autour d'un intent explicite. Pour suivre cette bonne pratique, procédez comme suit:

  1. Vérifiez que les champs d'action, de package et de composant de l'intent de base sont définis.
  2. Utilisez FLAG_IMMUTABLE, ajouté à Android 6.0 (niveau d'API 23), pour créer des intents en attente. Cet indicateur empêche les applications qui reçoivent un PendingIntent de remplir les propriétés non renseignées. Si la minSdkVersion de votre application est 22 ou inférieure, vous pouvez assurer la sécurité et la compatibilité à l'aide du code suivant:

    if (Build.VERSION.SDK_INT >= 23) {
      // Create a PendingIntent using FLAG_IMMUTABLE.
    } else {
      // Existing code that creates a PendingIntent.
    }

Résolution de l'intent

Lorsque le système reçoit un intent implicite pour démarrer une activité, il recherche la meilleure activité pour cet intent en la comparant aux filtres d'intent en fonction de trois aspects:

  • Action.
  • Données (URI et type de données).
  • Catégorie :

Les sections suivantes décrivent comment les intents sont mis en correspondance avec les composants appropriés en fonction de la déclaration du filtre d'intent dans le fichier manifeste d'une application.

Test d'action

Pour spécifier les actions d'intent acceptées, un filtre d'intent peut déclarer zéro ou plusieurs éléments <action>, comme illustré dans l'exemple suivant:

<intent-filter>
    <action android:name="android.intent.action.EDIT" />
    <action android:name="android.intent.action.VIEW" />
    ...
</intent-filter>

Pour transmettre ce filtre, l'action spécifiée dans Intent doit correspondre à l'une des actions répertoriées dans le filtre.

Si le filtre ne répertorie aucune action, rien ne correspond à un intent. Par conséquent, tous les intents échouent au test. Toutefois, si un élément Intent ne spécifie pas d'action, le test réussit tant que le filtre contient au moins une action.

Test de catégorie

Pour spécifier les catégories d'intent acceptées, un filtre d'intent peut déclarer zéro ou plusieurs éléments <category>, comme illustré dans l'exemple suivant:

<intent-filter>
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    ...
</intent-filter>

Pour qu'un intent réussisse le test de catégorie, chaque catégorie de Intent doit correspondre à une catégorie du filtre. L'inverse n'est pas nécessaire. Le filtre d'intent peut déclarer plus de catégories que ce qui est spécifié dans Intent, et le Intent peut toujours transmettre. Par conséquent, un intent sans catégorie réussit toujours ce test, quelles que soient les catégories déclarées dans le filtre.

Remarque:Android applique automatiquement la catégorie CATEGORY_DEFAULT à tous les intents implicites transmis à startActivity() et startActivityForResult(). Si vous souhaitez que votre activité reçoive des intents implicites, elle doit inclure une catégorie pour "android.intent.category.DEFAULT" dans ses filtres d'intent, comme indiqué dans l'exemple <intent-filter> précédent.

Test de données

Pour spécifier les données d'intent acceptées, un filtre d'intent peut déclarer zéro ou plusieurs éléments <data>, comme illustré dans l'exemple suivant:

<intent-filter>
    <data android:mimeType="video/mpeg" android:scheme="http" ... />
    <data android:mimeType="audio/mpeg" android:scheme="http" ... />
    ...
</intent-filter>

Chaque élément <data> peut spécifier une structure d'URI et un type de données (type de média MIME). Chaque partie de l'URI est un attribut distinct: scheme, host, port et path:

<scheme>://<host>:<port>/<path>

L'exemple suivant présente les valeurs possibles pour ces attributs:

content://com.example.project:200/folder/subfolder/etc

Dans cet URI, le schéma est content, l'hôte est com.example.project, le port est 200 et le chemin d'accès est folder/subfolder/etc.

Chacun de ces attributs est facultatif dans un élément <data>, mais il existe des dépendances linéaires:

  • Si aucun schéma n'est spécifié, l'hôte est ignoré.
  • Si aucun hôte n'est spécifié, le port est ignoré.
  • Si le schéma et l'hôte ne sont pas spécifiés, le chemin d'accès est ignoré.

Lorsque l'URI d'un intent est comparé à une spécification d'URI dans un filtre, il n'est comparé qu'aux parties de l'URI incluses dans le filtre. Par exemple :

  • Si un filtre ne spécifie qu'un schéma, tous les URI associés à ce schéma correspondent au filtre.
  • Si un filtre spécifie un schéma et une autorité, mais pas de chemin d'accès, tous les URI ayant le même schéma et la même autorité passent le filtre, quels que soient leurs chemins d'accès.
  • Si un filtre spécifie un schéma, une autorité et un chemin d'accès, seuls les URI ayant le même schéma, la même autorité et le même chemin d'accès le sont.

Remarque:Une spécification de chemin d'accès peut contenir un caractère générique astérisque (*) pour ne nécessiter qu'une correspondance partielle du nom de chemin.

Le test de données compare à la fois l'URI et le type MIME de l'intent à un URI et un type MIME spécifiés dans le filtre. Les règles sont les suivantes:

  1. Un intent qui ne contient ni URI ni type MIME ne réussit le test que si le filtre ne spécifie aucun URI ni type MIME.
  2. Un intent contenant un URI, mais aucun type MIME (ni explicite, ni inférable à partir de l'URI) ne réussit le test que si son URI correspond au format d'URI du filtre et que le filtre ne spécifie pas de type MIME.
  3. Un intent contenant un type MIME, mais pas d'URI, ne réussit le test que si le filtre répertorie le même type MIME et ne spécifie pas de format d'URI.
  4. Un intent qui contient à la fois un URI et un type MIME (explicite ou inférable à partir de l'URI) ne transmet la partie type MIME du test que si ce type correspond à un type répertorié dans le filtre. Elle transmet la partie URI du test soit si son URI correspond à un URI du filtre, soit s'il possède un URI content: ou file: et que le filtre ne spécifie pas d'URI. En d'autres termes, un composant est supposé prendre en charge les données content: et file: si son filtre ne répertorie que un type MIME.

Remarque:Si un intent spécifie un URI ou un type MIME, le test des données échouera si le <intent-filter> ne comporte aucun élément <data>.

Cette dernière règle, la règle (d), reflète l'attente que les composants puissent obtenir des données locales auprès d'un fournisseur de fichiers ou de contenu. Par conséquent, leurs filtres peuvent ne répertorier qu'un type de données et n'ont pas besoin de nommer explicitement les schémas content: et file:. L'exemple suivant montre un cas typique dans lequel un élément <data> indique à Android qu'il peut obtenir des données d'image auprès d'un fournisseur de contenu et les afficher:

<intent-filter>
    <data android:mimeType="image/*" />
    ...
</intent-filter>

Les filtres qui spécifient un type de données, mais pas un URI sont peut-être les plus courants, car la plupart des données disponibles sont distribuées par des fournisseurs de contenu.

Une autre configuration courante est un filtre avec un schéma et un type de données. Par exemple, un élément <data> comme celui-ci indique à Android que le composant peut récupérer des données vidéo sur le réseau afin d'effectuer l'action:

<intent-filter>
    <data android:scheme="http" android:mimeType="video/*" />
    ...
</intent-filter>

Correspondance d'intent

Les intents sont mis en correspondance avec les filtres d'intent non seulement pour découvrir un composant cible à activer, mais également pour obtenir des informations sur l'ensemble de composants de l'appareil. Par exemple, l'application Home remplit le lanceur d'applications en recherchant toutes les activités avec des filtres d'intent qui spécifient l'action ACTION_MAIN et la catégorie CATEGORY_LAUNCHER. Une correspondance n'est efficace que si les actions et les catégories de l'intent correspondent au filtre, comme décrit dans la documentation de la classe IntentFilter.

Votre application peut utiliser la mise en correspondance d'intent de la même manière que l'application Home. PackageManager comporte un ensemble de méthodes query...() qui renvoient tous les composants pouvant accepter un intent particulier et une série similaire de méthodes resolve...() qui déterminent le meilleur composant pour répondre à un intent. Par exemple, queryIntentActivities() renvoie une liste de toutes les activités pouvant exécuter l'intent transmis en tant qu'argument, et queryIntentServices() renvoie une liste similaire de services. Aucune de ces méthodes n'active les composants. Elle répertorie simplement ceux qui peuvent répondre. Il existe une méthode similaire, queryBroadcastReceivers(), pour les broadcast receivers.