Intents y filtros de intents

Un Intent es un objeto de transmisión de mensajes que puedes usar para solicitar una acción de otro componente de la app. Si bien las intents facilitan la comunicación entre componentes de varias formas, existen tres casos de uso fundamentales:

  • Cómo iniciar una actividad

    Un Activity representa una sola pantalla en una app. Puedes iniciar una instancia nueva de un Activity pasando un Intent a startActivity(). Intent describe la actividad que se debe iniciar y contiene los datos necesarios para ello.

    Si deseas recibir un resultado de la actividad cuando finalice, llama a startActivityForResult(). Tu actividad recibe el resultado como un objeto Intent separado en la devolución de llamada onActivityResult() de la actividad. Para obtener más información, consulta la guía de Actividades.

  • Cómo iniciar un servicio

    Un Service es un componente que realiza operaciones en segundo plano sin una interfaz de usuario. Con Android 5.0 (API nivel 21) y versiones posteriores, puedes iniciar un servicio con JobScheduler. Para obtener más información sobre JobScheduler, consulta su API-reference documentation.

    En las versiones anteriores a Android 5.0 (nivel de API 21), puedes iniciar un servicio usando métodos de la clase Service. Puedes iniciar un servicio para realizar una operación única (como descargar un archivo) pasando un Intent a startService(). El Intent describe el servicio que se debe iniciar y contiene los datos necesarios.

    Si el servicio está diseñado con una interfaz cliente-servidor, puedes establecer la vinculación con el servicio desde otro componente pasando un Intent a bindService(). Para obtener más información, consulta la guía Servicios.

  • Transmitir una transmisión

    Una emisión es un aviso que cualquier aplicación puede recibir. El sistema transmite varias emisiones de eventos, como cuando se inicia el sistema o comienza a cargarse el dispositivo. Puedes enviar una transmisión a otras apps pasando un Intent a sendBroadcast() o sendOrderedBroadcast().

El resto de esta página explica cómo funcionan las intents y cómo debes usarlas. Para obtener información relacionada, consulta Cómo interactuar con otras apps y Cómo compartir contenido.

Tipos de intents

Existen dos tipos de intents:

  • Los intents explícitos especifican qué componente de qué aplicación satisfará el intent especificando un ComponentName completo. Por lo general, el usuario usa una intent explícita para iniciar un componente en su propia aplicación porque conoce el nombre de clase de la actividad o el servicio que desea iniciar. Por ejemplo, puedes iniciar una actividad nueva en tu app en respuesta a una acción del usuario o iniciar un servicio para descargar un archivo en segundo plano.
  • Los intents implícitos no nombran un componente específico, sino que declaran una acción general para realizar, lo que permite que un componente de otra app la controle. Por ejemplo, si deseas mostrar al usuario una ubicación en un mapa, puedes usar un intent implícito para solicitar que otra app capaz muestre una ubicación específica en un mapa.

La figura 1 muestra cómo se usa una intent para iniciar una actividad. Cuando el objeto Intent nombra un componente de actividad específico de forma explícita, el sistema inicia ese componente de inmediato.

Figura 1: Cómo se entrega un intent implícito a través del sistema para iniciar otra actividad: [1] La actividad A crea un Intent con una descripción de acción y la pasa a startActivity(). [2] El sistema Android busca en todas las apps un filtro de intents que coincida con el intent. Cuando se encuentra una coincidencia, [3] el sistema inicia la actividad coincidente (Actividad B) invocando su método onCreate() y pasándole el Intent.

Cuando usas un intent implícito, el sistema Android busca el componente apropiado para iniciar comparando el contenido del intent con los filtros de intents declarados en el archivo de manifiesto de otras apps en el dispositivo. Si la intent coincide con un filtro de intents, el sistema inicia ese componente y le entrega el objeto Intent. Si varios filtros de intents son compatibles, el sistema muestra un cuadro de diálogo para que el usuario pueda elegir la aplicación que se debe usar.

Un filtro de intents es una expresión en el archivo de manifiesto de una app que especifica el tipo de intents que el componente podría recibir. Por ejemplo, declarar un filtro de intents para una actividad permite que otras apps la inicien directamente con un tipo de intent específico. Del mismo modo, si no declaras ningún filtro de intents para una actividad, esta solo se puede iniciar con un intent explícito.

Precaución: Para garantizar que tu app sea segura, siempre usa un intent explícito cuando inicies un Service y no declares filtros de intents para tus servicios. El uso de un intent explícito para iniciar un servicio es un riesgo de seguridad porque no puedes estar seguro de qué servicio responderá al intent, y el usuario no puede ver qué servicio se inicia. A partir de Android 5.0 (nivel de API 21), el sistema arroja una excepción si llamas a bindService() con un intent implícito.

