Introdução às atividades

A classe Activity é um componente crucial de um app Android, e a maneira como as atividades são iniciadas e reunidas é uma parte fundamental do modelo de aplicativo da plataforma. Diferentemente dos paradigmas de programação em que os apps são lançados com um método main, o sistema Android inicia o código em uma instância Activity invocando métodos de callback específicos que correspondem a estágios específicos do ciclo de vida.

Este documento apresenta o conceito de atividades e fornece uma orientação simplificada sobre como trabalhar com elas. Para mais informações sobre as práticas recomendadas para a arquitetura do seu app, consulte o Guia para a arquitetura do app.

O conceito de atividades

A experiência em apps para dispositivos móveis é diferente da versão para computador, porque a interação do usuário com o app nem sempre começa no mesmo lugar. Em vez disso, a jornada do usuário geralmente começa de forma não determinista. Por exemplo, se você abrir um app de e-mails na tela inicial, poderá ver uma lista de e-mails. Por outro lado, se você estiver usando um app de rede social que inicialize seu app de e-mails, poderá ir diretamente para a tela do app de e-mails para escrever uma mensagem.

A classe Activity foi desenvolvida para facilitar esse paradigma. Quando um app invoca outro, o app de chamada invoca uma atividade no outro app, em vez do app como um todo. Dessa forma, a atividade serve como ponto de entrada para a interação de um app com o usuário. Você implementa uma atividade como uma subclasse da classe Activity.

Uma atividade fornece a janela em que o app desenha a própria interface. Essa janela normalmente preenche a tela, mas pode ser menor do que a tela e flutuar sobre outras janelas.

Normalmente, uma atividade em um app é especificada como a atividade principal, que é a primeira tela a aparecer quando o usuário inicia o app. Em apps modernos do Compose, essa é a única atividade necessária, já que ela hospeda elementos combináveis em uma arquitetura de atividade única em vez de ter uma hierarquia de visualização. Em vez de o app ter várias atividades para telas, elementos combináveis no host de atividade têm vários destinos de navegação.

Para usar as atividades no seu app, é necessário registrar informações sobre elas no manifesto do app, e é recomendável conhecer os ciclos de vida das atividades. O restante deste documento apresenta esses assuntos.

Como configurar o manifesto

Para que seu app possa usar as atividades, você precisa declará-las junto a alguns dos atributos delas no manifesto.

Declarar atividades

Para declarar sua atividade, abra o arquivo de manifesto e adicione um elemento <activity> como filho do elemento <application>. Exemplo:

<manifest ... >
  <application ... >
      <activity android:name=".ExampleActivity" />
      ...
  </application ... >
  ...
</manifest >

O único atributo obrigatório para esse elemento é android:name, que especifica o nome da classe da atividade. Você também pode adicionar atributos que definem características de atividade, como etiqueta, ícone ou tema da interface. Para mais informações sobre esses e outros atributos, consulte a documentação de referência do elemento <activity>.

Declarar filtros de intent

Os filtros de intent são um recurso muito poderoso da plataforma Android. Eles oferecem a capacidade de iniciar uma atividade com base não apenas em uma solicitação explícita, mas também implícita. Por exemplo, uma solicitação explícita pode dizer ao sistema para "Iniciar a atividade de envio de e-mail no app Gmail". Por outro lado, uma solicitação implícita diz ao sistema para "Iniciar uma tela de envio de e-mail em qualquer atividade que possa fazer o trabalho". Quando a interface do sistema pergunta a um usuário qual app usar na execução de uma tarefa, um filtro de intent está em ação.

Você pode aproveitar esse recurso declarando um atributo <intent-filter> no elemento <activity>. A definição desse elemento inclui um elemento <action> e, opcionalmente, um elemento <category> e/ou um elemento <data>. Esses elementos são combinados para especificar o tipo de intent ao qual sua atividade pode responder. Por exemplo, o snippet de código a seguir mostra como configurar uma atividade que envia dados de texto e e-mail e recebe solicitações de outras atividades para fazer isso:

<activity android:name=".ExampleActivity" android:icon="@drawable/app_icon">
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="text/plain" />
    </intent-filter>
   <intent-filter>
        <action android:name="android.intent.action.SENDTO" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="mailto" />
    </intent-filter>
</activity>

Neste exemplo, o elemento <action> especifica que essa atividade envia dados. Declarar o elemento <category> como DEFAULT permite que a atividade receba solicitações de inicialização. O elemento <data> especifica o tipo de dados que essa atividade pode enviar. O snippet de código a seguir mostra como chamar a atividade descrita acima para compor um e-mail:

fun composeEmail(addresses: Array<String>, subject: String) {
    val intent = Intent(Intent.ACTION_SENDTO).apply {
        data = Uri.parse("mailto:") // Only email apps handle this.
        putExtra(Intent.EXTRA_EMAIL, addresses)
        putExtra(Intent.EXTRA_SUBJECT, subject)
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    }
}

Se você pretende que seu app seja autossuficiente e não permita que outros apps ativem as atividades dele, não precisa de outros filtros de intent. As atividades que você não quiser disponibilizar para outros apps não precisam ter filtros de intent, e você pode iniciá-las usando intents explícitos. Para mais informações sobre como suas atividades podem responder a intents, consulte Intents e filtros de intent.

Processar intents recebidas

O exemplo a seguir mostra um padrão para gerenciar o ciclo de vida da atividade ao processar vários tipos de intent: compartilhamentos de texto único, imagens únicas e matrizes de imagens múltiplas. Ao encaminhar essas entradas variadas por uma função handleIntent centralizada, isso garante que as ações ACTION_SEND e ACTION_SEND_MULTIPLE sejam analisadas e delegadas corretamente ao ViewModel para uma atualização reativa da interface.

class ExampleActivity : ComponentActivity() {
  private val viewModel: MyViewModel by viewModels()

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    handleIntent(intent)
    setContent {
      ComposeApp(viewModel)
    }
  }

  override fun onNewIntent(intent: Intent) {
    super.onNewIntent(intent)
    setIntent(intent)
    handleIntent(intent)
  }

  private fun handleIntent(intent: Intent?) {
    when (intent?.action) {
      Intent.ACTION_SEND -> {
        if ("text/plain" == intent.type) {
          intent.getStringExtra(Intent.EXTRA_TEXT)?.let {
            viewModel.handleText(it) // Update UI to reflect text being shared
          }
        } else if (intent.type?.startsWith("image/") == true) {
          (intent.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java))?.let {
            viewModel.handleImage(it) // Update UI to reflect image being shared
          }
        }
      }

      Intent.ACTION_SEND_MULTIPLE -> {
          if (intent.type?.startsWith("image/") == true) {
              intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM, Uri::class.java)?.let {
                  viewModel.handleMultipleImages(it) // Update UI to reflect multiple images being shared
              }
          } else {
              // Handle other types
          }
      }

      else -> {
          // Handle other intents
      }
    }
  }
}

Declarar permissões

Você pode usar a tag <activity> do manifesto para controlar quais apps podem iniciar uma atividade específica. Uma atividade mãe não pode iniciar uma atividade filha a menos que as duas tenham as mesmas permissões no manifesto. Se você declarar um elemento <uses-permission> para uma atividade mãe, cada atividade filha precisará ter um elemento <uses-permission> correspondente.

Por exemplo, se seu app quiser usar um app hipotético chamado SocialApp para compartilhar uma postagem em rede social, o próprio SocialApp precisa definir a permissão que um app autor da chamada precisa ter:

<manifest>
<activity android:name="...."
   android:permission="com.google.socialapp.permission.SHARE_POST"

/>

Em seguida, para poder chamar o SocialApp, seu app precisa corresponder ao conjunto de permissões definido no manifesto do SocialApp:

<manifest>
   <uses-permission android:name="com.google.socialapp.permission.SHARE_POST" />
