Intents e filtros

Um Intent é um objeto de mensagens que pode ser usado para solicitar uma ação de outro componente do app. As intents facilitam a comunicação entre componentes de várias maneiras, mas existem três casos de uso fundamentais:

  • Como iniciar uma atividade

    Um Activity representa uma única tela em um app. Você pode iniciar uma nova instância de um Activity transmitindo um Intent para startActivity(). O Intent descreve a atividade a ser iniciada e carrega todos os dados necessários.

    Se você quiser receber um resultado da atividade quando ela for concluída, chame startActivityForResult(). A atividade recebe o resultado como um objeto Intent separado no callback onActivityResult(). Para mais informações, consulte o guia Atividades.

  • Como iniciar um serviço

    Um Service é um componente que executa operações em segundo plano sem uma interface do usuário. Com o Android 5.0 (API de nível 21) e versões mais recentes, é possível iniciar um serviço com JobScheduler. Para saber mais sobre JobScheduler, consulte a API-reference documentation.

    Para versões anteriores ao Android 5.0 (nível 21 da API), é possível iniciar um serviço usando métodos da classe Service. É possível iniciar um serviço para executar uma operação única (como fazer o download de um arquivo) transmitindo um Intent para startService(). O Intent descreve o serviço a ser iniciado e carrega todos os dados necessários.

    Se o serviço for projetado com uma interface cliente-servidor, será possível vincular o serviço de outro componente transmitindo um Intent para bindService(). Para mais informações, consulte o guia Serviços.

  • Transmitir uma transmissão

    Transmissão é uma mensagem que qualquer aplicativo pode receber. O sistema oferece várias transmissões para eventos dele, como quando ele é inicializado ou o dispositivo começa a carregar. Você pode enviar uma transmissão para outros apps transmitindo um Intent para sendBroadcast() ou sendOrderedBroadcast().

O restante desta página explica como os intents funcionam e como usá-los. Para informações relacionadas, consulte Como interagir com outros apps e Como compartilhar conteúdo.

Tipos de intents

Há dois tipos de intents:

  • As intents explícitas especificam um componente de qual aplicativo vai satisfazer a intent, especificando uma ComponentName completa. Normalmente, uma intent explícita é usada para iniciar um componente no seu próprio app, porque você sabe o nome de classe da atividade ou do serviço que quer iniciar. Por exemplo, você pode iniciar uma nova atividade no app em resposta a uma ação do usuário ou iniciar um serviço para fazer o download de um arquivo em segundo plano.
  • As intents implícitas não nomeiam um componente específico, mas declaram uma ação geral a ser executada, o que permite que um componente de outro app a processe. Por exemplo, se você quiser mostrar ao usuário uma localização no mapa, poderá usar uma intent implícita para solicitar que outro app compatível mostre um local especificado no mapa.

A figura 1 mostra como um intent é usado ao iniciar uma atividade. Quando o objeto Intent nomeia explicitamente um componente da atividade específico, o sistema inicia esse componente imediatamente.

Figura 1. Como uma intent implícita é enviada pelo sistema para iniciar outra atividade: [1] a atividade A cria uma Intent com uma descrição de ação e a transmite para startActivity(). [2] O sistema Android pesquisa todos os apps em busca de um filtro de intent que corresponda à intent. Quando uma correspondência é encontrada, [3] o sistema inicia a atividade correspondente (Atividade B) invocando o método onCreate() e transmitindo a Intent.

Quando você usa uma intent implícita, o sistema Android encontra o componente adequado para iniciar comparando o conteúdo da intent com os filtros de intent declarados no arquivo de manifesto de outros apps no dispositivo. Se a intent corresponder a um filtro de intent, o sistema iniciará esse componente e entregará o objeto Intent. Se vários filtros de intent forem compatíveis, o sistema exibirá uma caixa de diálogo para que o usuário possa escolher qual app usar.

Um filtro de intent é uma expressão no arquivo de manifesto de um app que especifica o tipo de intents que o componente quer receber. Por exemplo, ao declarar um filtro de intent para uma atividade, você permite que outros apps iniciem sua atividade diretamente com um determinado tipo de intent. Da mesma forma, se você não declarar nenhum filtro de intent para uma atividade, ela só poderá ser iniciada com uma intent explícita.

Cuidado:para garantir a segurança do app, sempre use uma intent explícita ao iniciar um Service e não declare filtros de intent para seus serviços. O uso de uma intent implícita para iniciar um serviço representa um risco de segurança, porque não é possível determinar qual serviço responderá à intent, e o usuário não poderá ver qual serviço será iniciado. No Android 5.0 (nível 21 da API) e versões mais recentes, o sistema gera uma exceção quando você chama bindService() com uma intent implícita.