Cómo crear una intent

Un objeto Intent contiene información que el sistema Android usa para determinar qué componente iniciar (como el nombre exacto del componente o la categoría del componente que debe recibir la intent), además de la información que usa el componente receptor para realizar correctamente la acción (como la acción que se debe realizar y los datos en los que se debe actuar).

La información principal que contiene un Intent es la siguiente:

Nombre del componente
Es el nombre del componente que se debe iniciar.

Esto es opcional, pero es la información clave que hace que una intent sea explícita, lo que significa que la intent debe enviarse solamente al componente de la app definido en el nombre del componente. Sin un nombre de componente, el intent es implícito y el sistema decide qué componente debe recibirlo en función de la información adicional del intent (como la acción, los datos y la categoría, que se describen a continuación). Si necesitas iniciar un componente específico en tu app, debes especificar el nombre del componente.

Nota: Cuando inicies un Service, siempre especifica el nombre del componente. De lo contrario, no puedes estar seguro de qué servicio responderá al intent, y el usuario no puede ver qué servicio se inicia.

Este campo de Intent es un objeto ComponentName, que puedes especificar con un nombre de clase completamente calificado del componente de destino, incluido el nombre del paquete de la app, por ejemplo, com.example.ExampleActivity. Puedes establecer el nombre del componente con setComponent(), setClass(), setClassName() o con el constructor Intent.

Acción
Es una cadena que especifica la acción genérica que se debe realizar (como ver o elegir).

En el caso de la intent de una emisión, es la acción que se produjo y que se está registrando. La acción determina en gran medida cómo se estructura el resto de la intent, en particular la información que se incluye en los datos y extras.

Puedes especificar tus propias acciones para que las usen las intents en tu aplicación (o para que las usen otras apps a fin de invocar componentes en tu aplicación); pero, usualmente, debes especificar acciones constantes definidas por la clase Intent u otras clases del framework. Estas son algunas acciones comunes para iniciar una actividad:

ACTION_VIEW
Usa esta acción en un intent con startActivity() cuando tengas información que una actividad pueda mostrar al usuario, como una foto para ver en una app de galería o una dirección para ver en una app de mapas.
ACTION_SEND
También se conoce como la intent de compartir y debes usarla en una intent con startActivity() cuando tengas información que el usuario pueda compartir mediante otra app, como una app de correo electrónico o intercambio social.

Consulta la referencia de la clase Intent para conocer más constantes que definen las acciones genéricas. Otras acciones se definen en otras partes del framework de Android, como en Settings para las acciones que abren pantallas específicas en la app de Configuración del sistema.

Puedes especificar la acción de una intent con setAction() o con un constructor Intent.

Si defines tus propias acciones, asegúrate de incluir el nombre del paquete de la app como prefijo, tal como se muestra en el siguiente ejemplo:

Kotlin

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

Java

static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
Datos
El URI (un objeto Uri) que hace referencia a los datos en los que se debe realizar la acción o el tipo de MIME de esos datos. El tipo de datos proporcionados suele estar determinado por la acción del intent. Por ejemplo, si la acción es ACTION_EDIT, los datos deben contener el URI del documento que se debe editar.

Cuando creas un intent, a menudo es importante especificar el tipo de datos (su tipo de MIME) además de su URI. Por ejemplo, una actividad que puede mostrar imágenes probablemente no sea capaz de reproducir un archivo de audio aunque los formatos de URI sean similares. Especificar el tipo de MIME de tus datos ayuda al sistema Android a encontrar el mejor componente para recibir tu intent. Sin embargo, el tipo de MIME a veces se puede deducir del URI, especialmente cuando los datos son un URI content:. Un URI content: indica que los datos se encuentran en el dispositivo y son controlados por un ContentProvider, lo que hace que el sistema pueda ver el tipo de datos de MIME.

Para establecer solo el URI de datos, llama a setData(). Para establecer solo el tipo de MIME, llama a setType(). Si es necesario, puedes configurar ambos explícitamente con setDataAndType().

Precaución: Si deseas establecer tanto el URI como el tipo de MIME, no llames a setData() ni a setType(), ya que cada uno anula el valor del otro. Siempre usa setDataAndType() para establecer el URI y el tipo de MIME.

Categoría
Es una cadena que contiene información adicional sobre el tipo de componente que debe controlar el intent. En un intent, se puede incluir cualquier cantidad de descripciones de categorías, pero la mayoría de los intents no requieren una categoría. Estas son algunas categorías comunes:
CATEGORY_BROWSABLE
La actividad de destino permite que la inicie un navegador web para mostrar datos a los que hace referencia un vínculo, como una imagen o un mensaje de correo electrónico.
CATEGORY_LAUNCHER
La actividad es la actividad inicial de una tarea y está enumerada en el selector de la aplicación del sistema.

