Un Intent es un objeto de mensajería que puedes usar para solicitar una acción de otro componente de la app.
Si bien los intents facilitan la comunicación entre componentes de varias maneras, existen tres casos de uso fundamentales:
- Cómo iniciar una actividad
Un objeto
Activityrepresenta una sola pantalla en una app. Puedes iniciar una nueva instancia de un objetoActivitypasando un objetoIntentastartActivity(). ElIntentdescribe la actividad que se iniciará y lleva los datos necesarios.Si quieres recibir un resultado de la actividad cuando finalice, llama a
startActivityForResult(). Tu actividad recibe el resultado como un objetoIntentindependiente en la devolución de llamadaonActivityResult()de tu actividad. Para obtener más información, consulta la guía de actividades. - Cómo iniciar un servicio
Un
Servicees un componente que realiza operaciones en segundo plano sin una interfaz de usuario. Con Android 5.0 (nivel de API 21) y versiones posteriores, puedes iniciar un servicio conJobScheduler. Para obtener más información sobreJobScheduler, consulta suAPI-reference documentation.En las versiones anteriores a Android 5.0 (nivel de API 21), puedes iniciar un servicio con los métodos de la clase
Service. Puedes iniciar un servicio para realizar una operación única (como descargar un archivo) pasando unIntentastartService(). ElIntentdescribe el servicio que se iniciará y lleva los datos necesarios.Si el servicio está diseñado con una interfaz cliente-servidor, puedes vincularlo a otro componente pasando un
IntentabindService(). Para obtener más información, consulta la guía de Servicios. - Cómo enviar un anuncio
Una transmisión es un mensaje que cualquier app puede recibir. El sistema entrega varias transmisiones para eventos del sistema, como cuando se inicia el sistema o el dispositivo comienza a cargarse. Puedes enviar una transmisión a otras apps pasando un
IntentasendBroadcast()osendOrderedBroadcast().
En el resto de esta página, se explica cómo funcionan los intents y cómo usarlos. 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, para lo que se especifica un
ComponentNamecompleto. Por lo general, usarás un intent explícito para iniciar un componente en tu propia app, ya que conoces el nombre de clase de la actividad o el servicio que quieres iniciar. Por ejemplo, puedes iniciar una actividad nueva dentro de 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 que se debe realizar, lo que permite que un componente de otra app la controle. Por ejemplo, si quieres mostrarle al usuario una ubicación en un mapa, puedes usar un intent implícito para solicitar que otra app compatible muestre una ubicación especificada en un mapa.
En la figura 1, se muestra cómo se usa un intent cuando se inicia 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 la acción y lo 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 adecuado para iniciarlo comparando el contenido del intent con los filtros de intents declarados en el archivo de manifiesto de otras apps del dispositivo. Si el intent coincide con un filtro de intents, el sistema inicia ese componente y le entrega el objeto Intent. Si hay varios filtros de intents compatibles, el sistema muestra un diálogo para que el usuario pueda elegir qué app 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 desea recibir. Por ejemplo, si declaras un filtro de intents para una actividad, posibilitas que otras apps inicien directamente tu actividad con un tipo de intent determinado. Del mismo modo, si no declaras ningún filtro de intents para una actividad, esta solo se podrá iniciar con un intent explícito.
Precaución: Para garantizar la seguridad de tu app, usa siempre 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 lanza una excepción si llamas a bindService() con un intent implícito.
Cómo compilar un intent
Un objeto Intent contiene información que el sistema Android usa para determinar qué componente iniciar (como el nombre exacto o la categoría del componente que debe recibir el intent), además de información que el componente receptor usa para realizar la acción de forma correcta (como la acción que se debe realizar y los datos sobre 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 iniciará.
Este es opcional, pero es la información fundamental que hace que un intent sea explícito, lo que significa que el intent solo se debe entregar al componente de la app definido por el nombre del componente. Sin un nombre de componente, el intent es implícito y el sistema decide qué componente debe recibirlo según la otra información 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, especifica siempre 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
Intentes un objetoComponentName, 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 consetComponent(),setClass(),setClassName()o con el constructorIntent. - Acción
- Es una cadena que especifica la acción genérica que se debe realizar (como view o pick).
En el caso de un intent de transmisión, esta es la acción que se llevó a cabo y que se informa. La acción determina en gran medida cómo se estructura el resto del intent, en especial, la información que se incluye en los datos y los extras.
Puedes especificar tus propias acciones para que las usen los intents dentro de tu app (o para que las usen otras apps para invocar componentes en tu app), pero, por lo general, especificas constantes de acción definidas por la clase
Intento por otras clases de 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 el intent share. Debes usarlo en un intent con
startActivity()cuando tengas algunos datos que el usuario pueda compartir a través de otra app, como una app de correo electrónico o de redes sociales.
Consulta la referencia de la clase
Intentpara obtener más constantes que definen acciones genéricas. Otras acciones se definen en otras partes del framework de Android, como enSettingspara acciones que abren pantallas específicas en la app de Configuración del sistema.Puedes especificar la acción para un intent con
setAction()o con un constructorIntent.Si defines tus propias acciones, asegúrate de incluir el nombre del paquete de tu app como prefijo, 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
- Es el URI (un objeto
Uri) que hace referencia a los datos sobre los que se debe actuar o el tipo de MIME de esos datos. Por lo general, el tipo de datos proporcionados depende de la acción del intent. Por ejemplo, si la acción esACTION_EDIT, los datos deben contener el URI del documento que se editará.Cuando creas un intent, suele ser 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 pueda 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, a veces, el tipo de MIME se puede inferir del URI, en especial cuando los datos son un URI
content:. Un URIcontent:indica que los datos se encuentran en el dispositivo y los controla unContentProvider, lo que hace que el tipo de MIME de los datos sea visible para el sistema.Para establecer solo el URI de datos, llama a
setData(). Para establecer solo el tipo de MIME, llama asetType(). Si es necesario, puedes establecer ambos de forma explícita consetDataAndType().Precaución: Si deseas establecer el URI y el tipo de MIME, no llames a
setData()ni asetType(), ya que cada uno anula el valor del otro. Usa siempresetDataAndType()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. Se puede colocar cualquier cantidad de descripciones de categorías en un intent, 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 un navegador web la inicie para mostrar los datos a los que se hace referencia en 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 aparece en el selector de aplicaciones del sistema.
Consulta la descripción de la clase
Intentpara obtener la lista completa de categorías.Puedes especificar una categoría con
addCategory().
Estas propiedades que se mencionan más arriba (nombre del componente, acción, datos y categoría) representan las características que definen un intent. Cuando lee estas propiedades, el sistema Android puede resolver qué componente de la app debe iniciar. Sin embargo, un intent puede contener información adicional que no afecta la forma en que se resuelve en un componente de la app. Un intent también puede proporcionar la siguiente información:
- Adicional
- Pares clave-valor que contienen información adicional necesaria para completar
la acción solicitada.
Así como algunas acciones usan tipos particulares de URI de datos, algunas acciones también usan extras particulares.
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 objetoBundlecon todos los datos adicionales y, luego, insertar elBundleen elIntentconputExtras().Por ejemplo, cuando creas un intent para enviar un correo electrónico con
ACTION_SEND, puedes especificar el destinatario to con la claveEXTRA_EMAILy el subject con la claveEXTRA_SUBJECT.La clase
Intentespecifica muchas constantesEXTRA_*para los tipos de datos estandarizados. Si necesitas declarar tus propias claves adicionales (para los intents que recibe tu app), asegúrate de incluir el nombre del paquete de tu app como prefijo, 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
ParcelableniSerializablecuando envíes un intent que esperas que reciba otra app. Si una app intenta acceder a los datos de un objetoBundle, pero no tiene acceso a la clase empaquetada o serializada, el sistema genera unaRuntimeException. - Marcas
- Los flags se definen en la clase
Intentque funciona como metadatos para el intent. Las marcas pueden indicarle al sistema Android cómo iniciar una actividad (por ejemplo, a qué tarea debe pertenecer la actividad) 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 intent explícito
Un intent explícito es aquel que usas para iniciar un componente de app específico, como
una actividad o un servicio en particular en tu app. Para crear un intent explícito, define
el nombre del componente para el objeto Intent; todas
las demás propiedades del intent son opcionales.
Por ejemplo, si creaste un servicio en tu app, llamado 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 theContext// The fileUrl is a string URL, such as "http://www.example.com/image.png" val downloadIntent = Intent(this, DownloadService::class.java).apply { data =Uri.parse(fileUrl) } startService(downloadIntent)
Java
// Executed in an Activity, so 'this' is theContext// The fileUrl is a string URL, such as "http://www.example.com/image.png" Intent downloadIntent = new Intent(this, DownloadService.class); downloadIntent.setData(Uri.parse(fileUrl)); startService(downloadIntent);
El constructor Intent(Context, Class)
proporciona a la app Context y al
componente un objeto Class. Por lo tanto, este intent inicia de forma 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 intent implícito
Un intent implícito especifica una acción que puede invocar cualquier app del dispositivo que pueda realizar la acción. Usar un intent implícito es útil cuando tu app no puede realizar la acción, pero es probable que otras apps sí puedan y quieras 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 para 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 y que contiene datos "text/plain"). Si solo hay una app que puede controlarlo, se abrirá de inmediato y recibirá el intent. Si ninguna otra app puede controlarlo, tu app puede detectar la ActivityNotFoundException que se produce. Si varias actividades aceptan el intent, el sistema muestra un diálogo como el que se muestra en la Figura 2, de modo que el usuario pueda elegir qué app usar.
También se proporciona más información para iniciar 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 hay más de una app que responde a tu intent implícito, el usuario puede seleccionar qué app usar y convertirla en la opción predeterminada para la acción. La capacidad de seleccionar una opción predeterminada es útil cuando se realiza una acción para la que el usuario probablemente quiera usar la misma app cada vez, como cuando se abre una página web (los usuarios suelen preferir un solo navegador web).
Sin embargo, si varias apps pueden responder al intent y el usuario desea usar una app diferente cada vez, debes mostrar explícitamente un diálogo de selección. En el diálogo de selección, se le pide al usuario que elija la app que usará para la acción (el usuario no puede seleccionar una app predeterminada para la acción). Por ejemplo, cuando tu app realiza la acción "compartir" con ACTION_SEND, es posible que los usuarios quieran compartir contenido con otra app según su situación actual, por lo que siempre debes usar el diálogo del selector, como se muestra en la figura 2.
Para mostrar el selector, crea un Intent con createChooser() y pásalo 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 que se pasó al método createChooser() y se usa 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 para realizar una acción en nombre de otra app. Con el objetivo de mejorar la seguridad de la plataforma, Android 12 (nivel de API 31) y versiones posteriores proporcionan una función de depuración que te advierte si tu app realiza un inicio no seguro de un intent. Por ejemplo, tu app podría realizar un inicio no seguro de un intent anidado, que es un intent que se pasa como un extra en otro intent.
Si la app realiza las siguientes dos acciones, el sistema detecta un lanzamiento de intent inseguro y se produce una infracción de StrictMode:
- La app descomprime un intent anidado desde los valores adicionales de un intent entregado.
- La app inicia inmediatamente un componente de la app con ese intent anidado, como cuando se pasa el intent a
startActivity(),startService()obindService().
Para obtener más detalles sobre cómo identificar esta situación y realizar cambios en tu app, lee la entrada de blog sobre intents de anidamiento 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
tu app detecta un incumplimiento de StrictMode, te recomendamos que detengas la ejecución de la app para
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 la posibilidad de que se inicie un intent no seguro y se produzca un incumplimiento 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 la app con un intent anidado interno, establece el atributo android:exported de ese componente en false.
Usa un PendingIntent en lugar de un intent anidado. De esa manera, cuando otra app desempaqueta el PendingIntent de su Intent contenedor, 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 uno 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 de vuelta a tu app:
- Tu app crea un intent que invoca una actividad en otra app. Dentro de ese intent, agregas un objeto
PendingIntentcomo un extra. Este intent pendiente invoca un componente de tu app que no se exporta. - Cuando recibe el intent de tu app, la otra app extrae el objeto
PendingIntentanidado. - La otra app invoca el método
send()en el objetoPendingIntent. - Después de devolver 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 un intent implícito
Para anunciar qué intents implícitos puede recibir tu app, declara uno o más filtros de intents para cada uno de los componentes de tu app con un elemento <intent-filter> en tu 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 del intent. El sistema entrega un intent implícito al componente de tu app solo si el intent puede pasar por uno de tus filtros de intents.
Nota: Un intent explícito siempre se entrega a su 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 pueda realizar.
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 se inicia la actividad,
inspecciona el Intent y decide cómo comportarse según la información
en el Intent (por ejemplo, si se muestran o no los controles del editor).
Cada filtro de intents se define con un elemento <intent-filter> en el archivo de manifiesto de la app, anidado en el componente correspondiente de la app (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 transmisiones de tu app usa filtros de intents y no establece de forma explícita el valor de android:exported, no se podrá instalar la app en un dispositivo que ejecute Android 12 o una versión posterior.
Dentro de <intent-filter>,
puedes especificar el tipo de intents que se aceptarán con uno o más
de estos tres elementos:
<action>- Declara la acción de intent aceptada en el atributo
name. El valor debe ser el valor de cadena literal de una acción, no la constante de clase. <data>- Declara el tipo de datos aceptados con 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 intent aceptada en el atributo
name. El valor debe ser el valor de cadena literal de una acción, no la constante de clase.Nota: Para recibir intents implícitos, debes incluir la categoría
CATEGORY_DEFAULTen el filtro de intents. Los métodosstartActivity()ystartActivityForResult()tratan a todos los intents como si declararan la categoríaCATEGORY_DEFAULT. Si no declaras esta categoría en el filtro de intents, no se resolverá ningún intent implícito en la actividad.
Por ejemplo, esta es una declaración de actividad con un filtro de intents para recibir un intent ACTION_SEND cuando el tipo de datos es 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 controlar todas las combinaciones de esos elementos de filtro.
Cuando quieras controlar varios tipos de intents, pero solo en combinaciones específicas de acción, datos y tipo de categoría, deberás crear varios filtros de intents.
Un intent implícito se prueba con un filtro comparando el intent con cada uno de los tres elementos. Para que se entregue al componente, el intent debe pasar las tres pruebas. Si no coincide con ninguno de ellos, el sistema Android no entregará el intent al componente. Sin embargo, debido a que un componente puede tener varios filtros de intents, un intent que no pasa por uno de los filtros de un componente podría pasar por otro. En la sección que aparece a continuación, sobre la resolución de intents, se proporciona más información sobre cómo el sistema resuelve los intents.
Precaución: Usar 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ícitos, otra app puede iniciar el componente de tu app con un intent explícito si el desarrollador determina los nombres de tus 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.
Del mismo modo, para evitar ejecutar de forma involuntaria el Service de otra app, usa siempre un intent explícito para iniciar tu propio servicio.
Nota: Para todas las actividades, debes declarar tus filtros de intents en el archivo de manifiesto.
Sin embargo, los filtros para los receptores de transmisiones se pueden registrar de forma dinámica llamando a registerReceiver(). Luego, puedes cancelar el registro del receptor con unregisterReceiver(). Esto permite que tu app escuche transmisiones específicas solo durante un período determinado mientras se ejecuta.
Filtros de ejemplo
Para demostrar algunos de los comportamientos de los filtros de intents, aquí tienes un ejemplo del archivo de manifiesto de una app para compartir contenido en redes sociales:
<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, es decir, la actividad que se abre cuando el usuario inicia la app por primera vez con el ícono de selector:
- La acción
ACTION_MAINindica que este es el punto de entrada principal y no espera ningún dato de intent. - La categoría
CATEGORY_LAUNCHERindica que el ícono de esta actividad se debe colocar en el selector de aplicaciones del sistema. Si el elemento<activity>no especifica un ícono conicon, el sistema usa el ícono del elemento<application>.
Ambos deben vincularse para que la actividad aparezca en el selector de aplicaciones.
La segunda actividad, ShareActivity, tiene como objetivo facilitar el uso compartido de contenido de texto y contenido multimedia. Si bien los usuarios pueden ingresar a esta actividad navegando a ella desde MainActivity, también pueden ingresar a 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 Google panorama.
Cómo hacer 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 este 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 emisora 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 que envía el intent.
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_servery apps que configuranandroid:sharedUserIdcomoandroid.uid.system. - Intents que se originan en la raíz.
Obtén más información sobre la coincidencia de intents.
Cómo usar un intent pendiente
Un objeto PendingIntent es un wrapper alrededor de un objeto Intent. El propósito principal de un PendingIntent es otorgar permiso a una aplicación externa para que use el Intent contenido como si se ejecutara desde el propio proceso de tu app.
Entre los casos de uso principales de un intent pendiente, se incluyen los siguientes:
- Declarar un intent que se ejecutará cuando el usuario realice una acción con tu Notification (el
NotificationManagerdel sistema Android ejecuta elIntent) - Declarar un intent que se ejecutará cuando el usuario realice una acción con tu widget de la app (la app de la pantalla principal ejecuta el
Intent) - Declarar un intent que se ejecutará en un momento futuro especificado (el
AlarmManagerdel sistema Android ejecuta elIntent)
Así como cada objeto Intent está diseñado para que lo controle un tipo específico de componente de la app (un Activity, un Service o un BroadcastReceiver), también se debe crear un PendingIntent con la misma consideración. Cuando se usa un intent pendiente, la app no
ejecuta el intent con una llamada como startActivity(). En su lugar, debes declarar el tipo de componente previsto cuando crees el PendingIntent llamando al método de creador correspondiente:
PendingIntent.getActivity()para unIntentque inicia unActivity.PendingIntent.getService()para unIntentque inicia unService.PendingIntent.getBroadcast()para unIntentque inicia unBroadcastReceiver.
A menos que tu app reciba intents pendientes de otras apps, los métodos anteriores para crear un PendingIntent probablemente sean los únicos métodos PendingIntent que necesites.
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 Notificaciones y Widgets de apps.
Especifica la mutabilidad
Si tu app se orienta a Android 12 o versiones posteriores, debes especificar la mutabilidad de cada objeto PendingIntent que cree. 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 configurar ninguna de las marcas de mutabilidad, el sistema arroja una 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 pueden modificar el intent para ajustar el resultado de invocarlo.
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, en ciertos casos de uso, se requieren objetos PendingIntent mutables:
- Cómo brindar compatibilidad con las acciones de respuesta directa en las notificaciones La respuesta directa requiere un cambio en los datos del clip en el objeto PendingIntent asociado con la respuesta. Por lo general, para solicitar este cambio, debes pasar
FILL_IN_CLIP_DATAcomo una marca al métodofillIn(). - Asociar notificaciones con el framework de Android Auto, con instancias de
CarAppExtender - Colocar conversaciones en burbujas con instancias de
PendingIntentUn objetoPendingIntentmutable permite que el sistema aplique las marcas correctas, comoFLAG_ACTIVITY_MULTIPLE_TASKyFLAG_ACTIVITY_NEW_DOCUMENT. - Solicitar información de ubicación del dispositivo llamando a
requestLocationUpdates()o APIs similares El objetoPendingIntentmutable permite que el sistema agregue extras de intents que representen 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
AlarmManagerEl objetoPendingIntentmutable permite que el sistema agregue el intent adicionalEXTRA_ALARM_COUNT. Este extra representa la cantidad de veces que se activó una alarma repetitiva. Con este extra, el intent puede notificar con precisión a una app si se activó una alarma repetitiva varias veces, como cuando el dispositivo estaba inactivo.
Si tu app crea un objeto PendingIntent mutable, te recomendamos que uses un intent explícito y completes el 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 con un intent explícito. Para seguir esta práctica recomendada, haz lo siguiente:
- Verifica que estén configurados los campos de acción, paquete y componente del intent de base.
-
Usa
FLAG_IMMUTABLE, que se agregó en Android 6.0 (nivel de API 23), para crear intents pendientes. Esta marca evita que las apps que reciben unPendingIntentcompleten propiedades sin completar. Si elminSdkVersionde tu app es22o un valor inferior, puedes proporcionar seguridad y compatibilidad juntas con 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ándola con los filtros de intents en función de tres aspectos:
- Acción.
- Datos (tanto el URI como el tipo de datos)
- Categoría.
En las siguientes secciones, se describe cómo se hacen coincidir 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 intent aceptadas, un filtro de intents puede declarar cero o más elementos <action>, 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 este filtro, la acción especificada en el Intent debe coincidir con una de las acciones que se indican en el filtro.
Si el filtro no enumera ninguna acción, no hay nada con lo que un intent pueda coincidir, por lo que todos los intents fallan la prueba. Sin embargo, si un Intent
no especifica una acción, pasa la prueba siempre que 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 cero o más elementos <category>, 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 un intent pase la prueba de categoría, cada categoría del Intent debe coincidir con una categoría del filtro. No es necesario lo contrario: el filtro de intents puede declarar más categorías que las especificadas en el Intent, y el Intent aún se aprueba. Por lo tanto, un intent sin categorías siempre pasa esta prueba, independientemente de las categorías que se declaren 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 quieres que tu actividad reciba intents implícitos, debe incluir una categoría para "android.intent.category.DEFAULT" en sus filtros de intents, como se muestra en el ejemplo de <intent-filter> anterior.
Prueba de datos
Para especificar los datos de intents aceptados, un filtro de intents puede declarar cero o más elementos <data>, 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 MIME).
Cada parte del URI es un atributo independiente: scheme, host, port y path:
<scheme>://<host>:<port>/<path>
En el siguiente ejemplo, se muestran los 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 hay dependencias lineales:
- Si no se especifica un esquema, se ignora el host.
- Si no se especifica un host, se ignora el puerto.
- Si no se especifican el esquema ni el host, se ignora la ruta de acceso.
Cuando el URI de un intent se compara con una especificación de URI en un filtro, solo se compara con las partes del URI incluidas en el filtro. Por ejemplo:
- Si un filtro especifica solo un esquema, todos los URIs con ese esquema coinciden con el filtro.
- Si un filtro especifica un esquema y una autoridad, pero no una ruta de acceso, todos los URIs con el mismo esquema y la misma autoridad pasan el filtro, independientemente de sus rutas de acceso.
- Si un filtro especifica un esquema, una autoridad y una ruta de acceso, solo los URIs con el mismo esquema, la misma autoridad y la misma ruta de acceso pasan el filtro.
Nota: Una especificación de ruta de acceso puede contener un asterisco comodín (*) para requerir solo una coincidencia parcial del nombre de la ruta de acceso.
La prueba de datos compara el URI y el tipo de MIME del intent con un URI y un tipo de MIME especificados en el filtro. Las reglas son las siguientes:
- Un intent que no contiene un URI ni un tipo de MIME pasa la prueba solo si el filtro no especifica ningún URI ni tipo de MIME.
- Un intent que contiene un URI, pero no un tipo de MIME (ni explícito ni inferible a partir del URI), pasa la prueba solo si su URI coincide con el formato de URI del filtro y, de la misma manera, el filtro no especifica un tipo de MIME.
- Un intent que contiene un tipo de MIME, pero no un URI, pasa la prueba solo si el filtro incluye el mismo tipo de MIME y no especifica un formato de URI.
- Un intent que contiene un URI y un tipo de MIME (ya sea explícito o inferible del URI) pasa la parte del tipo de MIME de la prueba solo si ese tipo coincide con un tipo que aparece en el filtro. Pasa la parte del URI de la prueba
si su URI coincide con un URI del filtro o si tiene un URI
content:ofile:, y el filtro no especifica un URI. En otras palabras, se supone que un componente admite datoscontent:yfile:si su filtro enumera solo un tipo de MIME.
Nota: Si un intent especifica un URI o un tipo de MIME, la prueba de datos fallará si no hay elementos <data> en el <intent-filter>.
Esta última regla, la regla (d), refleja la expectativa de que los componentes puedan 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 de forma explícita 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 los proporcionan los proveedores de contenido.
Otra configuración común es un filtro con 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>
Coincidencia de intents
Los intents se comparan con los filtros de intents no solo para descubrir un componente de destino que se deba activar, sino también para descubrir algo sobre el conjunto de componentes del dispositivo. Por ejemplo, la app de Home propaga el selector de aplicaciones buscando todas las actividades con filtros de intents que especifican la acción ACTION_MAIN y la categoría CATEGORY_LAUNCHER.
Una coincidencia solo se produce si las acciones y las categorías del Intent coinciden con el filtro, como se describe en la documentación de la clase IntentFilter.
Tu aplicación puede usar la coincidencia de intents de manera similar a como lo hace la app de Home.
El PackageManager tiene un conjunto de métodos query...()
que devuelven todos los componentes que pueden aceptar un intent en particular y
una serie similar de métodos resolve...() que determinan el mejor
componente para responder a un intent. Por ejemplo, queryIntentActivities() muestra una lista de todas las actividades que pueden realizar el intent que se pasa como argumento, y queryIntentServices() muestra una lista similar de servicios.
Ninguno de los métodos activa los componentes, solo enumeran los que pueden responder. Existe un método similar, queryBroadcastReceivers(), para los receptores de transmisiones.