Criação de um intent

Um objeto Intent carrega informações que o sistema Android usa para determinar qual componente iniciar (como o nome exato do componente ou a categoria do componente que receberá a intent), além de informações que o componente destinatário usa para executar a ação corretamente (como a ação a ser tomada e os dados a serem consultados).

As principais informações contidas em um Intent são as seguintes:

Nome do componente
O nome do componente a ser iniciado.

Isso é opcional, mas é a informação essencial que torna uma intent explícita, o que significa que ela precisa ser entregue apenas ao componente do app definido pelo nome do componente. Sem um nome de componente, a intent será implícita, e o sistema decidirá qual componente receberá a intent com base nas outras informações da intent (como ação, dados e categoria, descritas abaixo). Se você precisar iniciar um componente específico no app, especifique o nome dele.

Observação:ao iniciar uma Service, sempre especifique o nome do componente. Caso contrário, não será possível ter certeza de qual serviço responderá à intent, e o usuário não poderá ver qual serviço é iniciado.

Esse campo da Intent é um objeto ComponentName, que pode ser especificado usando um nome de classe totalmente qualificado do componente de destino, incluindo o nome do pacote do app. Por exemplo, com.example.ExampleActivity. Você pode definir o nome do componente com setComponent(), setClass(), setClassName() ou com o construtor Intent.

Ação
Uma string que especifica a ação genérica a ser realizada (como visualizar ou escolher).

No caso de um intent de transmissão, essa é a ação que entrou em vigor e que está sendo relatada. A ação determina em grande parte como o restante da intent é estruturado, especialmente as informações contidas nos dados e nos extras.

É possível especificar as próprias ações para uso por intents no seu app (ou para uso por outros apps para invocar componentes no seu app), mas você geralmente especifica constantes de ação definidas pela classe Intent ou por outras classes do framework. Confira algumas ações comuns para iniciar uma atividade:

ACTION_VIEW
Use essa ação em uma intent com startActivity() quando houver informações que uma atividade pode mostrar ao usuário, como uma foto para ver em um app de galeria ou um endereço para ver em um app de mapas.
ACTION_SEND
Também conhecida como intent de compartilhamento, use-a em uma intent com startActivity() quando houver dados que o usuário pode compartilhar por outro app, como um app de e-mail ou de compartilhamento em redes sociais.

Consulte a referência da classe Intent para ver mais constantes que definem ações genéricas. Outras ações são definidas em outros lugares no framework do Android, como em Settings para ações que abrem telas específicas no app Configurações do sistema.

Você pode especificar a ação para uma intent com setAction() ou com um construtor Intent.

Se você definir suas próprias ações, inclua o nome do pacote do seu app como prefixo, conforme mostrado neste exemplo:

Kotlin

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

Java

static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
Dados
O URI (um objeto Uri) que referencia os dados a serem aproveitados e/ou o tipo MIME desses dados. Geralmente, o tipo de dados fornecido é determinado pela ação da intent. Por exemplo, se a ação for ACTION_EDIT, os dados precisam conter o URI do documento a ser editado.

Ao criar uma intent, muitas vezes é importante especificar o tipo de dados (o tipo MIME) além do URI. Por exemplo, uma atividade capaz de mostrar imagens provavelmente não poderá reproduzir um arquivo de áudio, mesmo que os formatos de URI possam ser semelhantes. A especificação do tipo MIME dos dados ajuda o sistema Android a encontrar o melhor componente para receber a intent. No entanto, o tipo MIME às vezes pode ser inferido pelo URI, principalmente quando os dados são um URI de content:. Um URI content: indica que os dados estão localizados no dispositivo e são controlados por um ContentProvider, que torna o tipo MIME de dados visível para o sistema.

Para definir apenas o URI de dados, chame setData(). Para definir apenas o tipo MIME, chame setType(). Se necessário, é possível definir ambos explicitamente com setDataAndType().

Cuidado:se você quiser definir o URI e o tipo MIME, não chame setData() e setType(), porque um deles anula o outro. Sempre use setDataAndType() para definir o URI e o tipo MIME.