Consulta la descripción de la clase Intent para obtener la lista completa de categorías.

Puedes especificar una categoría con addCategory().

Las propiedades nombradas anteriormente (nombre de componente, acción, datos y categoría) representan las características definitorias de un intent. Mediante la lectura de estas propiedades, el sistema Android puede resolver qué componente de la app debe iniciar. Sin embargo, un intent puede llevar información adicional que no afecta cómo se resuelve en un componente de la app. Una intent también puede incluir la siguiente información:

Adicional
Pares clave-valor que contienen información adicional necesaria para realizar la acción solicitada. Al igual que algunas acciones usan tipos particulares de URI de datos, algunas acciones también usan extras específicos.

Puedes agregar datos adicionales con varios métodos putExtra(), cada uno de los cuales acepta dos parámetros: el nombre de la clave y el valor. También puedes crear un objeto Bundle con todos los datos adicionales y, luego, insertar el Bundle en el Intent con putExtras().

Por ejemplo, cuando creas un intent para enviar un correo electrónico con ACTION_SEND, puedes especificar el destinatario para con la clave EXTRA_EMAIL y el asunto con la clave EXTRA_SUBJECT.

La clase Intent especifica muchas constantes EXTRA_* para tipos de datos estandarizados. Si tienes que declarar tus propias claves extra (para intents que recibe tu app), asegúrate de incluir el nombre del paquete de la app como prefijo, tal como se muestra en el siguiente ejemplo:

Kotlin

const val EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS"

Java

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

Precaución: No uses datos de Parcelable ni Serializable cuando envíes un intent que esperas que reciba otra app. Si una app intenta acceder a datos de un objeto Bundle, pero no tiene acceso a la clase empaquetada o serializada, el sistema arroja un RuntimeException.

Marcas
Los indicadores se definen en la clase Intent que funciona como metadatos para la intent. Los indicadores pueden indicar al sistema Android cómo iniciar una actividad (por ejemplo, a qué tarea debe pertenecer) y cómo tratarla después de que se inicia (por ejemplo, si pertenece a la lista de actividades recientes).

Para obtener más información, consulta el método setFlags().

Ejemplo de una intent explícita

Una intent explícita es una intent que se usa para iniciar un componente específico de la aplicación, como una actividad o un servicio particular en la aplicación. Para crear una intent explícita, define el nombre de componente para el objeto Intent. Todas las demás propiedades de intent son opcionales.

Por ejemplo, si creaste un servicio en tu app denominado DownloadService, diseñado para descargar un archivo de la Web, puedes iniciarlo con el siguiente código:

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

El constructor Intent(Context, Class) proporciona a la app Context y al componente un objeto Class. Por lo tanto, el intent inicia de manera explícita la clase DownloadService en la app.

Para obtener más información sobre cómo compilar y, luego, iniciar un servicio, consulta la guía de Servicios.

Ejemplo de una intent implícita

Un intent implícito especifica una acción que puede invocar cualquier app en el dispositivo que pueda realizar la acción. El uso de una intent implícita es útil cuando la app no puede realizar la acción, pero otras apps probablemente sí, y quieres que el usuario elija qué app usar.

Por ejemplo, si tienes contenido que quieres que el usuario comparta con otras personas, crea un intent con la acción ACTION_SEND y agrega extras que especifiquen el contenido que se compartirá. Cuando llamas a startActivity() con ese intent, el usuario puede elegir una app mediante la cual compartir el contenido.

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

Cuando se llama a startActivity(), el sistema examina todas las apps instaladas para determinar cuáles pueden controlar este tipo de intent (un intent con la acción ACTION_SEND que contiene datos de texto/sin formato). Si solo hay una app que puede manejarla, esta se abre inmediatamente y se le entrega el intent. Si ninguna otra app puede controlarlo, tu app puede detectar el ActivityNotFoundException que se produce. Si varias actividades aceptan la intent, el sistema muestra un cuadro de diálogo, como el que se muestra en la figura 2, para que el usuario pueda elegir la aplicación que se debe usar.

También se proporciona más información sobre el lanzamiento de otras apps en la guía sobre cómo enviar al usuario a otra app.

Figura 2: Diálogo de selector

Cómo forzar un selector de apps

