Buscar

Ciclo de Vida da Activity no Android

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 3, do total de 39 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 6, do total de 39 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 9, do total de 39 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Prévia do material em texto

PROGRAMAÇÃO IV 
AULA 5 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Prof. Ricardo Rodrigues Lecheta 
 
 
CONVERSA INICIAL 
Olá, seja bem-vindo a mais uma aula! 
Nós já aprendemos como criar layouts em XML e como tratar os eventos 
nas telas. A estrutura básica de um aplicativo já estudamos, e nesta aula vamos 
aprender mais detalhes sobre a classe Activity. 
Nos aplicativos Android, tudo gira em torno de uma activity. Ela representa 
uma tela do aplicativo, sendo responsável pelo layout e lógica. Nesta aula, 
vamos estudar o ciclo de vida de uma activity, que é um dos assuntos mais 
importantes para que você seja um desenvolvedor Android de sucesso. Também 
vamos estudar como passar parâmetros de uma tela para outra, e detalhes sobre 
a classe Intent, que é uma das mais importantes no Android, sendo usada desde 
para abrir uma tela do aplicativo até para chamar outros aplicativos como o 
browser, Google Maps, câmera etc. 
TEMA 1 – CICLO DE VIDA DA ACTIVITY 
Chegou o momento de entender um dos assuntos mais importantes sobre 
uma activity, que é o seu ciclo de vida. Falando com um português bem claro, o 
ciclo de vida consiste em um conjunto de métodos da classe Activity que é 
chamado em determinados momentos e representam o estado atual da activity, 
por exemplo, se a tela está visível ou parada em segundo plano (background). 
Um dos métodos do ciclo de vida nós já estudamos, que é o 
onCreate(bundle), chamado uma única vez ao criar a activity e responsável por 
fazer a inicialização do layout e algumas configurações. Outro método é o 
onDestroy(), que é chamado sempre que a tela é fechada, algo que acontece 
quando o usuário clica no botão voltar ou se o método finish() é chamado no 
código. Em ambos os casos, a activity é eliminada de memória, e nesse 
momento, podemos limpar os recursos e objetos utilizados, caso seja 
necessário. 
Uma forma simples de entender o ciclo de vida é com um exemplo prático 
de uso de GPS no aplicativo. Imagine que o aplicativo precisa obter a localização 
do usuário sempre que ele entra em uma determinada tela e depois mostrar um 
mapa com esta localização. Porém, sabemos que ao chamar as funções de 
GPS, existe um aumento significativo no consumo de bateria. Então imagine que 
o aplicativo está mostrando um mapa com a localização do usuário, mas depois 
 
 
disso, o usuário clica no botão Home para voltar à tela inicial para abrir outro 
aplicativo. Neste momento, a activity do mapa terá seus métodos onPause() e 
onStop() chamados, pois a activity foi parada e agora está em segundo plano 
(background). Esses métodos onPause() ou onStop() podem ser utilizados para 
desligar o monitoramento do GPS e economizar recursos e bateria do celular. 
Seguindo o raciocínio, quando o usuário voltar ao aplicativo e mostrar 
novamente aquele mapa com a localização, o método onResume() será 
chamado, o que indica que a tela da activity que possui o mapa está novamente 
visível. Neste momento, podemos ligar novamente o GPS. 
Acredito que, se você entendeu o que foi explicado acima, já é 90% do 
caminho andado, mas, como o tema é um pouco complexo, vamos estudar o 
diagrama de fluxo oficial do ciclo de vida de uma activity e cada método em 
detalhes. 
A figura a seguir demonstra o ciclo de vida completo de uma activity, 
exibindo os possíveis estados e a chamada de cada método. 
Figura 1 – Activity 
 
Depois de ver o diagrama, leia atentamente o significado de cada método 
do ciclo de vida da activity. 
 
 
● onCreate(bundle): o método onCreate(bundle) é chamado uma única 
vez. O objetivo desse método é fazer a inicialização necessária para 
executar o aplicativo. Nele, deve-se criar uma view e chamar o método 
setContentView(view) para configurar o layout da tela. O objeto bundle 
contém os parâmetros enviados para a activity e pode ser nulo. Vamos 
estudar sobre passagem de parâmetros em outra aula. 
● onStart(): o método onStart() é chamado quando a activity está ficando 
visível ao usuário e já tem uma view. Pode ser chamado depois dos 
métodos onCreate() ou onRestart(), dependendo do estado do aplicativo. 
Este método raramente é usado. 
● onRestart(): o método onRestart() é chamado quando uma activity foi 
parada temporariamente e está sendo iniciada outra vez. Esse método 
raramente é usado. 
● onResume(): o método onResume() é chamado quando a activity está no 
topo da pilha “activity stack” e, dessa forma, já está executando como a 
activity principal e interagindo com o usuário. Podemos dizer que o 
método onResume() representa o estado de que a activity está 
executando. É importante você entender que, quando o método 
onResume() executar, o usuário já estará vendo a tela. Por isso, esse 
método é frequentemente utilizado para disparar threads que consultam 
os dados em web services ou banco de dados para atualizar as 
informações da tela. 
● onPause(): sempre que a tela da activity fechar, o método onPause() será 
chamado. Isso pode acontecer se o usuário pressionar o botão Home ou 
o botão voltar do Android. Ou também pode acontecer se você receber 
uma ligação telefônica. Nesse momento, o método onPause() é chamado 
para salvar o estado do aplicativo, para que posteriormente, quando a 
activity voltar a executar, tudo possa ser recuperado, se necessário, no 
método onResume(). 
● onStop(): o método onStop() é chamado logo depois do método 
onPause() e indica que a activity está sendo encerrada e não está mais 
visível ao usuário. Depois de parada, a activity pode ser reiniciada, se 
necessário. Caso isso ocorra, o método onRestart() é chamado para 
reiniciar a activity. Caso a activity fique muito tempo parada em segundo 
plano e o sistema operacional do Android precise limpar os recursos para 
 
 
liberar memória, o método onDestroy() pode ser automaticamente 
chamado para remover completamente a activity da pilha. 
● onDestroy(): o método onDestroy() literalmente encerra a execução de 
uma activity. Ele pode ser chamado automaticamente pelo sistema 
operacional para liberar recursos ou pode ser chamado pelo aplicativo 
com o método finish() da classe Activity. Depois do método onDestroy() 
ter executado, a activity será removida completamente da pilha e seu 
processo no sistema operacional também será completamente encerrado. 
TEMA 2 – EXEMPLO PRÁTICO SOBRE O CICLO DE VIDA 
Para demonstrar as chamadas dos métodos do ciclo de vida, vamos criar 
a classe LogActivity, que sobrescreve os principais métodos do ciclo de vida e 
escreve um log com a classe LogCat. Esta activity não precisa de XML, portanto, 
podemos criá-la como uma classe normal com o wizard > File > New > Kotlin 
File/Class, e podemos deixá-la no mesmo pacote onde estão as outras activities. 
● LogActivity 
package com.example.helloandroid 
 
import androidx.appcompat.app.AppCompatActivity 
import android.os.Bundle 
import android.util.Log 
 
/* Activity que imprime logs nos métodos de ciclo de vida */ 
open class LogActivity : AppCompatActivity() { 
 private val TAG = "android" 
 