Categorias
Uma string que contém outras informações sobre o tipo de componente que processará a intent. Qualquer número de descrições de categoria pode ser colocado em uma intent, mas a maioria delas não exige uma categoria. Confira algumas categorias comuns:
CATEGORY_BROWSABLE
A atividade de destino permite ser iniciada por um navegador da Web para exibir dados referenciados por um link, como uma imagem ou uma mensagem de e-mail.
CATEGORY_LAUNCHER
A atividade é a atividade inicial de uma tarefa e é listada no inicializador do aplicativo do sistema.

Consulte a descrição da classe Intent para ver a lista completa de categorias.

É possível especificar uma categoria com addCategory().

As propriedades listadas acima (nome do componente, ação, dados e categoria) representam as características de definição de uma intent. Ao ler essas propriedades, o sistema Android pode resolver qual componente do app ele precisa iniciar. No entanto, uma intent pode carregar outras informações que não afetam a forma como ela é resolvida para um componente do app. Os intents também podem fornecer o seguinte:

Extras
Pares de chave-valor que carregam informações adicionais necessárias para realizar a ação solicitada. Assim como algumas ações usam determinados tipos de URIs de dados, outras também usam determinados extras.

É possível adicionar dados com vários métodos putExtra(), cada um aceitando dois parâmetros: o nome da chave e o valor. Você também pode criar um objeto Bundle com todos os dados extras e inserir a Bundle no Intent com putExtras().

Por exemplo, ao criar uma intent para enviar um e-mail com ACTION_SEND, é possível especificar o destinatário to com a chave EXTRA_EMAIL e o assunto com a chave EXTRA_SUBJECT.

A classe Intent especifica muitas constantes EXTRA_* para tipos de dados padronizados. Se for necessário declarar suas próprias chaves extras (para intents que seu app receba), inclua o nome do pacote dele como prefixo, conforme mostrado no exemplo a seguir:

Kotlin

const val EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS"

Java

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

Cuidado: não use dados Parcelable ou Serializable ao enviar uma intent que você espera que outro app receba. Se um app tentar acessar dados em um objeto Bundle, mas não tiver acesso à classe parcelada ou serializada, o sistema vai gerar uma RuntimeException.

Sinalizações
As sinalizações são definidas na classe Intent e funcionam como metadados para a intent. As sinalizações podem instruir o sistema Android a iniciar uma atividade (por exemplo, a que tarefa a atividade precisa pertencer) e como tratá-la depois que ela for iniciada (por exemplo, se ela pertence à lista de atividades recentes).

Para mais informações, consulte o método setFlags().

Exemplo de intent explícito

Uma intent explícita é usada para iniciar um componente de app específico, como uma atividade ou serviço específico no app. Para criar uma intent explícita, defina o nome do componente para o objeto Intent. Todas as outras propriedades da intent são opcionais.

Por exemplo, se você criou um serviço no seu app chamado DownloadService, projetado para fazer o download de um arquivo da Web, ele pode ser iniciado com o seguinte 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);

O construtor Intent(Context, Class) fornece ao app Context e ao componente um objeto Class. Dessa forma, essa intent inicia explicitamente a classe DownloadService no app.

Para mais informações sobre como criar e iniciar um serviço, consulte o guia Serviços.

Exemplo de intent implícito

Uma intent implícita especifica uma ação que pode invocar qualquer app no dispositivo capaz de realizar a ação. O uso de uma intent implícita é útil quando seu app não pode realizar a ação, mas outros apps provavelmente podem e você quer que o usuário escolha qual app usar.

Por exemplo, se você tiver conteúdo que quer que o usuário compartilhe com outras pessoas, crie uma intent com a ação ACTION_SEND e adicione extras que especifiquem o conteúdo a ser compartilhado. Quando você chama startActivity() com essa intent, o usuário pode escolher um app para compartilhar o conteúdo.

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

Quando startActivity() é chamado, o sistema examina todos os apps instalados para determinar quais podem processar esse tipo de intent, ou seja, uma intent com a ação ACTION_SEND e que carrega dados de texto/simples. Se houver apenas um app com capacidade de processamento, ele será aberto imediatamente e receberá a intent. Se nenhum outro app puder lidar com isso, seu app poderá capturar a ActivityNotFoundException que ocorre. Se várias atividades aceitarem a intent, o sistema exibirá uma caixa de diálogo como a mostrada na Figura 2, para que o usuário possa escolher qual app usar.

Para mais informações sobre como iniciar outros apps, consulte o guia sobre como enviar o usuário para outro app.

Figura 2. Caixa de diálogo seletora.

Como forçar um seletor de aplicativo