Cuando más de una app responde a tu intent implícito, el usuario puede seleccionar qué app se debe usar y establecerla como la opción predeterminada para la acción. La capacidad de seleccionar un valor predeterminado es útil cuando se realiza una acción para la cual el usuario probablemente desee utilizar la misma aplicación en lo sucesivo, por ejemplo, al abrir una página web (los usuarios suelen preferir un solo navegador web).

Sin embargo, si varias apps pueden responder al intent y es posible que el usuario quiera usar una diferente cada vez, debes mostrar explícitamente un diálogo de selección. En el diálogo del selector, se le pide al usuario que elija qué app quiere usar para la acción (no puede seleccionar una app predeterminada para la acción). Por ejemplo, cuando tu app realiza la acción de "compartir" con la acción ACTION_SEND, es posible que los usuarios deseen compartir con una app diferente según la situación en la que estén. Por ello, debes usar siempre el cuadro de diálogo de selección, como se muestra en la figura 2.

Para mostrar el selector, crea un Intent con createChooser() y transfiérelo a startActivity(), como se muestra en el siguiente ejemplo. En este ejemplo, se muestra un diálogo con una lista de apps que responden al intent pasado al método createChooser(), con el texto proporcionado como título del diálogo.

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

Detecta lanzamientos de intents no seguros

Es posible que tu app inicie intents para navegar entre componentes dentro de ella o realizar una acción en nombre de otra app. Para mejorar la seguridad de la plataforma, Android 12 (nivel de API 31) y las versiones posteriores proporcionan una función de depuración que te advierte si la app realiza un lanzamiento no seguro de un intent. Por ejemplo, tu app podría realizar un lanzamiento no seguro de un intent anidado, que es un intent que se pasa como un valor adicional en otro intent.

Si tu app realiza las siguientes dos acciones, el sistema detecta un lanzamiento de intent inseguro y se produce un incumplimiento de StrictMode:

  1. La app descomprime un intent anidado desde los valores adicionales de un intent entregado.
  2. La app inicia inmediatamente un componente de la app con ese intent anidado, como cuando se pasa el intent a startActivity(), startService() o bindService().

Si deseas obtener más información para identificar esta situación y realizar cambios en tu app, lee la entrada de blog sobre intents anidados de Android en Medium.

Verifica si hay lanzamientos de intents no seguros

Para verificar lanzamientos de intents no seguros en la app, llama a detectUnsafeIntentLaunch() cuando configures VmPolicy, como se muestra en el siguiente fragmento de código. Si la app detecta un incumplimiento de StrictMode, te recomendamos detener la ejecución de la app a fin de proteger la información potencialmente sensible.

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

Usa los intents de manera más responsable

Para minimizar las posibilidades de que se inicie un intent no seguro y se produzca una infracción de StrictMode, sigue estas prácticas recomendadas.

Copia solamente los valores adicionales y fundamentales dentro de los intents, y realiza cualquier limpieza o validación necesaria. Es posible que la app copie los valores adicionales de un intent a otro que se use para lanzar un componente nuevo. Esto sucede cuando la app llama a putExtras(Intent) o putExtras(Bundle). Si la app realiza una de estas operaciones, copia solo los valores adicionales que espera el componente receptor. Si el otro intent (que recibe la copia) lanza un componente que no se exportó, limpia y valida los valores adicionales antes de copiarlos en el intent que lanza el componente.

No exportes los componentes de tu app de forma innecesaria. Por ejemplo, si quieres iniciar un componente de app con un intent interno anidado, establece el atributo android:exported de ese componente en false.

Usa un PendingIntent en lugar de un intent anidado. De esta manera, cuando otra app separa el PendingIntent de su Intent que lo contiene, la otra app puede iniciar el PendingIntent con la identidad de tu app. Esta configuración permite que la otra app inicie de forma segura cualquier componente, incluido un componente no exportado, en tu app.

En el diagrama de la figura 2, se muestra cómo el sistema pasa el control de tu app (cliente) a otra app (servicio) y viceversa:

  1. Tu app crea un intent que invoca una actividad en otra app. Dentro de ese intent, agregas un objeto PendingIntent como valor adicional. Este intent pendiente invoca un componente de tu app que no se exporta.
  2. Cuando recibe el intent de tu app, la otra app extrae el objeto PendingIntent anidado.
  3. La otra app invoca el método send() en el objeto PendingIntent.
  4. Después de pasar el control a tu app, el sistema invoca el intent pendiente con el contexto de tu app.

Figura 2: Diagrama de la comunicación entre apps cuando se usa un intent pendiente anidado.

Cómo recibir una intent implícita

Para informar las intents implícitas que puede recibir tu app, declara uno o más filtros para cada componente de esta con un elemento <intent-filter> en el archivo de manifiesto. Cada filtro de intents especifica el tipo de intents que acepta según la acción, los datos y la categoría de la intent. El sistema entregará la intent implícita al componente de la app solo si la intent puede pasar por uno de los filtros.