 // Nome da classe sem o pacote 
 private val className: String 
 get() { 
 val s = javaClass.name 
 return s.substring(s.lastIndexOf(".")+1) 
 } 
 override fun onCreate(bundle: Bundle?) { 
 super.onCreate(bundle) 
 Log.d(TAG, className + ".onCreate().") 
 } 
 override fun onStart() { 
 super.onStart() 
 Log.d(TAG, className + ".onStart().") 
 } 
 override fun onRestart() { 
 super.onRestart() 
 Log.d(TAG, className + ".onRestart().") 
 } 
 override fun onResume() { 
 super.onResume() 
 Log.d(TAG, className + ".onResume().") 
 
 
 } 
 override fun onPause() { 
 super.onPause() 
 Log.d(TAG, className + ".onPause().") 
 } 
 override fun onSaveInstanceState(outState: Bundle) { 
 super.onSaveInstanceState(outState) 
 Log.d(TAG, className + ".onSaveInstanceState().") 
 } 
 override fun onStop(){ 
 super.onStop() 
 Log.d(TAG, className + ".onStop().") 
 } 
 override fun onDestroy() { 
 super.onDestroy() 
 Log.d(TAG, className + ".onDestroy().") 
 } 
} 
Importante: sempre que sobrescrever um método da classe Activity, 
chame o método da classe-mãe com o super; caso contrário, uma exceção será 
lançada em tempo de execução. 
No início da classe, foi declarada a propriedade className e foi 
sobrescrito o método get() para que ela tenha um retorno, facilitando o seu uso 
no código. A sintaxe é um pouco chatinha, mas a única coisa que esse código 
está fazendo é retornar o nome da classe da activity, sem o pacote. 
Saiba mais 
Para mais detalhes sobre como criar propriedades em Kotlin, segue o link 
oficial: <https://kotlinlang.org/docs/reference/properties.html>. 
Essa classe imprime um log quando os métodos do ciclo de vida são 
chamados. O log é criado com a tag android, portanto, é necessário criar um 
filtro para essa tag na janela do LogCat, conforme já estudamos. O próximo 
passo é alterar a classe MainActivity para ser filha de LogActivity, assim, ela vai 
herdar todos os métodos que foram customizados na sua classe-mãe. 
● MainActivity.kt 
class MainActivity : LogActivity() { 
Altere as outras activities, também. Vamos apresentar exemplos com a 
activity de Cadastro: 
class CadastroActivity : LogActivity() { 
 
 
Ao executar o projeto no emulador, não estamos interessados no layout 
da activity, então, desta vez, vamos analisar somente os logs que mostram as 
chamadas para cada método do ciclo de vida. 
Na primeira vez que o aplicativo executar, os seguintes logs serão 
gerados. Observe que os métodos onCreate(), onStart() e onResume() são 
chamados na sequência. 
D/android: MainActivity.onCreate(). 
D/android: MainActivity.onStart(). 
D/android: MainActivity.onResume(). 
Agora clique no botão voltar (back) do emulador para sair da activity. 
Observe que agora os métodos onPause(), onStop() e onDestroy() foram 
chamados para encerrar o ciclo de vida da activity. 
D/android: MainActivity.onPause(). 
D/android: MainActivity.onStop(). 
D/android: MainActivity.onDestroy(). 
Nesse caso, como o botão voltar foi pressionado, o sistema operacional 
sabe que a activity precisa ser destruída. Por isso, o método onDestroy() foi 
chamado, eliminando completamente a activity da memória. 
Agora volte à tela inicial do emulador e entre novamente no aplicativo. 
Isso vai chamar os métodos onCreate(), onStart() e onResume(). Desta vez, 
clique no botão Home para voltar à tela inicial. Observe que, nos logs, os 
métodos onPause() e onStop() foram chamados, mas não o método onDestroy(). 
D/android: MainActivity.onPause(). 
D/android: MainActivity.onStop(). 
D/android: MainActivity.onSaveInstanceState(). 
Isso acontece porque a activity não foi completamente destruída, apenas 
retirada do topo da pilha, e agora está parada em segundo plano. O método 
onSaveInstanceState() é um caso à parte e vamos estudar depois. 
Por último, encontre o ícone do aplicativo na tela inicial e abra o aplicativo 
novamente. Isso vai reiniciar a activity, fazendo com que ela volte ao topo da 
pilha para ser executada em primeiro plano. Assim, os métodos onRestart(), 
onStart() e onResume() são chamados. 
 
 
D/android: MainActivity.onRestart(). 
D/android: MainActivity.onStart(). 
D/android: MainActivity.onResume(). 
Observe que o método onCreate(bundle) é chamado uma única vez. Se 
a activity estiver parada em segundo plano, o método onCreate(bundle) não será 
chamado novamente. Já o método onResume() é chamado sempre que a tela 
fica visível ao usuário. 
Perceba que os métodos onPause() e onStop() são sempre chamados ao 
sair da tela. O método onResume() é chamado sempre que iniciar ou voltar para 
o aplicativo. 
O método onCreate() é chamado apenas na primeira vez que o aplicativo 
é criado. Caso a activity seja totalmente destruída, evento que acontece ao clicar 
no botão voltar, o método onDestroy() é chamado para eliminar os recursos e 
encerrar o ciclo de vida da activity. Outra forma de destruir a activity é chamar o 
método finish() programaticamente no código. 
Entender o funcionamento do ciclo de vida de uma activity é muito 
importante para desenvolver aplicativos no Android. Temos de estar preparados 
para ações externas que podem ocorrer, tal como uma ligação telefônica para o 
usuário. Essa ação faz com que o sistema operacional do Android interrompa 
(pause/stop) a activity atual, colocando-a em segundo plano. Isso é feito porque 
o aplicativo nativo da ligação é que vai ocupar o topo da pilha de atividades. 
Numa situação como essa, quando a ligação terminar, temos de voltar e executar 
o aplicativo de onde ele parou, sem perder nenhuma informação. 
2.1 Ciclo de vida ao girar o celular? 
Você se lembra que comentamos que ainda estudaríaos o método 
onSaveInstanceState(bundle)? Sabe aquele parâmetro bundle que existe no 
método onCreate(bundle)? Você já pensou para que ele serve? Pois muito bem, 
chegou o momento de obtermos essas respostas. 
Ao girar a tela do celular da vertical para a horizontal, o Android vai destruir 
a activity atual e recriá-la logo em seguida. Sim, é isso mesmo. O Android faz 
isso porque ele precisa recriar todas as views e aplicar espaçamentos e margens 
adequadas para a nova orientação (vertical ou horizontal). 
Durante esse processo de troca de orientação, o Android vai chamar o 
método onSaveInstanceState(bundle) na classe da activity. Esse método 
 
 
recebe um objeto do tipo android.os.Bundle como argumento que deve ser 
utilizado para armazenar os dados em uma estrutura de chave e valor. Para 
demonstrar esse comportamento, vamos executar o aplicativo novamente. Ao 
fazer isso, a MainActivity é criada e inserida no topo da pilha, conforme mostram 
estes logs: 
D/android: MainActivity.onCreate(). 
D/android: MainActivity.onStart(). 
D/android: MainActivity.onResume(). 
Agora, pressione os botões do emulador para girar a tela para a horizontal 
(na barra lateral que fica na direita do emulador). Caso o emulador não gire, 
verifique se a opção auto-rotate está habilitada, pois nas versões mais novas 
do Android isso vem desligado por padrão. 
Figura 2 – Auto-rotate 
 
Crédito: Khuruzero/Shutterstock. 
Os logs a seguir mostram que a activity foi destruída e recriada, mas 
durante esse processo, o método onSaveInstanceState(bundle) foi chamado. 
MainActivity.onPause(). 
MainActivity.onStop(). 
MainActivity.onSaveInstanceState(). // Salva o estado 
MainActivity.onDestroy(). // Destrói a activity 
MainActivity.onCreate(). // Recria a activity e recupera o estado 
MainActivity.onStart(). 
MainActivity.onResume(). 
A ideia é que se o aplicativo salvou valores no Bundle (estrutura de chave 
e valor) lá no método onSaveInstanceState(bundle), é possível recuperar 
 
 
esses valores no bundle que vem como parâmetro no método 
onCreate(bundle?). É para isso que serve esse bundle no método 
onCreate(bundle?). Se for a primeira vez que a activity é executada, o parâmetro 
bundle sempre estará nulo, por isso ele contém a sintaxe da interrogação (? – 
pode ser nulo). Entretanto, no caso da rotação da tela em que o Android faz esse 
processo para recriar a activity, o parâmetro bundle pode estar preenchido com 
os dados salvos no método onSaveInstanceState(bundle). 
Uma vez que já entendemos que o Android precisa matar a tela para 
recriar o layout na vertical ou horizontal, quando que na prática precisamos usar 
esses métodos para manter o estado da tela? Bom, imagine que seu aplicativo 
fez uma busca em um web service para mostrar uma lista de produtos, aliás, 
vamos estudar listas nas próximas aulas. Caso o usuário gire a tela do celular, 
sabemos que a tela será destruída e recriada, e nesse caso, vamos perder a 
lista. Se não fizermos nada, o aplicativo vai buscar a lista novamente no web 
service, porém, para isso, será feita umanova requisição na internet, algo que 
poderia ser evitado. Em vez disso, poderíamos salvar a lista dentro daquele 
bundle (HashTable), e depois podemos recuperar essa lista já pronta quando o 
método onCreate(bundle) for chamado. 
Como ainda não estudamos listas, vamos implementar um exemplo mais 
simples para que você consiga visualizar tudo isso funcionando na prática. A 
ideia é salvar uma variável inteira chamada count na classe. E a cada vez que a 
tela for rotacionada, vamos incrementar o valor dessa variável e salvá-la no 
bundle. A seguir, podemos ver a alteração de código necessária na 
MainActivity. Observe que estão omitidas as demais partes do código para 
simplificar o exemplo: 
class MainActivity : LogActivity() { 
 private var count = 0 
 override fun onCreate(savedInstanceState: Bundle?) { 
 super.onCreate(savedInstanceState) 
 setContentView(R.layout.activity_main) 
 if(savedInstanceState != null) { 
 // recupera o count 
 Log.d("android","Recuperando estado") 
 count = savedInstanceState.getInt("count") 
 } 
 Log.d("android","Count: $count") 
 . . . 
 } 
 override fun onSaveInstanceState(outState: Bundle) { 
 super.onSaveInstanceState(outState) 
 
 
 Log.d("android","Salvando estado") 
 count++ 
 outState.putInt("count",count) 
 } 
 . . . 
} 
Ao executar o projeto, a activity será iniciada e os métodos do ciclo de 
vida serão chamados. Neste momento, o valor da variável count é 0 (zero). 
MainActivity.onCreate(). 
Count: 0 
MainActivity.onStart(). 
MainActivity.onResume(). 
Ao girar a tela do emulador, a activity será destruída conforme já 
estudamos, e o método onSaveInstanceState(bundle) vai incrementar o valor 
da variável count e salvar o estado. Quando o método onCreate(bundle) for 
chamado novamente, a variável count estará lá. Neste exemplo, se você ficar 
rotacionando a tela continuamente, a variável count ficará sendo incrementada 
todas as vezes. 
MainActivity.onPause(). 
MainActivity.onStop(). 
MainActivity.onSaveInstanceState(). 
Salvando estado 
MainActivity.onDestroy(). // Activity foi destruída. 
MainActivity.onCreate(). // Activity recriada. 
Recuperando estado 
Count: 1 
MainActivity.onStart(). 
MainActivity.onResume(). 
2.2 Ciclo de vida ao navegar entre as telas? 
Ao navegar de uma tela para outra chamando método 
startActivity(intent), como fizemos com os botões da tela inicial de login, 
também chamam os métodos do ciclo de vida. Para exercitar mais uma vez e 
consolidar os conceitos aprendidos, abra o aplicativo novamente e fique olhando 
os logs. Depois, clique em algum dos botões como o de cadastro e veja os logs 
novamente. Podemos ver que foram gerados os seguintes logs: 
MainActivity.onPause(). 
CadastroActivity.onCreate(). 
CadastroActivity.onStart(). 
 
 
CadastroActivity.onResume(). 
MainActivity.onStop(). 
MainActivity.onSaveInstanceState(). 
Claramente podemos ver que a MainActivity foi parada (pause/stop) e a 
CadastroActivity foi criada e iniciada com as chamadas dos métodos onCreate 
> onStart > onResume. Então lembre-se, caso a MainActivty estivesse usando 
recursos como GPS, Bluetooth ou qualquer outro que não seja necessário na 
próxima tela, você deve parar estes recursos neste momento. 
Agora na tela de cadastro, clique no botão voltar. Desta vez, veremos os 
seguintes logs, o que mostra que a tela de cadastro foi destruída, e a 
MainActivity foi reiniciada com onRestart > onStart > onResume. Geralmente, 
no onResume() é onde colocamos os códigos para reiniciar os recursos que essa 
tela precisa, seja ligar GPS, Bluetooth, ou refazer uma busca no web service 
para preencher os dados da tela. 
CadastroActivity.onPause(). 
MainActivity.onRestart(). 
MainActivity.onStart(). 
MainActivity.onResume(). 
CadastroActivity.onStop(). 
CadastroActivity.onDestroy(). 
Com isso, concluímos nossos estudos sobre o ciclo de vida de uma 
activity, então, vamos agora falar de Intents. 
TEMA 3 – PASSANDO PARÂMETROS AO NAVEGAR ENTRE TELAS 
Na primeira tela do aplicativo, aprendemos que, para abrir uma nova 
activity, podemos usar o método startActivity(intent): 
startActivity(Intent(this,HomeActivity::class.java)) 
Para facilitar a leitura do código, vamos declarar uma variável para o 
objeto Intent que é passado como parâmetro na chamada do método. 
val intent = Intent(this,HomeActivity::class.java) 
startActivity(intent) 
Mas o que é este objeto intent? Explicando de uma maneira simples, a 
intent é o objeto que contém a "intenção" de abrir a tela, e nela podemos 
 
 
adicionar parâmetros para a próxima tela. Para que você entenda bem como isso 
funciona, vamos mais uma vez fazer um exemplo prático. 
Lembra quando fizemos o login? Nossa classe LoginService está assim: 
class LoginService { 
 fun login(login:String, senha: String): Usuario? { 
 if(login == "ricardo" && senha == "123") { 
 return Usuario("Ricardo","a@a.com") 
 } else if(login == "teste" && senha == "123") { 
 return Usuario("Teste","b@b.com") 
 } 
 return null 
 } 
} 
Isso significa que ao logar com ricardo/123, um objeto Usuário será criado 
e o nome será Ricardo. Crie a combinação que preferir para testar. No próximo 
exemplo, nosso objetivo será mostrar o nome do usuário logado na tela da 
Home, conforme mostra esta figura: 
Figura 3 – Nome de usuário 
 
Crédito: Khuruzero/Shutterstock. 
A seguir, podemos ver o código que trata a função de login e faz a 
passagem de parâmetro do nome do usuário para a próxima tela. As alterações 
estão destacadas em amarelo. Observe que a intent possui o método 
putExtras(bundle) que recebe o objeto Bundle, o qual contém os parâmetros. 
 
 
Mais uma vez, o objeto bundle é uma HashTable e, portanto, possui a estrutura 
de chave e valores. 
private fun onClickLogin() { 
 // Encontra as views 
 val tLogin = findViewById<TextView>(R.id.tLogin) 
 val tSenha = findViewById<TextView>(R.id.tSenha) 
 // Lê os textos 
 val login = tLogin.text.toString() 
 val senha = tSenha.text.toString() 
 // Valida o login 
 val service = LoginService() 
 val user = service.login(login,senha) 
 if(user != null) { 
 // Fecha a tela de login 
 finish() 
 // Abre a tela da home 
 val intent = Intent(this,HomeActivity::class.java) 
 val args = Bundle() 
 args.putString("nome", user.nome) 
 intent.putExtras(args) 
 startActivity(intent) 
 } else { 
 // Erro 
 alert("Login incorreto, digite os dados novamente") 
 } 
} 
No código, veja que ao criar o objeto Bundle, foi dado o nome de args 
para a variável: 
val args = Bundle() 
Isso é um padrão bem comum no desenvolvimento, pois neste caso, o 
Bundle representa os argumentos (args) que estamos passando como 
parâmetro para a próxima activity. 
Para finalizar o exemplo, precisamos modificar o layout da HomeActivity 
para mostrar um texto no centro da tela, portanto, faça a seguinte alteração no 
arquivo activity_home.xml: 
<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout 
xmlns:android="http://schemas.android.com/apk/res/android" 
 android:layout_width="match_parent" 
 android:layout_height="match_parent" 
 android:gravity="center"> 
 <TextView 
 android:id="@+id/tNome" 
 android:layout_width="wrap_content" 
 
 
 android:layout_height="wrap_content" 
 android:textSize="22sp" /> 
</LinearLayout> 
Observe que foi utilizado um LinearLayot com gravity=center para 
deixar o texto centralizado. O id do TextView é tNome, isso significa que 
podemos acessar essa view com essa variável no código, que é criada 
automaticamente pelo Kotlin Extensions. O código atualizado da HomeActivity 
fica assim: 
package com.example.helloandroid 
 
import androidx.appcompat.app.AppCompatActivity 
import android.os.Bundle 
import kotlinx.android.synthetic.main.activity_home.* 
 
class HomeActivity: AppCompatActivity() { 
 override fun onCreate(savedInstanceState: Bundle?) { 
 super.onCreate(savedInstanceState) 
 setContentView(R.layout.activity_home) 
 
 val args = intent.extras 
 val nome = args?.getString("nome") 
 tNome.text = nome 
 } 
} 
Destacamos em amarelo o import que deve ser feito do arquivo XML para 
que o Kotlin Extensions encontre a variável tNome. Preste atenção e não erre 
essa linha. Como é uma boa prática utilizar o mesmo id em diversos arquivos 
XML, repare que o id tNome também existe no arquivo de layout XML do 
cadastro. Um erro comum é importar o arquivo errado, o qual não está sendo 
utilizado nesta tela. 
Esse código é simples e está recuperando a mesma intent que é enviada 
pela activity. Na segunda linha, a variável nome é lida dos parâmetros que foram 
enviados. E na terceira linha, o texto do TextView é atualizado com o nome 
enviado como parâmetro. Esperamos que esse exemplo tenha sido simples de 
entender. Se você alterar o código da classe LoginService, poderá criar várias 
combinações de login e senha para testar e brincar. 
3.1 Passando um objeto como parâmetro 
No exemplo anterior, vimos como passar parâmetros utilizando o objeto 
bundle, mas o código que fizemos tem um problema, pois passamos a variável 
 
 
"nome" isoladamente, e se for necessário passar outros dados do usuário, como 
o email, teremos que criar outra linha para este parâmetro, por exemplo: 
val args = Bundle() 
args.putString("nome", user.nome) 
args.putString("email", user.email) 
Isso não é uma boa prática em termos de manutenção de código, pois se 
tivermos muitos parâmetros, teremos que adicionar várias linhas e isso pode até 
resultar em algum erro de lógica. Então, como podemos resolver esse problema? 
É muito simples, o correto é passar o objeto Usuário completo como parâmetro. 
Para fazer isso, vamos utilizar o método putSerializable(s) conforme mostrado 
a seguir: 
// Abre a tela da home 
val intent = Intent(this,HomeActivity::class.java) 
val args = Bundle() 
args.putSerializable("usuario", user) 
intent.putExtras(args) 
startActivity(intent) 
Ao fazer essa alteração, o código não vai compilar, portanto, mantenha a 
calma. Provavelmente, aquela variável 'user' ficou vermelha, e se você passar o 
mouse em cima dela, o Android Studio vai dizer que ela não é do tipo 
Serializable. 
Este Serializable é uma interface de marcação do Java que indica que 
um objeto pode ser serializado, ou seja, pode ser convertido de objeto para bytes 
e vice-versa. A boa notícia é que essa interface não possui nenhum método para 
implementar, e justamente por isso falamos que ela é uma interface de 
marcação, pois basta implementá-la para “marcar” esse objeto como Serializable 
e pronto, o resto o Java faz sozinho. Talvez você possa achar estranho falar de 
Java, mas o fato é que o Kotlin consegue chamar todos as classes e códigos do 
Java, e vale lembrar também que todo o SDK do Android é feito em Java 
internamente. Essa interoperabilidade entre o Kotlin e o Java é um dos grandes 
pontos fortes do Kotlin, pois ele traz mais produtividade no desenvolvimento, e 
ainda consegue chamar classes do Java. 
package com.example.helloandroid.domain 
 
import java.io.Serializable 
 
 
 
data class Usuario ( 
 var nome: String, 
 var email: String 
) : Serializable 
Note que, como estamos usando uma Data Class, entre parênteses está 
o construtor do objeto e lá ficam todos os atributos da classe. Depois dos 
parênteses, foi colocada a sintaxe para implementar uma interface. 
Com a classe Usuário pronta, o código agora está compilando 
normalmente e já podemos alterar a classe HomeActivity para ler o objeto 
enviado como parâmetro: 
package com.example.helloandroid 
 
import androidx.appcompat.app.AppCompatActivity 
import android.os.Bundle 
import com.example.helloandroid.domain.Usuario 
import kotlinx.android.synthetic.main.activity_home.* 
 
class HomeActivity : AppCompatActivity() { 
 override fun onCreate(savedInstanceState: Bundle?) { 
 super.onCreate(savedInstanceState) 
 setContentView(R.layout.activity_home) 
 
 val args = intent.extras 
 val user = args?.getSerializable("usuario") as Usuario 
 tNome.text = user.nome 
 } 
} 
Tenha atenção ao alterar o código e observe que estamos usando o 
método getSerializable(). Como esse método retorna um objeto do tipo 
Serializable, fizemos o cast para Usuário conforme estudamos nas aulas de 
Kotlin. 
Como agora a variável “user” é do tipo Usuário, podemos acessar seus 
atributos como user.nome e user.email tranquilamente. Se quiser brincar, altere 
a linha que configura o texto do TextView para mostrar o nome e e-mail: 
tNome.text = "${user.nome} - ${user.email}" 
Esperamos que tenha ficado clara a vantagem de passar um objeto inteiro 
como parâmetro, ao contrário de passar apenas uma simples variável. 
 
 
 
TEMA 4 – INTENTS E PERMISSÕES 
Uma Intent é o coração do Android e está presente em todos os lugares, 
ela representa uma mensagem do aplicativo para o sistema operacional, 
solicitando que algo seja realizado. Portanto, uma intent refere-se à intenção de 
realizar uma operação, como, por exemplo, abrir uma tela do aplicativo. Embora 
na maioria das vezes usamos intents para esses objetivos mais simples, ela é 
muito mais que isso. Com uma intent, podemos abrir telas de outros aplicativos 
como o de um email, SMS, mapas etc. 
Uma intent também é usada pelo sistema operacional para nos notificar 
de que algo aconteceu. Por exemplo, ao receber uma mensagem de push 
(aquelas notificações), podemos interceptar essa mensagem e fazer alguma 
tarefa. Isso tudo são casos mais avançados e que você vai aprender com o 
tempo se mergulhar a fundo no mundo do desenvolvimento para Android. 
Nos próximos exemplos, vou mostrar códigos de como criar algumas 
intents. Mas antes, vamos criar um novo projeto, assim já aproveitamos para 
revisar isso. Crie o projeto pelo Android Studio da mesma forma que mostrado 
na primeira aula com o wizard > File > New Project e escolha o template Empty 
Activity. No nome do projeto, digite HelloIntents e o resto deixe com os valores 
padrão. 
No layout XML da activity do arquivo activity_main.xml, deixe o seguinte 
layout, com apenas um botão para testarmos as intents. 
<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout 
xmlns:android="http://schemas.android.com/apk/res/android" 
 android:layout_width="match_parent" 
 android:layout_height="match_parent" 
 android:gravity="center"> 
 <Button 
 android:id="@+id/btTeste" 
 android:layout_width="wrap_content" 
 android:layout_height="wrap_content" 
 android:text="Teste" 
 android:textSize="22sp" /> 
</LinearLayout> 
Observe que foi dado o id btTeste para o botão, assim, podemos usá-lo 
para adicionar um evento e chamar as intents que vamos brincar. 
package com.example.helloligacao 
 
 
 
import android.os.Bundle 
import androidx.appcompat.app.AppCompatActivity 
import kotlinx.android.synthetic.main.activity_main.* 
 
class MainActivity : AppCompatActivity() { 
 override fun onCreate(savedInstanceState: Bundle?) { 
 super.onCreate(savedInstanceState) 
 setContentView(R.layout.activity_main) 
 
 btTeste.setOnClickListener { 
 // Código aqui 
 } 
 } 
} 
Pronto! A estrutura de código para os próximos exemplos está criada, 
então, vamos continuar. 
4.1 Intents simples 
A primeira intent que vamos estudar é uma chamada simples que vai abrir 
o browser, basta fazer a seguinte alteração no código: 
package com.example.helloligacao 
 
import android.content.Intent 
import android.net.Uri 
import android.os.Bundle 
import androidx.appcompat.app.AppCompatActivity 
import kotlinx.android.synthetic.main.activity_main.* 
 
class MainActivity : AppCompatActivity() { 
 override fun onCreate(savedInstanceState: Bundle?) { 
 super.onCreate(savedInstanceState)setContentView(R.layout.activity_main) 
 
 btTeste.setOnClickListener { 
 val intent = Intent(Intent.ACTION_VIEW, 
Uri.parse("https://google.com")) 
 startActivity(intent) 
 } 
 } 
} 
Feito isso, execute o projeto no emulador. Ao clicar no botão, o browser 
será aberto na página do Google, como mostrado na próxima figura. 
 
 
 
 
Figura 4 – Browser 
 
Crédito: Khuruzero/Shutterstock. 
No próximo exemplo, vamos passar como parâmetro a localização da 
Ópera de Arame em Curitiba. Note que passamos os parâmetros com a URI 
geo: seguida da lat/lng e do zoom para o Mapa que neste caso é 15. 
Altere o código que chama a intent para ficar assim: 
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("geo://@-
25.3848941,-49.2763565,15z")) 
startActivity(intent) 
Desta vez, o aplicativo do Google Maps será chamado e mostrará a 
localização desejada. 
Figura 5 – Google Maps 
 
Crédito: Khuruzero/Shutterstock. 
 
 
Até o momento, estudamos dois exemplos de chamadas de intents que 
abrem o browser e o mapa, e vimos o quão útil podem ser essas chamadas. 
Saiba mais 
Existem várias outras chamadas de intents que podemos fazer e 
recomendamos que você leia a documentação oficial sobre Intents Comuns, que 
são essas chamadas simples que abrem outros aplicativos. Veja mais no link a 
seguir: <https://developer.android.com/guide/components/intents-
common?hl=pt-br>. 
4.2 Intents seguras e permissões 
Agora vamos mostrar um exemplo mais complexo e já adiantamos que 
ele não vai funcionar de primeira e o aplicativo vai travar. Vamos tentar fazer 
uma ligação telefônica usando a URI 'tel:número': 
Altere o código que chama a intent mais uma vez: 
val intent = Intent(Intent.ACTION_CALL, 
Uri.parse("tel:987654321")) 
startActivity(intent) 
Ao chamar essa intent, o aplicativo vai travar conforme o esperado e 
teremos esta exceção no LogCat. Tente ler a mensagem passo a passo e com 
atenção: 
FATAL EXCEPTION: main 
 Process: com.example.helloandroid, PID: 7822 
 java.lang.SecurityException: Permission Denial: starting Intent { 
act=android.intent.action.CALL dat=tel:xxxxxxxxx 
Podemos ver pela mensagem que a permissão foi negada pelo sistema 
(Permission Denial). Isso aconteceu porque uma ligação é restrita pelo fato de 
que ela pode gerar custos para o usuário. Sendo assim, antes de fazer uma 
ligação, precisamos pedir permissão para o usuário com um alerta com este: 
 
 
 
 
 
 
Figura 6 – Alerta 
 
Crédito: Khuruzero/Shutterstock. 
Antes de aprendermos a fazer isso no código, vamos estudar alguns 
conceitos sobre permissões. Primeiramente, vá até a tela de configurações do 
aplicativo HelloIntents que acabamos de criar. Nas últimas versões do Android, 
isso pode ser feito clicando e segurando no ícone do aplicativo por 2 segundos, 
depois selecione a opção App Info. Na tela de configurações, veja que esse 
aplicativo não requisitou nenhuma permissão para o usuário. 
Figura 7 – Permissões 
 
Crédito: Khuruzero/Shutterstock. 
 
 
Sendo assim, o que precisamos fazer é configurar o aplicativo para 
solicitar esta permissão. A primeira tarefa a fazer é adicionar a tag <uses-
permission> no arquivo de manifesto. Essa tag define as permissões 
necessárias para o aplicativo, ou seja, todos aqueles alertas que temos que 
mostrar ao usuário para ele autorizar o uso de chamadas restritas. Tenha 
atenção, pois a configuração fica antes da tag <application>. 
● AndroidManifest.xml 
<?xml version="1.0" encoding="utf-8"?> 
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
 package="com.example.helloligacao"> 
 
 <uses-permission android:name="android.permission.CALL_PHONE" /> 
 
 <application . . .> 
 . . . 
 </application> 
 
</manifest> 
Pronto, feito isso, execute o projeto novamente no emulador. 
O resultado é que o aplicativo vai continuar travando. Mas a boa notícia é 
que, ao voltar na tela de configuração do aplicativo, você verá que desta vez 
existe uma permissão que ainda não foi autorizada, conforme podemos verificar 
na próxima figura. 
Figura 8 – Permissão não autorizada 
 
Crédito: Khuruzero/Shutterstock. 
 
 
Para fazermos o primeiro teste e validar a intent que faz a ligação, clique 
na permissão de telefone (Phone) e depois clique em Allow para aceitar essa 
permissão. 
Figura 9 – Aceitar permissão 
 
Crédito: Khuruzero/Shutterstock. 
Muito bem. Acabamos de fazer um artifício técnico, conhecido 
popularmente como gambiarra, para conceder uma permissão para o aplicativo. 
Isso significa que já podemos fazer a ligação utilizando aquela intent. 
Desta vez, ao clicar no botão que chama a intent, o aplicativo que faz as 
chamadas telefônicas será aberto e vai iniciar a ligação. Legal, não é mesmo? 
Figura 10 – Ligação 
 
Crédito: Khuruzero/Shutterstock. 
 
 
4.3 Solicitando as permissões pelo código 
O exemplo da ligação funcionou e foi importante para entendermos o que 
é uma permissão, mas é claro que o correto é solicitar as permissões dentro do 
aplicativo, e é exatamente isso que vamos fazer no próximo exemplo. 
Para continuar, entre na tela das permissões novamente e, desta vez, 
clique em Deny para negar a permissão e voltar ao estado inicial do aplicativo. 
Figura 11 – Negar permissão 
 
Crédito: Khuruzero/Shutterstock. 
Feito isso, crie a classe PermissionUtils no projeto com o seguinte 
código. Veja que a classe foi anotada com a palavra reservada object, portanto, 
ela é um singleton e pode ser utilizada com a notação Classe.método(), 
conforme estudamos na aula de Kotlin. 
package com.example.helloligacao 
 
import android.app.Activity 
import android.content.Context 
import android.content.pm.PackageManager 
import androidx.core.app.ActivityCompat 
import androidx.core.content.ContextCompat 
 
object PermissionUtils { 
 // Solicita uma permissão se ainda não foi concedida 
 fun request(activity: Activity, permissions: Array<String>) : 
Boolean { 
 val ok = validate(activity, permissions) 
 if (!ok) { 
 ActivityCompat.requestPermissions( 
 activity, 
 permissions, 
 1 
 ) 
 
 
 } 
 return ok 
 } 
 // Valida se as permissões foram concedidas 
 fun validate(context: Context, permissions: Array<String>): 
Boolean { 
 for (permission in permissions) { 
 val result = ContextCompat.checkSelfPermission(context, 
permission) 
 if (result != PackageManager.PERMISSION_GRANTED) { 
 return false 
 } 
 } 
 return true 
 } 
} 
Com o tempo, você vai estudar e aprender mais sobre o código contido 
nessa classe. Por enquanto, vamos aprender a utilizá-la, sendo assim, faça as 
seguintes alterações no código e leia os comentários com atenção, pois as 
explicações estão no código. 
package com.example.helloligacao 
 
import android.Manifest 
import android.content.Intent 
import android.net.Uri 
import android.os.Bundle 
import android.view.View 
import androidx.appcompat.app.AppCompatActivity 
import kotlinx.android.synthetic.main.activity_main.* 
 
class MainActivity : AppCompatActivity() { 
 override fun onCreate(savedInstanceState: Bundle?) { 
 super.onCreate(savedInstanceState) 
 setContentView(R.layout.activity_main) 
 
 btTeste.setOnClickListener { 
 val intent = Intent(Intent.ACTION_CALL, 
Uri.parse("tel:987654321")) 
 startActivity(intent) 
 } 
 
 // (1): Esse método valida se a permissão já foi concedida. 
 // Caso o usuário já tenha aceitado, ele retorna true. 
 // Caso contrário, retorna false e mostra o alerta ao usuário. 
 val ok = PermissionUtils.request(this, 
arrayOf(Manifest.permission.CALL_PHONE)) 
 
 if(!ok) { 
 // Se o usuário ainda não aceitou a permissão ou se ele 
foi negada... 
 // Deixa o botão Invisível 
 btTeste.visibility = View.INVISIBLE} 
 } 
 
 override fun onRequestPermissionsResult( 
 
 
 requestCode: Int, 
 permissions: Array<String>, 
 grantResults: IntArray 
 ) { 
 super.onRequestPermissionsResult(requestCode, permissions, 
grantResults) 
 val ok = PermissionUtils.validate(this, permissions) 
 if(ok) { 
 // Deixa o botão visível se o usuário aceitou a permissão 
 btTeste.visibility = View.VISIBLE 
 } 
 } 
} 
O objetivo deste código é mostrar o alerta de permissão para o usuário. 
Isso é feito nesta linha: 
val ok = PermissionUtils.request(this, 
arrayOf(Manifest.permission.CALL_PHONE)) 
Caso o método retorne false, sabemos que o alerta foi mostrado ao 
usuário e o aplicativo está aguardando se ele vai aceitar ou não a permissão. 
Por isso, nesse momento, o botão é deixado invisível, pois não podemos deixar 
o usuário clicar nele antes de aceitar a permissão. 
btTeste.visibility = View.INVISIBLE 
 
Figura 12 – Permissão 
 
Crédito: Khuruzero/Shutterstock. 
 
 
Quando o usuário responder, o método onRequestPermissionsResult() 
é chamado para validar as permissões, algo que estamos fazendo com esta linha 
de código: 
val ok = PermissionUtils.validate(this, permissions) 
E por fim, caso o usuário tenha aceitado a permissão, o botão é deixado 
visível novamente, o que significa que o usuário autorizou este aplicativo a fazer 
ligações telefônicas. 
btTeste.visibility = View.VISIBLE 
Conforme vimos, controlar as permissões do aplicativo não é uma tarefa 
muito simples, é um trabalho bem delicado e que exige um certo refinamento. 
Neste exemplo simples, o botão apenas foi deixado invisível, mas dependendo 
do caso, o tratamento poderia ser mostrar outro layout no lugar do botão. O 
interessante é que aprendemos a usar a propriedade visibility da View, e com 
ela, podemos informar se uma view está visível ou invisível. 
Como desenvolvedor, você precisa trabalhar a interface do aplicativo para 
não permitir que o usuário chame alguma funcionalidade restrita se ele ainda 
não aprovou a permissão, pois se o fizer, o Android lançará uma exceção. 
Caso tenha um celular Android em mãos, uma sugestão é você entrar na 
tela de permissões do WhatsApp e depois negar todas as permissões. Ao voltar 
ao WhatsApp, tente chamar alguma funcionalidade restrita como gravar um 
áudio ou tirar uma foto. Você verá que o WhatsApp mostra uns alertas elegantes 
para o usuário, ou seja, ele faz o controle se as permissões estão aceitas e as 
trata devidamente. 
TEMA 5 – TIRANDO UMA FOTO 
Para finalizar nossos estudos sobre uma intent, vamos fechar com chave 
de ouro e tirar uma foto com a câmera pelo aplicativo. Da mesma maneira que 
abrimos o browser e o Google Maps e depois também fizemos uma ligação, 
podemos usar uma intent para abrir o aplicativo da câmera. A diferença é que o 
aplicativo da câmera vai nos retornar a foto que foi tirada pelo usuário, portanto, 
temos que aguardar esse retorno. 
 
 
Atenção: este exemplo é mais complexo e vai exigir mais de você. Nele, 
vamos revisar quase tudo o que já aprendemos, portanto, leve o tempo que 
precisar para estudar o código com calma. 
Para esta brincadeira, vamos criar um novo projeto chamado 
HelloCamera, da mesma forma que fizemos nos exemplos anteriores. Altere o 
layout da activity para ter um botão na parte superior, e uma imagem logo abaixo. 
O objetivo é clicar o botão para tirar a foto e depois mostrá-la no ImageView. 
● activity_main.xml 
<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout 
xmlns:android="http://schemas.android.com/apk/res/android" 
 android:layout_width="match_parent" 
 android:layout_height="match_parent" 
 android:orientation="vertical" 
 android:padding="16dp"> 
 
 <ImageButton 
 android:id="@+id/btAbrirCamera" 
 android:layout_width="60dp" 
 android:layout_height="60dp" 
 android:layout_gravity="center" 
 android:src="@android:drawable/ic_menu_camera" 
 android:text="Abrir a Câmera" /> 
 
 <ImageView 
 android:id="@+id/imgFoto" 
 android:layout_width="match_parent" 
 android:layout_height="0dp" 
 android:layout_gravity="center" 
 android:layout_weight="1" /> 
</LinearLayout> 
O interessante é que no layout XML criamos um botão com o 
ImageButton, ou seja, é um botão que vai mostrar uma imagem de uma câmera. 
Para isso, usamos um recurso nativo do Android que já tem essa figura: 
@android:drawable/ic_menu_camera. Observe também que as duas views 
possuem ids, portanto, podemos acessá-las no código. 
Com o layout criado, vamos continuar. Primeiro, vamos digitar todos os 
arquivos necessários para o exemplo funcionar, e depois vamos estudar o 
código, combinado? 
A seguir, temos o código da activity que dispara a intent para abrir a 
câmera. O código está comentado, leia com atenção. 
 
 
 
● MainActivity.kt 
package com.example.hellocamera 
 
import android.Manifest 
import android.app.Activity 
import android.content.Intent 
import android.os.Bundle 
import android.os.Environment 
import android.provider.MediaStore 
import android.util.Log 
import android.view.View 
import androidx.appcompat.app.AppCompatActivity 
import androidx.core.content.FileProvider 
import kotlinx.android.synthetic.main.activity_main.* 
import java.io.File 
 
class MainActivity : AppCompatActivity() { 
 // Caminho para salvar o arquivo 
 var file: File? = null 
 
 public override fun onCreate(savedInstanceState: Bundle?) { 
 super.onCreate(savedInstanceState) 
 setContentView(R.layout.activity_main) 
 
 btAbrirCamera.setOnClickListener { 
 onClickCamera() 
 } 
 if (savedInstanceState != null) { 
 // (*2*) Se girou a tela recupera o estado 
 file = savedInstanceState.getSerializable("file") 
as File 
 showImage(file) 
 } 
 
 // Valida a permissão para Câmera 
 val ok = PermissionUtils.request(this, 
arrayOf(Manifest.permission.CAMERA)) 
 if(!ok) { 
 // Deixa o botão Invisível 
 btAbrirCamera.visibility = View.INVISIBLE 
 } 
 } 
 
 // Cria um arquivo no sdcard privado do aplicativo 
 // A câmera vai salvar a foto no caminho deste arquivo 
 fun createFile(fileName: String): File { 
 val sdCardDir = 
getExternalFilesDir(Environment.DIRECTORY_PICTURES) 
 if (sdCardDir != null && sdCardDir.exists()) { 
 sdCardDir.mkdir() 
 } 
 val file = File(sdCardDir, fileName) 
 // Salva file como atributo para salvar estado caso 
gire a tela 
 this.file = file 
 return file 
 
 
 } 
 
 private fun onClickCamera() { 
 // (*1*) Cria o caminho do arquivo no sdcard 
 val file = createFile("foto.jpg") 
 log("Camera file: $file") 
 // Chama a intent informando o arquivo para salvar a 
foto 
 val i = Intent(MediaStore.ACTION_IMAGE_CAPTURE) 
 val uri = FileProvider.getUriForFile( 
 this, 
 applicationContext.packageName + ".provider", file 
 ) 
 i.putExtra(MediaStore.EXTRA_OUTPUT, uri) 
 // Chama a intent de captura de foto 
 startActivityForResult(i, 0) 
 } 
 
 override fun onActivityResult(requestCode: Int, resultCode: 
Int, data: Intent?) { 
 super.onActivityResult(requestCode, resultCode, data) 
 if (resultCode == Activity.RESULT_OK) { 
 // (*4*) Se a câmera retornou, vamos mostrar o 
arquivo da foto 
 showImage(file) 
 } 
 } 
 
 // Atualiza a imagem na tela 
 private fun showImage(file: File?) { 
 if (file != null && file.exists()) { 
 // (*5*) Redimensiona a imagem para o tamanho do 
ImageView 
 val bitmap = ImageUtils.resize(file, 1200, 800) 
 log("img1: w/h:" + imgFoto.width + "/" + 
imgFoto.height) 
 log("img2: w/h:" + bitmap.width + "/" + 
bitmap.height) 
 log("file:" + file) 
 // (*6*) Mostra a foto no ImageViewimgFoto.setImageBitmap(bitmap) 
 } 
 } 
 
 fun log(s: String) { 
 Log.d("android", s) 
 } 
 
 override fun onSaveInstanceState(outState: Bundle) { 
 super.onSaveInstanceState(outState) 
 // (*3*) Salvar o estado caso gire a tela 
 outState.putSerializable("file", file) 
 } 
 
 override fun onRequestPermissionsResult( 
 requestCode: Int, 
 permissions: Array<String>, 
 
 
 grantResults: IntArray 
 ) { 
 super.onRequestPermissionsResult(requestCode, 
permissions, grantResults) 
 val ok = PermissionUtils.validate(this, permissions) 
 if(ok) { 
 // Deixa o botão visível se o usuário aceitou a 
permissão 
 btAbrirCamera.visibility = View.VISIBLE 
 } 
 } 
} 
Como já estudamos o que é uma intent, permissões e o método 
onSaveInstanceState(bundle), a maior parte do código você deverá entender 
sem problemas. Observe que logo no início do código estamos solicitando a 
permissão para utilizar a câmera. Caso a permissão seja negada, o botão ficará 
invisível. 
val ok = PermissionUtils.request(this, 
arrayOf(Manifest.permission.CAMERA)) 
A classe PermissionUtils é exatamente a mesma que usamos no 
exemplo da ligação telefônica, é só copiar para este projeto. E para a permissão 
funcionar, adicione no arquivo de manifesto a seguinte configuração: 
● AndroidManifest.xml 
<?xml version="1.0" encoding="utf-8"?> 
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
 package="com.example.hellocamera"> 
 
 <uses-permission android:name="android.permission.CAMERA" /> 
 
 <application > 
 . . . 
 </application> 
 
</manifest> 
Mas para o código compilar, ainda precisamos criar o arquivo ImageUtils, 
que vai fazer o dimensionamento da foto antes de mostrar no ImageView, pois 
as câmeras, atualmente, tiram fotos com muitos pixels, e para economizar 
memória, os aplicativos fazem este redimensionamento. 
 
 
 O código desta classe está com comentários em inglês, pois foi tirado da 
documentação oficial1. 
● ImageUtils.kt 
package com.example.hellocamera 
 
import android.graphics.Bitmap 
import android.graphics.BitmapFactory 
import java.io.File 
 
object ImageUtils { 
 // Faz o resize da imagem 
 fun resize(file: File, reqWidth: Int, reqHeight: Int): 
Bitmap { 
 // Verifica as dimensões do arquivo 
 val options = BitmapFactory.Options() 
 options.inJustDecodeBounds = true 
 BitmapFactory.decodeFile(file.absolutePath, options) 
 // Calculate inSampleSize 
 options.inSampleSize = calculateInSampleSize(options, 
reqWidth, reqHeight) 
 // Decode bitmap with inSampleSize set 
 options.inJustDecodeBounds = false 
 return BitmapFactory.decodeFile(file.absolutePath, 
options) 
 } 
 // Calcula o fator de escala 
 fun calculateInSampleSize(options: BitmapFactory.Options, 
reqWidth: Int, reqHeight: Int): 
 Int { 
 // Raw height and width of image 
 val height = options.outHeight 
 val width = options.outWidth 
 var inSampleSize = 1 
 if (height > reqHeight || width > reqWidth) { 
 val halfHeight = height / 2 
 val halfWidth = width / 2 
 while 
 (halfHeight / inSampleSize >= reqHeight && 
halfWidth / inSampleSize >= reqWidth) { 
 inSampleSize *= 2 
 } 
 } 
 return inSampleSize 
 } 
} 
Pronto, estamos quase terminando, falta apenas uma configuração. Saiba 
que, quando chamarmos a intent para abrir a câmera, vamos passar como 
 
1 Disponível em: <https://developer.android.com/training/camera/photobasics>. Acesso em: 22 
jan. 2021. 
https://developer.android.com/training/camera/photobasics
 
 
parâmetro o caminho de um arquivo para que a foto seja salva. Esse arquivo 
ficará numa pasta privada do nosso aplicativo: 
/storage/emulated/0/Android/data/com.example.hellocamera/files/Pictur
es/foto.jpg 
Mas, por questões de segurança, a partir do Android 7 (Nougat), o Android 
não permite que a câmera ou qualquer outro aplicativo salve arquivos nessa 
pasta sem darmos acesso. Portanto, as configurações que vamos fazer agora é 
dar acesso ao aplicativo da câmera para gravar arquivos na pasta privada do 
nosso aplicativo. 
Para isso, crie uma pasta /res/xml conforme indicado na figura e o arquivo 
provider_paths.xml. 
● /res/xml/provider_paths.xml 
<?xml version="1.0" encoding="utf-8"?> 
<paths> 
 <external-path name="external_files" path="."/> 
</paths> 
Figura 13 – Pastas 
 
Por último, temos que criar esta configuração no arquivo de manifesto. 
 
 
 
 
● AndroidManifest.xml 
<?xml version="1.0" encoding="utf-8"?> 
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
 package="com.example.hellocamera"> 
 
 <uses-permission android:name="android.permission.CAMERA" /> 
 
 <application . . . > 
 <activity android:name=".MainActivity" . . . > 
 
 <!-- Permite a Câmera salvar fotos no nosso app --> 
 <provider 
 android:name="androidx.core.content.FileProvider" 
 android:authorities="${applicationId}.provider" 
 android:exported="false" 
 android:grantUriPermissions="true"> 
 <meta-data 
 android:name="android.support.FILE_PROVIDER_PATHS" 
 android:resource="@xml/provider_paths" /> 
 </provider> 
 
 </application> 
</manifest> 
 
Pronto, terminamos! Vamos ver o projeto funcionando? 
Ao executar o projeto no emulador, o primeiro passo é solicitar ao usuário 
a permissão para utilizar a câmera. 
Figura 14 – Permissão 
 
Crédito: khuruzero/Shutterstock. 
 
 
Essa parte do código é exatamente igual ao exemplo anterior que fizemos 
e não iremos explicá-la, portanto, revise o código com atenção. 
Ao clicar no botão para tirar uma foto, o método onClickCamera() é 
chamado. Nele, veja que criamos o arquivo no qual queremos que a foto seja 
salva. Logo depois, é criada uma intent com ação 
MediaStore.ACTION_IMAGE_CAPTURE, que será interceptada pela câmera. 
Você verá no código que ao criar a intente, é usado o parâmetro 
MediaStore.EXTRA_OUTPUT para informar o caminho do arquivo que a 
câmera vai receber como argumento. 
Feito isso, a câmera será chamada e você poderá tirar uma foto, conforme 
mostra a figura. Observação: o emulador possui um bug em que, na primeira 
vez, a foto não funciona. Se isso acontecer, clique no botão voltar para fechar a 
câmera e tente novamente. 
Figura 15 – Câmera 
 
Crédito: khuruzero/Shutterstock. 
Depois de tirar a foto, clique no botão de confirmar dentro do aplicativo da 
câmera. Isso vai fechar a câmera e devolver o arquivo para nosso aplicativo. O 
resultado é que o método onActivityResult() será chamado. Ele é um método 
que é chamado quando um aplicativo retorna dados para outro. Nós não 
havíamos comentado, mas veja que, ao chamar a intent da câmera, foi utilizado 
o método startActivityForResult() justamente por isso. Veja no método 
onClickCamera(). 
 
 
O método onActivityResult() contém a lógica necessária para mostrar a 
foto no ImageView. Veja o código do método showImage(file). Nele, estamos 
chamando a classe ImageUtils para fazer o redimensionamento da foto, 
conforme já explicado. O resultado depois de tirar a foto podemos ver na figura 
a seguir: 
Figura 16 – Foto 
 
Crédito: khuruzero/Shutterstock. 
Por último, faça um teste e gire a tela do emulador para horizontal. O 
resultado é que a foto é salva e não perdemos o estado do aplicativo. Isso só foi 
possível porque o método onSaveInstanceState(bundle) foi implementado, 
conforme já estudamos. Nele, salvamos o arquivo "file" e, depois, quando o 
Android recriou a activity, lá no método onCreate(bundle), esse arquivo foi 
recuperado e mostrado automaticamente na tela. 
Figura 17 – Tela na horizontal 
 
Crédito: khuruzero/Shutterstock. 
 
 
FINALIZANDO 
Nesta aula, estudamos o ciclo de vida de uma activitye depois 
aprendemos conceitos importantes sobre uma intent, a passagem de parâmetros 
entre telas e o controle de permissões paras as intents restritas. Por fim, 
aprendemos a tirar uma foto com a câmera. 
 
 
 
REFERÊNCIAS 
ANDROID DEVELOPERS. Documentação oficial - Intents comuns. 
Disponível em: <https://developer.android.com/guide/components/intents-
common?hl=pt-br>. 
_____. Documentação oficial – Câmera. Disponível em: 
<https://developer.android.com/training/camera/photobasics>. 
_____. Android Training – Loading Large Bitmaps Efficiently. Disponível 
em: <https://developer.android.com/topic/performance/graphics/load-
bitmap.html>. 
LECHETA, R. Android Essencial com Kotlin. 2. ed. 2018.

Continue navegando