Quando há mais de um app que responde à intent implícita, o usuário pode selecionar qual app usar e tornar esse app a escolha padrão para a ação. A capacidade de selecionar um padrão é útil ao realizar uma ação para a qual o usuário provavelmente quer usar o mesmo app todas as vezes, como ao abrir uma página da Web (os usuários geralmente preferem apenas um navegador).

No entanto, se vários apps puderem responder à intent e o usuário quiser usar um app diferente de cada vez, mostre uma caixa de diálogo seletora de forma explícita. A caixa de diálogo seletora solicita que o usuário selecione qual app usar para a ação. O usuário não pode selecionar um app padrão para a ação. Por exemplo, quando o app realiza "compartilhar" com a ação ACTION_SEND, os usuários podem querer compartilhar usando um app diferente, dependendo da situação atual. Portanto, use sempre a caixa de diálogo seletora, conforme mostrado na Figura 2.

Para mostrar o seletor, crie um Intent usando createChooser() e transmita-o para startActivity(), conforme mostrado no exemplo abaixo. Este exemplo mostra uma caixa de diálogo com uma lista de apps que respondem à intent transmitida ao método createChooser() e usa o texto fornecido como título da caixa de 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);
}

Detectar inicializações de intents não seguras

O app pode iniciar intents para navegar entre componentes dentro do app ou para executar uma ação em nome de outro app. Para melhorar a segurança da plataforma, o Android 12 (nível 31 da API) e versões mais recentes oferecem um recurso de depuração que avisa se o app realiza uma inicialização não segura de uma intent. Por exemplo, o app pode executar uma inicialização não segura de uma intent aninhada, que é uma intent transmitida como um extra em outra.

Se o app realizar as duas ações a seguir, o sistema detectará uma inicialização de intent não segura e ocorrerá uma violação de StrictMode:

  1. O app separa as intents aninhadas das outras intents enviadas.
  2. O app imediatamente inicia um componente do app usando essa intent aninhada, como ao transmitir a intent para startActivity(), startService() ou bindService().

Para saber mais sobre como identificar essa situação e fazer mudanças no seu app, leia a postagem do blog sobre Android Nesting Intents (link em inglês) no Medium.

Verificar se há inicializações de intents não seguras

Para verificar se há inicializações de intents não seguras no app, chame detectUnsafeIntentLaunch() ao configurar o VmPolicy, conforme mostrado no snippet de código a seguir. Caso o app detecte uma violação de StrictMode, é recomendado interromper a execução para proteger informações possivelmente confidenciais.

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

Usar intents de forma mais responsável

Para minimizar a chance de um lançamento de intent não seguro e uma violação do StrictMode, siga estas práticas recomendadas.

Copie apenas os extras essenciais nas intents e execute todas as limpezas e validações necessárias. O app pode copiar os extras de uma intent para outra usada para iniciar um novo componente. Isso ocorre quando o app chama putExtras(Intent) ou putExtras(Bundle). Se o app executar uma dessas operações, copie apenas os extras esperados pelo componente de recebimento. Se a outra intent (que recebe a cópia) iniciar um componente que não foi exportado, limpe e valide os extras antes de copiá-los para a intent que inicia o componente.

Não exporte os componentes do app sem necessidade. Por exemplo, se você pretende iniciar um componente de app usando uma intent aninhada interna, defina o atributo android:exported desse componente como false.

Use um PendingIntent em vez de uma intent aninhada. Dessa forma, quando outro app separa a PendingIntent do que contém Intent, ele pode iniciar a PendingIntent usando a identidade do seu app. Essa configuração permite que o outro app inicie com segurança qualquer componente, inclusive um componente não exportado, no seu app.

O diagrama na Figura 2 mostra como o sistema transmite o controle do seu app (cliente) para outro (serviço) e de volta para seu app:

  1. O app cria uma intent que invoca uma atividade em outro app. Nessa intent, você adiciona um objeto PendingIntent como extra. Essa intent pendente invoca um componente no app. Esse componente não é exportado.
  2. Ao receber a intent do seu app, o outro app extrai o objeto PendingIntent aninhado.
  3. O outro app invoca o método send() no objeto PendingIntent.
  4. Depois de passar o controle de volta ao app, o sistema invoca a intent pendente usando o contexto do app.

Figura 2. Diagrama de comunicação entre apps ao usar uma intent pendente aninhada.

Como receber um intent implícito

Para anunciar quais intents implícitas seu app pode receber, declare um ou mais filtros de intent para cada um dos componentes do app com um elemento <intent-filter> no arquivo de manifesto. Cada filtro de intent especifica o tipo de intents aceito com base na ação, nos dados e na categoria da intent. O sistema vai enviar uma intent implícita ao componente do app somente se a intent puder passar por um dos filtros de intent.