Nota: Un intent explícito siempre se entrega al destino, independientemente de los filtros de intents que declare el componente.

Un componente de la app debe declarar filtros independientes para cada trabajo único que puede hacer. Por ejemplo, una actividad en una app de galería de imágenes puede tener dos filtros: uno para ver una imagen y otro para editarla. Cuando comienza la actividad, inspecciona el Intent y decide cómo comportarse según la información del Intent (como mostrar los controles del editor o no).

Cada filtro de intents está definido por un elemento <intent-filter> en el archivo de manifiesto de la app, anidado en el componente correspondiente (como un elemento <activity>).

En cada componente de la app que incluya un elemento <intent-filter>, establece de forma explícita un valor para android:exported. Este atributo indica si otras apps pueden acceder al componente de la app. En algunas situaciones, como las actividades cuyos filtros de intents incluyen la categoría LAUNCHER, es útil establecer este atributo en true. De lo contrario, es más seguro establecer este atributo en false.

Advertencia: Si una actividad, un servicio o un receptor de emisión de tu app usan filtros de intents y no establecen explícitamente el valor de android:exported, no se podrá instalar tu app en un dispositivo que ejecute Android 12 o versiones posteriores.

Dentro de <intent-filter>, puedes especificar el tipo de intents que se aceptará con uno o más de estos tres elementos:

<action>
Declara la acción de intent aceptada, en el atributo name. Debe ser el valor de string literal de una acción, no la constante de clase.
<data>
Declara el tipo de datos que se acepta, mediante el uso de uno o más atributos que especifican varios aspectos del URI de datos (scheme, host, port, path) y el tipo de MIME.
<category>
Declara la categoría de la intent aceptada, en el atributo name. El valor debe ser el de la cadena literal de una acción, no la constante de clase.

Nota: Para recibir intents implícitos, debes incluir la categoría CATEGORY_DEFAULT en el filtro de intents. Los métodos startActivity() y startActivityForResult() tratan a todos los intents como si declararan la categoría CATEGORY_DEFAULT. Si no declaras esta categoría en el filtro de intents, no se resolverá ningún intent implícito en tu actividad.

Por ejemplo, a continuación, se muestra una declaración de actividad con un filtro de intents para recibir un intent ACTION_SEND cuando el tipo de datos es un texto:

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

Puedes crear un filtro que incluya más de una instancia de <action>, <data> o <category>. Si lo haces, debes asegurarte de que el componente pueda administrar cualquier combinación de esos elementos de filtros.

Cuando quieras administrar varios tipos de intents, pero solo en combinaciones específicas de tipos de acciones, datos y categorías, deberás crear varios filtros de intents.

Una intent implícita se prueba con un filtro comparando la intent con cada uno de los tres elementos. Para que se entregue al componente, la intent debe pasar las tres pruebas. Si no coincide ni siquiera con uno de ellos, el sistema Android no entregará el intent al componente. Sin embargo, como un componente puede tener varios filtros de intents, una intent que no pase por el filtro de un componente quizá pase por otro filtro. Se proporciona más información sobre la manera en que el sistema resuelve las intents en la sección sobre Resolución de intents que aparece más abajo.

Precaución: El uso de un filtro de intents no es una forma segura de evitar que otras apps inicien tus componentes. Si bien los filtros de intents restringen un componente para que responda solo a ciertos tipos de intents implícitas, otra app puede iniciar potencialmente el componente de tu app usando una intent explícita si el desarrollador determina los nombres de los componentes. Si es importante que solo tu app pueda iniciar uno de tus componentes, no declares filtros de intents en tu manifiesto. En su lugar, establece el atributo exported en "false" para ese componente.

De manera similar, para evitar ejecutar accidentalmente el Service de otra app, siempre usa una intent explícita cuando inicies tu propio servicio.

Nota: Para todas las actividades, debes declarar tus filtros de intents en el archivo de manifiesto. Sin embargo, los filtros de receptores de emisión pueden registrarse de forma dinámica llamando a registerReceiver(). Luego, puedes anular el registro del receptor con unregisterReceiver(). De esta manera, tu app puede detectar transmisiones específicas solo durante un período determinado mientras se ejecuta.

Ejemplos de filtros

Para demostrar algunos comportamientos de filtros de intents, se incluye un ejemplo del archivo de manifiesto de una app de intercambio social:

<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 primera actividad, MainActivity, es el punto de entrada principal de la app, la actividad que se abre cuando el usuario inicia la app inicialmente con el ícono del selector:

  • La acción ACTION_MAIN indica que este es el punto de entrada principal y que no espera datos de intents.
  • La categoría CATEGORY_LAUNCHER indica que el ícono de esta actividad debe colocarse en el selector de la aplicación del sistema. Si el elemento <activity> no especifica un ícono con icon, el sistema usa el ícono del elemento <application>.

Estos dos deben sincronizarse para que la actividad aparezca en el selector de la aplicación.

La segunda actividad, ShareActivity, tiene como objetivo facilitar el uso compartido de texto y contenido multimedia. Si bien los usuarios pueden ingresar a esta actividad navegando hasta ella desde MainActivity, también pueden ingresar ShareActivity directamente desde otra app que emita un intent implícito que coincida con uno de los dos filtros de intents.

Nota: El tipo de MIME, application/vnd.google.panorama360+jpg, es un tipo de datos especial que especifica fotos panorámicas, que puedes controlar con las APIs de panorama de Google.

Haz coincidir los intents con los filtros de intents de otras apps

Si otra app se orienta a Android 13 (nivel de API 33) o versiones posteriores, puede controlar el intent de tu app solo si coincide con las acciones y categorías de un elemento <intent-filter> en esa otra app. Si el sistema no encuentra una coincidencia, arroja una ActivityNotFoundException. La app de envío debe controlar esta excepción.

Del mismo modo, si actualizas tu app para que se oriente a Android 13 o versiones posteriores, todos los intents que se originan en apps externas se entregan a un componente exportado de tu app solo si ese intent coincide con las acciones y categorías de un elemento <intent-filter> que declara tu app. Este comportamiento se produce independientemente de la versión del SDK de destino de la app de envío.

En los siguientes casos, no se aplica la coincidencia de intents:

  • Intents enviados a componentes que no declaran ningún filtro de intents.
  • Intents que se originan en la misma app.
  • Intents que se originan en el sistema es decir, los intents que se envían desde el "UID del sistema" (uid=1000). Las apps del sistema incluyen system_server y apps que configuran android:sharedUserId como android.uid.system.
  • Intents que se originan en la raíz.

Obtén más información sobre la coincidencia de intents.

Cómo usar una intent pendiente

Un objeto PendingIntent es un contenedor de un objeto Intent. El objetivo principal de un PendingIntent es otorgar permiso a una aplicación externa para usar el Intent contenido como si se ejecutara desde el proceso de tu propia app.

Estos son algunos de los casos de uso principales de una intent pendiente:

  • Declarar un intent que se ejecutará cuando el usuario realice una acción con tu notificación (el NotificationManager del sistema Android ejecuta la Intent).
  • Declarar una intent que se ejecutará cuando el usuario realice una acción con el widget de tu app (la app de la pantalla principal ejecuta la Intent).
  • Declarar un intent que se ejecutará en un momento especificado en el futuro (el AlarmManager del sistema Android ejecuta el Intent).

Así como cada objeto Intent está diseñado para ser manejado por un tipo específico de componente de la app (ya sea un Activity, un Service o un BroadcastReceiver), se debe crear un PendingIntent con la misma consideración. Cuando usas un intent pendiente, tu app no lo ejecuta con una llamada como startActivity(). En cambio, debes declarar el tipo de componente deseado cuando crees el PendingIntent llamando al método creador respectivo:

A menos que tu app esté recibiendo intents pendientes de otras apps, es probable que los métodos anteriores para crear una PendingIntent sean los únicos métodos PendingIntent que necesitarás.

Cada método toma el Context de la app actual, el Intent que deseas unir y uno o más indicadores que especifican cómo se debe usar el intent (por ejemplo, si se puede usar más de una vez).

Para obtener más información sobre el uso de intents pendientes, consulta la documentación de cada uno de los casos de uso respectivos, como en las guías de la API de Notifications y App Widgets.

Especifica la mutabilidad

Si tu app está orientada a Android 12 o versiones posteriores, debes especificar la mutabilidad de cada objeto PendingIntent que cree la app. Para declarar que un objeto PendingIntent determinado es mutable o inmutable, usa la marca PendingIntent.FLAG_MUTABLE o PendingIntent.FLAG_IMMUTABLE, respectivamente.

Si tu app intenta crear un objeto PendingIntent sin establecer una de esas marcas de mutabilidad, el sistema arroja una excepción IllegalArgumentException, y aparece el siguiente mensaje en 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.

Crea intents pendientes inmutables cuando sea posible

En la mayoría de los casos, tu app debe crear objetos PendingIntent inmutables, como se muestra en el siguiente fragmento de código. Si un objeto PendingIntent es inmutable, otras apps no podrán modificar el intent para ajustar el resultado de invocación del 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);