</manifest>

Para mais informações sobre permissões e segurança em geral, consulte a Lista de verificação de segurança.

Como gerenciar o ciclo de vida da atividade

Ao longo da vida útil de uma atividade, ela passa por vários estados. Uma série de callbacks são usados para lidar com transições entre estados. As seções a seguir apresentam esses callbacks. Em um app do Compose, não é recomendável se conectar diretamente a esses callbacks. Em vez disso, use a API Lifecycle para observar mudanças de estado. Para mais informações, consulte Integrar o ciclo de vida com o Compose.

onCreate

Você precisa implementar esse callback, que é acionado quando o sistema cria sua atividade. A implementação inicializará os componentes essenciais da atividade. Por exemplo, aqui, o app criará visualizações e vinculará dados a listas.

Em um app do Compose, use esse callback para configurar o elemento combinável host usando setContent, conforme mostrado abaixo:

class MyActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Text(text = stringResource(id = R.string.greeting))
        }
    }
}

Quando onCreate termina, o próximo callback sempre é onStart.

onStart

Quando onCreate sai, a atividade entra no estado "Iniciado" e se torna visível para o usuário. Esse callback contém o que equivale aos preparativos finais da atividade para ir para o primeiro plano e se tornar interativa.

onResume

O sistema invoca esse callback imediatamente antes de a atividade começar a interagir com o usuário. Neste ponto, a atividade fica na parte de cima da pilha de atividades e captura toda a entrada do usuário. A maior parte da funcionalidade principal de um app é implementada no método onResume.

O callback onPause sempre segue onResume.

onPause

O sistema chama onPause quando a atividade perde o foco e entra em um estado "Pausado". Esse estado ocorre quando, por exemplo, o usuário toca no botão "Voltar" ou "Recentes". Quando o sistema chama onPause para sua atividade, isso significa, tecnicamente, que ela ainda está parcialmente visível. Porém, na maioria das vezes, é uma indicação de que o usuário está deixando a atividade e que logo ela entrará no estado "Interrompido" ou "Retomado".

Uma atividade no estado "Pausado" pode continuar atualizando a interface se o usuário estiver esperando por isso. Exemplos de uma dessas atividades incluem a exibição da tela de um mapa de navegação ou de um player de mídia sendo reproduzido. Mesmo que essas atividades percam o foco, o usuário espera que a interface continue sendo atualizada.

Não use onPause para salvar dados do app ou dados do usuário, fazer chamadas de rede ou executar transações de banco de dados. Para mais informações sobre como salvar dados, consulte Como salvar e restaurar o estado transitório da interface.

Quando onPause termina a execução, o próximo callback é onStop ou onResume, dependendo do que acontece depois que a atividade entra no estado "Pausado".

onStop

O sistema chama onStop quando a atividade não está mais visível para o usuário. Isso pode acontecer porque a atividade está sendo destruída, uma nova atividade está sendo iniciada ou uma atividade existente está entrando em um estado "Retomado" e está cobrindo a atividade interrompida. Em todos esses casos, a atividade interrompida não fica mais visível.

O próximo callback que o sistema chamar será onRestart, se a atividade voltar a interagir com o usuário, ou onDestroy se essa atividade for completamente encerrada.

onRestart

O sistema invoca esse callback quando uma atividade no estado "Interrompido" está prestes a ser reiniciada. onRestart restaura o estado da atividade a partir do momento em que ela foi interrompida.

Esse callback é sempre seguido por onStart.

onDestroy

O sistema invoca esse callback antes de uma atividade ser destruída.

Esse é o último callback que a atividade recebe. onDestroy geralmente é implementado para garantir que todos os recursos de uma atividade sejam liberados quando ela (ou o processo que a contém) for destruída.

Esta seção é apenas uma introdução a esse tema. Para um tratamento mais detalhado do ciclo de vida da atividade e dos callbacks dela, consulte Ciclo de vida da atividade.