Observação:uma intent explícita é sempre entregue ao destino, independentemente dos filtros de intent declarados pelo componente.

Um componente do app precisa declarar filtros separados para cada job exclusivo que pode fazer. Por exemplo, uma atividade em um app de galeria de imagens pode ter dois filtros: um para visualizar uma imagem e outro para editar uma imagem. Quando a atividade começa, ela inspeciona o Intent e decide como se comportar com base nas informações da Intent, por exemplo, para mostrar ou não os controles do editor.

Cada filtro de intent é definido por um elemento <intent-filter> no arquivo de manifesto do app, aninhado no componente correspondente do app (como um elemento <activity>).

Em cada componente do app que inclui um elemento <intent-filter>, defina explicitamente um valor para android:exported. Esse atributo indica se o componente do app pode ser acessado por outros apps. Em algumas situações, como atividades com filtros de intent que incluem a categoria LAUNCHER, é útil definir esse atributo como true. Caso contrário, é mais seguro definir esse atributo como false.

Aviso:se uma atividade, um serviço ou um broadcast receiver no app usar filtros de intent e não definir explicitamente o valor para android:exported, o app não poderá ser instalado em um dispositivo com o Android 12 ou mais recente.

Dentro de <intent-filter>, é possível especificar o tipo de intents aceitas usando um ou mais destes três elementos:

<action>
Declara a ação da intent aceita no atributo name. O valor precisa ser o valor literal da string de uma ação, não a constante da classe.
<data>
Declara o tipo de dados aceitos, usando um ou mais atributos que especificam vários aspectos do URI de dados (scheme, host, port, path) e do tipo MIME.
<category>
Declara a categoria da intent aceita no atributo name. O valor precisa ser o valor literal da string de uma ação, não a constante da classe.

Observação:para receber intents implícitas, inclua a categoria CATEGORY_DEFAULT no filtro de intents. Os métodos startActivity() e startActivityForResult() tratam todas as intents como se elas declarassem a categoria CATEGORY_DEFAULT. Se você não declarar essa categoria no filtro de intents, nenhuma intent implícita será resolvida para sua atividade.

Por exemplo, confira uma declaração de atividade com um filtro de intent para receber uma intent ACTION_SEND quando o tipo de dado for 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>

É possível criar um filtro que inclua mais de uma instância de <action>, <data> ou <category>. Se fizer isso, será necessário ter certeza de que o componente pode processar todas as combinações desses elementos de filtro.

Para processar vários tipos de intents, mas apenas em combinações específicas de ação, dados e tipo de categoria, você precisará criar vários filtros de intents.

Uma intent implícita é testada em relação a um filtro, comparando a intent a cada um dos três elementos. Para ser entregue ao componente, o intent deve passar por todos os três testes. Se ele não corresponder a nenhum deles, o sistema Android não vai entregar a intent ao componente. No entanto, como um componente pode ter vários filtros de intent, uma intent que não passar por um dos filtros de um componente poderá passar por outro filtro. Confira mais informações sobre como o sistema resolve intents na seção abaixo sobre Resolução de intents.

Cuidado : o uso de um filtro de intent não é uma maneira segura de impedir que outros apps iniciem seus componentes. Embora os filtros de intent restrinjam um componente a responder apenas a determinados tipos de intents implícitas, outro app poderá iniciar o componente do app usando uma intent explícita, caso o desenvolvedor determine os nomes dos componentes. Se for importante que somente seu próprio app possa iniciar um dos componentes, não declare filtros de intent no manifesto. Em vez disso, defina o atributo exported como "false" para esse componente.

Da mesma forma, para evitar a execução acidental do Service de um app diferente, use sempre uma intent explícita para iniciar seu próprio serviço.

Observação:para todas as atividades, é necessário declarar os filtros de intent no arquivo de manifesto. No entanto, os filtros para broadcast receivers podem ser registrados dinamicamente. Para isso, chame registerReceiver(). Cancele o registro do receptor com unregisterReceiver(). Isso permite que o app detecte transmissões específicas durante um período especificado apenas enquanto ele está em execução.

Exemplos de filtros

Para demonstrar alguns dos comportamentos do filtro de intent, confira um exemplo do arquivo de manifesto de um app de compartilhamento em redes sociais:

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