Sin embargo, algunos casos de uso requieren objetos PendingIntent mutables:

  • Compatibilidad con acciones de respuesta directa en notificaciones La respuesta directa requiere un cambio en los datos de clips del objeto PendingIntent asociado con la respuesta. Por lo general, este cambio se solicita cuando se pasa FILL_IN_CLIP_DATA como una marca al método fillIn().
  • Asociar las notificaciones con el framework de Android Auto mediante instancias de CarAppExtender
  • Colocar conversaciones en burbujas con instancias de PendingIntent Un objeto PendingIntent mutable permite que el sistema aplique las marcas correctas, como FLAG_ACTIVITY_MULTIPLE_TASK y FLAG_ACTIVITY_NEW_DOCUMENT.
  • Solicitar información de ubicación del dispositivo llamando a requestLocationUpdates() o APIs similares El objeto PendingIntent mutable permite que el sistema agregue extras de intent que representan eventos del ciclo de vida de la ubicación. Estos eventos incluyen un cambio de ubicación y la disponibilidad de un proveedor.
  • Programar alarmas con AlarmManager El objeto mutable PendingIntent permite que el sistema agregue el intent adicional EXTRA_ALARM_COUNT. Este valor adicional representa la cantidad de veces que se activó una alarma repetida. Al contener este extra, el intent puede notificar con precisión a una app si una alarma repetitiva se activó varias veces, como cuando el dispositivo estaba suspendido.

Si tu app crea un objeto PendingIntent mutable, te recomendamos que uses un intent explícito y completes el elemento ComponentName. De esa manera, cada vez que otra app invoque el PendingIntent y pase el control a tu app, siempre se iniciará el mismo componente.

Usa intents explícitos dentro de intents pendientes

Para definir mejor cómo otras apps pueden usar los intents pendientes de tu app, siempre une un intent pendiente a un intent explícito. Para seguir esta práctica recomendada, haz lo siguiente:

  1. Comprueba que estén configurados los campos de acción, paquete y componente del intent base.
  2. Usa FLAG_IMMUTABLE, que se agregó en Android 6.0 (API nivel 23), para crear intents pendientes. Esta marca evita que las apps que reciben un PendingIntent completen propiedades que no se propagaron. Si el minSdkVersion de tu app es 22 o anterior, puedes proporcionar seguridad y compatibilidad juntos usando el siguiente código:

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

Resolución de intents

Cuando el sistema recibe un intent implícito para iniciar una actividad, busca la mejor actividad para el intent comparándolo con los filtros de intents basándose en tres aspectos:

  • Acción.
  • Datos (tanto URI como tipo de datos)
  • Categoría

En las siguientes secciones, se describe cómo se establece la coincidencia de los intents con los componentes adecuados según la declaración del filtro de intents en el archivo de manifiesto de una app.

Prueba de acción

Para especificar las acciones de intents aceptadas, un filtro de intents puede declarar un número de elementos <action> de cero en adelante, tal como se muestra en el siguiente ejemplo:

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

Para pasar el filtro, la acción especificada en el Intent debe coincidir con una de las acciones indicadas en el filtro.

Si el filtro no enumera ninguna acción, no hay nada con qué establecer la coincidencia, por lo que ninguna de las intents pasa la prueba. Sin embargo, si una Intent no especifica una acción, pasará la prueba (siempre y cuando el filtro contenga al menos una acción).

Prueba de categoría

Para especificar las categorías de intents aceptadas, un filtro de intents puede declarar un número de elementos <category> de cero en adelante, tal como se muestra en el siguiente ejemplo:

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

Para que una intent pase una prueba de categoría, cada categoría en la Intent debe coincidir con la categoría que figura en el filtro. No es necesario que suceda la situación inversa (el filtro de intents puede declarar más categorías que las que se especifican en el Intent y el Intent aún pasa). Por lo tanto, un intent sin categorías siempre pasa esta prueba, independientemente de qué categorías estén declaradas en el filtro.

Nota: Android aplica automáticamente la categoría CATEGORY_DEFAULT a todos los intents implícitos que se pasan a startActivity() y startActivityForResult(). Si deseas que tu actividad reciba intents implícitos, debes incluir una categoría para "android.intent.category.DEFAULT" en sus filtros de intents, como se muestra en el ejemplo anterior de <intent-filter>.

Prueba de datos

Para especificar los datos de intents que se aceptan, un filtro de intents puede declarar un número de elementos <data> de cero en adelante, tal como se muestra en el siguiente ejemplo:

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

Cada elemento <data> puede especificar una estructura de URI y un tipo de datos (tipo de medio de MIME). Cada parte del URI es un atributo independiente: scheme, host, port y path:

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