A primeira atividade, MainActivity, é o ponto de entrada principal do app, ou seja, a atividade que aberta quando o usuário inicializa o app pela primeira vez com o ícone na tela de início:

  • A ação ACTION_MAIN indica que esse é o ponto de entrada principal e não espera nenhum dado de intent.
  • A categoria CATEGORY_LAUNCHER indica que o ícone dessa atividade precisa ser colocado no Acesso rápido aos apps do sistema. Se o elemento <activity> não especificar um ícone com icon, o sistema usará o ícone do elemento <application>.

Esses dois devem ser pareados para que a atividade apareça no inicializador do aplicativo.

A segunda atividade, ShareActivity, destina-se a facilitar o compartilhamento de texto e conteúdo de mídia. Embora os usuários possam entrar nessa atividade navegando até ela a partir de MainActivity, eles também podem inserir ShareActivity diretamente de outro app que emite uma intent implícita correspondente a um dos dois filtros de intent.

Observação:o tipo MIME, application/vnd.google.panorama360+jpg, é um tipo de dados especial que especifica fotos panorâmicas que podem ser processadas com as APIs do Google Panorama.

Fazer a correspondência de intents com filtros de intents de outros apps

Caso outro app seja direcionado ao Android 13 (nível 33 da API) ou versões mais recentes, ele só poderá processar a intent do app se ela corresponder às ações e categorias de um elemento <intent-filter> nesse outro app. Se o sistema não encontrar uma correspondência, uma ActivityNotFoundException será gerada. O app de envio precisa processar essa exceção.

Da mesma forma, se você atualizar o app para que ele seja destinado ao Android 13 ou versões mais recentes, todas as intents originadas de apps externos serão entregues a um componente exportado do app somente se essa intent corresponder às ações e categorias de um elemento <intent-filter> declarado pelo app. Esse comportamento ocorre independentemente da versão do SDK de destino do app de envio.

Nos casos a seguir, a correspondência de intent não é aplicada:

  • Intents entregues a componentes que não declaram filtros de intent.
  • Intents originadas de um mesmo app.
  • Intents do sistema, ou seja, as intents enviadas do UID do sistema (uid=1000). Apps do sistema incluem system_server e apps que definem android:sharedUserId como android.uid.system.
  • Intents originadas de uma raiz.

Saiba mais sobre a correspondência de intent.

Uso de um intent pendente

Um objeto PendingIntent é um wrapper em torno de um objeto Intent. O objetivo principal de um PendingIntent é conceder permissão a um aplicativo externo para usar o Intent contido como se ele fosse executado no próprio processo do app.

Os principais casos de uso de um intent pendente são os seguintes:

  • Declarar uma intent a ser executada quando o usuário realiza uma ação com a Notificação (o NotificationManager do sistema Android executa o Intent).
  • Declarar uma intent a ser executada quando o usuário realiza uma ação com seu widget de app (o app de tela inicial executa a Intent).
  • Declarar uma intent a ser executada em um momento futuro especificado (o AlarmManager do sistema Android executa o Intent).

Assim como cada objeto Intent é projetado para ser processado por um tipo específico de componente do app (Activity, Service ou BroadcastReceiver), uma PendingIntent também precisa ser criada com a mesma consideração. Ao usar uma intent pendente, seu app não a executa com uma chamada como startActivity(). Em vez disso, é necessário declarar o tipo do componente pretendido ao criar o PendingIntent chamando o respectivo método do criador:

A menos que seu app esteja recebendo intents pendentes de outros apps, os métodos acima para criar um PendingIntent são provavelmente os únicos PendingIntent que você vai precisar.

Cada método usa a Context do app atual, a Intent que você quer unir e uma ou mais flags que especificam como a intent precisa ser usada. Por exemplo, se ela pode ser usada mais de uma vez.

Para saber mais sobre o uso de intents pendentes, consulte a documentação de cada um dos respectivos casos de uso, como nos guias da API Notificações e Apps Widgets.

Especificar a mutabilidade

Caso o app seja destinado ao Android 12 ou versões mais recentes, especifique a mutabilidade de cada objeto PendingIntent criado pelo app. Para declarar que determinado objeto PendingIntent é mutável ou imutável, use a sinalização PendingIntent.FLAG_MUTABLE ou PendingIntent.FLAG_IMMUTABLE, respectivamente.

Se o app tentar criar um objeto PendingIntent sem definir nenhuma flag de mutabilidade, o sistema vai gerar uma IllegalArgumentException e a seguinte mensagem será exibida no 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.

Criar intents pendentes imutáveis sempre que possível

Na maioria dos casos, o app precisa criar objetos PendingIntent imutáveis, conforme mostrado no snippet de código a seguir. Se um objeto PendingIntent for imutável, outros apps não poderão modificar a intent para ajustar o resultado da invocação da 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);

No entanto, alguns casos de uso exigem objetos PendingIntent mutáveis:

  • Compatibilidade com ações de resposta direta em notificações. A resposta direta requer uma mudança nos dados de clipe no objeto PendingIntent associado à resposta. Normalmente, essa mudança é solicitada transmitindo FILL_IN_CLIP_DATA como uma flag para o método fillIn().
  • Associar notificações ao framework do Android Auto usando instâncias de CarAppExtender.
  • Colocar conversas em balões usando instâncias de PendingIntent. Um objeto PendingIntent mutável permite que o sistema aplique as flags corretas, como FLAG_ACTIVITY_MULTIPLE_TASK e FLAG_ACTIVITY_NEW_DOCUMENT.
  • solicitar informações de local do dispositivo chamando requestLocationUpdates() ou APIs semelhantes. O objeto PendingIntent mutável permite que o sistema adicione extras de intent que representam eventos de ciclo de vida do local. Esses eventos incluem uma mudança de local e a disponibilização de um provedor.
  • Programar alarmes usando o AlarmManager. O objeto PendingIntent mutável permite que o sistema adicione o extra da intent EXTRA_ALARM_COUNT. Esse extra representa o número de vezes que um alarme recorrente foi acionado. Ao conter esse extra, a intent pode notificar com precisão um app sobre se um alarme recorrente foi acionado várias vezes, por exemplo, quando o dispositivo estava suspenso.

Caso seu app crie um objeto PendingIntent mutável, é altamente recomendável usar uma intent explícita e preencher o ComponentName. Dessa forma, todas as vezes que outro app invocar PendingIntent e devolver o controle para o app, o mesmo componente será iniciado.

Usar intents explícitas dentro de intents pendentes

Para definir melhor como outros apps podem usar as intents pendentes do seu app, sempre vincule a intent pendente a uma intent explícita. Para ajudar a seguir essa prática recomendada, faça o seguinte:

  1. Verifique se os campos de ação, pacote e componente da intent base estão definidos.
  2. Use o FLAG_IMMUTABLE, adicionado no Android 6.0 (nível 23 da API), para criar intents pendentes. Essa sinalização impede que apps que recebem uma PendingIntent preencham propriedades não preenchidas. Se a minSdkVersion do seu app for 22 ou anterior, você poderá oferecer segurança e compatibilidade usando este código:

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

Resolução de intents

Quando o sistema recebe uma intent implícita para iniciar uma atividade, ele procura a melhor atividade para a intent comparando-a aos filtros de intent com base em três aspectos:

  • Ação
  • Dados (URI e tipo de dados)
  • Categoria

As seções abaixo descrevem como as intents são associadas aos componentes apropriados de acordo com a declaração do filtro de intent no arquivo de manifesto de um app.

Teste de ação

Para especificar as ações de intent aceitas, um filtro de intent pode declarar zero ou mais elementos <action>, conforme mostrado no exemplo abaixo.

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

Para transmitir esse filtro, a ação especificada em Intent precisa corresponder a uma das ações listadas nele.

Se o filtro não listar nenhuma ação, não haverá nada a que uma intent corresponda, portanto, todas as intents falharão no teste. No entanto, se um Intent não especificar uma ação, ele será aprovado no teste, desde que o filtro contenha pelo menos uma ação.

Teste de categoria

Para especificar as categorias de intent aceitas, um filtro de intent pode declarar zero ou mais elementos <category>, conforme mostrado no exemplo abaixo.

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

Para que uma intent seja aprovada no teste, cada categoria no Intent precisa corresponder a uma categoria no filtro. O inverso não é necessário. O filtro de intent pode declarar mais categorias do que o especificado em Intent e o Intent ainda é transmitido. Portanto, uma intent sem categorias sempre passa nesse teste, independente das categorias declaradas no filtro.

Observação:o Android aplica automaticamente a categoria CATEGORY_DEFAULT a todas as intents implícitas transmitidas a startActivity() e startActivityForResult(). Se você quiser que a atividade receba intents implícitas, ela precisa incluir uma categoria para "android.intent.category.DEFAULT" nos filtros de intent, como mostrado no exemplo de <intent-filter> anterior.

Teste de dados

Para especificar os dados de intent aceitos, um filtro de intent pode declarar zero ou mais elementos <data>, conforme mostrado no exemplo abaixo:

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

Cada elemento <data> pode especificar uma estrutura de URI e um tipo de dados (tipo de mídia MIME). Cada parte do URI é um atributo separado: scheme, host, port e path:

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