El siguiente ejemplo muestra valores posibles para estos atributos:

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

En este URI, el esquema es content, el host es com.example.project, el puerto es 200 y la ruta de acceso es folder/subfolder/etc.

Cada uno de estos atributos es opcional en un elemento <data>, pero existen dependencias lineales:

  • Si no se especifica el esquema, se ignora el host.
  • Si el host no se especifica, se ignora el puerto.
  • Si no se especifican el esquema ni el host, se ignora la ruta de acceso.

Cuando se compara el URI de una intent con una especificación de URI en un filtro, solo se compara con las partes del URI que se incluyen en el filtro. Por ejemplo:

  • Si un filtro especifica solo un esquema, todos los URI con ese esquema coinciden con el filtro.
  • Si un filtro especifica un esquema y una autoridad, pero no una ruta de acceso, todos los URI con el mismo esquema y autoridad pasan el filtro, independientemente de la ruta que tengan.
  • Si un filtro especifica un esquema, una autoridad y una ruta de acceso, solo los URI con el mismo esquema, autoridad y ruta pasan el filtro.

Nota: La especificación de una ruta de acceso puede contener un asterisco (*) comodín para requerir solamente una coincidencia parcial del nombre de la ruta.

La prueba de datos compara tanto el URI como el tipo de MIME en la intent con el URI y el tipo de MIME especificados en el filtro. Las reglas son las siguientes:

  1. Una intent que no contiene ni un URI ni un tipo de MIME pasa la prueba solo si el filtro no especifica ningún URI ni tipo de MIME.
  2. Una intent que contiene un URI, pero no un tipo de MIME (ni explícitamente ni que se pueda inferir del URI), pasa la prueba solo si el URI coincide con el formato de URI del filtro y si el filtro tampoco especifica un tipo de MIME.
  3. Una intent que contiene un tipo de MIME, pero no un URI, pasa la prueba solo si el filtro enumera el mismo tipo de MIME y no especifica un formato de URI.
  4. Un intent que contiene un URI y un tipo de MIME (explícito o que se pueda inferir del URI) pasa la parte de tipo de MIME de la prueba solo si ese tipo coincide con uno que se indica en el filtro. Pasa la parte del URI de la prueba si este coincide con un URI del filtro o si tiene un URI content: o file:, y el filtro no especifica uno. Es decir, se presume que un componente admite datos content: y file: si su filtro enumera solo un tipo de MIME.

Nota: Si un intent especifica un tipo de URI o MIME, la prueba de datos fallará si no hay elementos <data> en <intent-filter>.

Esta última regla, la regla (d), refleja la expectativa de que los componentes pueden obtener datos locales de un archivo o proveedor de contenido. Por lo tanto, sus filtros pueden enumerar solo un tipo de datos y no necesitan nombrar explícitamente los esquemas content: y file:. En el siguiente ejemplo, se muestra un caso típico en el que un elemento <data> le indica a Android que el componente puede obtener datos de imagen de un proveedor de contenido y mostrarlos:

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

Los filtros que especifican un tipo de datos, pero no un URI, son quizás los más comunes, ya que la mayoría de los datos disponibles son proporcionados por proveedores de contenido.

Otra configuración común es aquella en la que los filtros tienen un esquema y un tipo de datos. Por ejemplo, un elemento <data> como el siguiente le indica a Android que el componente puede recuperar datos de video de la red para realizar la acción:

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

Coincidencias de intents

Se establecen coincidencias de las intents con filtros de intents no solo para detectar un componente de destino que se debe activar, sino también para detectar algo sobre el conjunto de componentes del dispositivo. Por ejemplo, la app de Home completa el selector de la aplicación encontrando todas las actividades con filtros de intents que especifiquen la acción ACTION_MAIN y la categoría CATEGORY_LAUNCHER. Una coincidencia solo es exitosa si las acciones y categorías del intent coinciden con el filtro, tal como se describe en la documentación de la clase IntentFilter.

Tu aplicación puede usar la coincidencia de intents de forma similar a como lo hace la app de pantalla principal. PackageManager tiene un conjunto de métodos query...() que muestran todos los componentes que pueden aceptar una intent específica, al igual que una serie similar de métodos resolve...() que determinan el mejor componente para responder a una intent. Por ejemplo, queryIntentActivities() muestra una lista de todas las actividades que pueden realizar el intent pasado como argumento, y queryIntentServices() muestra una lista similar de servicios. Ninguno de los métodos activa los componentes; solo enumeran cuáles pueden responder. Existe un método similar, queryBroadcastReceivers(), para receptores de emisión.