O exemplo abaixo mostra possíveis valores para esses atributos:

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

Nesse URI, o esquema é content, o host é com.example.project, a porta é 200 e o caminho é folder/subfolder/etc.

Cada um desses atributos é opcional em um elemento <data>, mas há dependências lineares:

  • Se não houver esquema especificado, o host será ignorado.
  • Se não houver host especificado, a porta será ignorada.
  • Se não houver esquema nem host especificado, o caminho será ignorado.

Quando o URI em uma intent é comparado a uma especificação de URI em um filtro, a comparação é feita apenas com as partes do URI incluídas no filtro. Por exemplo:

  • Se um filtro especificar apenas um esquema, todos os URIs com esse esquema corresponderão ao filtro.
  • Se um filtro especificar um esquema e uma autoridade, mas nenhum caminho, todos os URIs com o mesmo esquema e autoridade passarão pelo filtro, independentemente dos caminhos.
  • Se um filtro especificar um esquema, uma autoridade e um caminho, apenas URIs com o mesmo esquema, autoridade e caminho passarão pelo filtro.

Observação:uma especificação de caminho pode conter um asterisco curinga (*) para exigir apenas uma correspondência parcial do nome do caminho.

O teste de dados compara o URI e o tipo MIME da intent com um URI e um tipo MIME especificados no filtro. As regras são as seguintes:

  1. A intent que não contiver URI nem tipo MIME vai passar no teste somente se o filtro não especificar nenhum URI ou tipo MIME.
  2. Uma intent que contenha um URI, mas nenhum tipo MIME (nem explícito nem inferível pelo URI) vai passar no teste somente se o URI corresponder ao formato de URI do filtro e se o filtro não especificar um tipo MIME.
  3. A intent que contiver um tipo MIME, mas não um URI, vai passar no teste somente se o filtro listar o mesmo tipo MIME e não especificar um formato de URI.
  4. As intents que contêm um URI e um tipo MIME (explícito ou inferível pelo URI) vão passar na parte do tipo MIME do teste somente se esse tipo corresponder a um listado no filtro. Ela vai passar na parte do URI do teste se corresponder a um URI no filtro ou se tiver um URI content: ou file: e o filtro não especificar um URI. Em outras palavras, presume-se que um componente seja compatível com dados content: e file: se o filtro listar apenas um tipo MIME.

Observação:se uma intent especificar um URI ou tipo MIME, o teste de dados vai falhar se não houver elementos <data> no <intent-filter>.

Essa última regra, a regra (d), reflete a expectativa de que os componentes possam receber dados locais de um arquivo ou provedor de conteúdo. Portanto, os filtros podem listar apenas um tipo de dados e não precisam nomear explicitamente os esquemas content: e file:. O exemplo abaixo mostra um caso típico em que um elemento <data> informa ao Android que o componente pode receber dados de imagem de um provedor de conteúdo e mostrá-los:

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

Os filtros que especificam um tipo de dados, mas não um URI, são talvez os mais comuns, porque a maioria dos dados disponíveis é dispensada pelos provedores de conteúdo.

Outra configuração comum é de filtros com um esquema e um tipo de dados. Por exemplo, um elemento <data> como o seguinte informa ao Android que o componente pode extrair dados de vídeo da rede para realizar a ação:

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

Correspondência de intents

As intents são correspondidas com filtros de intent não apenas para descobrir um componente de destino a ser ativado, mas também para descobrir algo sobre o conjunto de componentes no dispositivo. Por exemplo, o app Home preenche o Acesso rápido aos apps encontrando todas as atividades com filtros de intent que especificam a ação ACTION_MAIN e a categoria CATEGORY_LAUNCHER. Uma correspondência só será bem-sucedida se as ações e categorias da intent corresponderem ao filtro, conforme descrito na documentação da classe IntentFilter.

O aplicativo pode usar a correspondência de intents de modo semelhante ao feito pelo aplicativo Home. O PackageManager tem um conjunto de métodos query...() que retornam todos os componentes que podem aceitar uma intent específica e uma série semelhante de métodos resolve...() que determinam o melhor componente para responder a uma intent. Por exemplo, queryIntentActivities() retorna uma lista de todas as atividades que podem executar a intent transmitida como um argumento, e queryIntentServices() retorna uma lista semelhante de serviços. Nenhum dos métodos ativa os componentes, eles apenas listam os que podem responder. Há um método semelhante, queryBroadcastReceivers(), para broadcast receivers.