Android
Training
Program
PORTUGAL
Aula #4
Fundações III
Slide 2
Slide 2 text
● Sejam excelentes uns para os outros
● Fale mais alto se vir ou ouvir alguma coisa
● O assédio não é tolerado
● Pratique "Sim e" um ao outro
Código de conduta
Mais informações: http://bit.ly/2IhF0l3
Slide 3
Slide 3 text
Andres-Leonardo
Martinez-Ortiz
Google
Carlos Mota
Formador
Renato Almeida
Formador
@davilagrau @cafonsomota @tallnato
Equipa
Daniela Ferreira
Gestora de
comunidades
Slide 4
Slide 4 text
● 12 aulas
● 1h30 cada aula
● ~1 aula por semana
● 14 Outubro a 16 Dezembro
● YouTube live
● Suporte assíncrono contínuo via Discord/email
● Todo o código disponível no GitHub
Photo by Arif Riyanto on Unspla
O programa
Slide 5
Slide 5 text
#0
14 de Outubro
Pronto para
começar
#1
21 de Outubro
Bem-vindos ao
Android
#2
28 de Outubro
Fundações I
#3
04 de Novembro
Fundações II
#4
11 de Novembro
Fundações III
#5
18 de Novembro
Listas, listas e
mais listas
#6
25 de Novembro
Jetpack,
Jetpack,
Jetpack!
#7 - #8
02 - 03 de Dezembro
Firebase
#9 - #10
09 - 10 de Dezembro
MLKit &
TensorFlow
#11
16 de Dezembro
Resumo
Semana Semana
Calendário
✅ ✅ ✅
Direto
✅
Slide 6
Slide 6 text
Photo by Mollie Sivaram on Unsplash
Slide 7
Slide 7 text
Photo by Mollie Sivaram on Unsplash
~5000 participantes
~2600 subscrições no YouTube
~20000 visualizações
Obrigado! ♀
Slide 8
Slide 8 text
Sumário
Photo by Mika Baumeister on Unsplash
● Resumo da aula anterior
● ViewModel
● LiveData
● Operações assíncronas
● Permissões
● Kotlin para principiantes
● Castanhas quentes e boas!
Resolução
medium resolution
(mdpi)
HTC Wildfire S
high resolution
(hdpi)
Samsung Galaxy S2
extra extra extra high resolution
(xxxhdpi)
Pixel 5
Slide 17
Slide 17 text
+351990000001
Slide 18
Slide 18 text
Trabalho para casa
● Melhora a UI do ecrã inicial
● Adicionar uma ScrollView
● Enviar o nome de utilizador para a Activity principal
● Utilizar o botão de ação do teclado para validar
Slide 19
Slide 19 text
Aula #4
Slide 20
Slide 20 text
e… o que acontece quando
rodamos o ecrã?
Slide 21
Slide 21 text
e… o que acontece quando
rodamos o ecrã?
Slide 22
Slide 22 text
No content
Slide 23
Slide 23 text
No content
Slide 24
Slide 24 text
Architecture
components
Slide 25
Slide 25 text
LiveData
ViewModel
Room
Slide 26
Slide 26 text
ViewModel
Slide 27
Slide 27 text
ViewModel
A classe ViewModel foi desenhada para guardar e gerir dados relacionados com
a interface do utilizador de forma consciente em relação ao ciclo de vida.
Esta classe permite que os dados continuem disponíveis mesmo quando as
configurações são alteradas. Por exemplo, quando o ecrã é rodado e a activity
é reconstruída.
- Android Developers documentation
Slide 28
Slide 28 text
● Permite separar os dados da sua representação gráfica
● Está associado a uma Activity ou Fragment
● Continua a ser executado mesmo que a aplicação não esteja visível
● Permanece em memória quando uma Activity é reconstruída
● Não deve aceder à interface gráfica da aplicação
ViewModel
dependencies {
...
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
}
Como utilizar?
Importar a biblioteca
app/build.gradle
Slide 31
Slide 31 text
class CounterViewModel : ViewModel() {
}
Como utilizar?
Implementar numa class
CounterViewModel.kt
Slide 32
Slide 32 text
class CounterViewModel : ViewModel() {
var number: Int = 0
private set
fun increment() {
++number
}
}
Como utilizar?
Lógica no ViewModel
CounterViewModel.kt
Slide 33
Slide 33 text
class CounterViewModel : ViewModel() {
var number: Int = 0
private set
fun increment() {
++number
}
}
Como utilizar?
Lógica no ViewModel
não é possível alterar o
valor de number, fora desta
class
CounterViewModel.kt
Slide 34
Slide 34 text
class CounterActivity : AppCompatActivity() {
lateinit var viewModel : CounterViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_counter)
viewModel = ViewModelProvider(this).get(CounterViewModel::class.java)
}
}
Como utilizar?
Instanciar numa Activity
CounterActivity.kt
Slide 35
Slide 35 text
class CounterActivity : AppCompatActivity() {
lateinit var viewModel : CounterViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_counter)
viewModel = ViewModelProvider(this).get(CounterViewModel::class.java)
}
}
Como utilizar?
Instanciar numa Activity
CounterActivity.kt
Slide 36
Slide 36 text
class CounterActivity : AppCompatActivity() {
...
override fun onCreate(savedInstanceState: Bundle?) {
...
findViewById(R.id.btn_counter).setOnClickListener {
viewModel.increment()
Toast.makeText(this, "Number: ${viewModel.number}", LENGTH_SHORT).show()
}
}
}
Como utilizar?
Lógica na Activity
CounterActivity.kt
Slide 37
Slide 37 text
No content
Slide 38
Slide 38 text
class CounterActivity : AppCompatActivity() {
...
override fun onCreate(savedInstanceState: Bundle?) {
...
val tvCounter = findViewById(R.id.tv_counter)
findViewById(R.id.btn_counter).setOnClickListener {
viewModel.increment()
tvCounter.text = viewModel.number.toString()
}
}
}
CounterActivity.kt
Como utilizar?
Actualizar uma TextView
Slide 39
Slide 39 text
No content
Slide 40
Slide 40 text
LiveData
Slide 41
Slide 41 text
class CounterViewModel : ViewModel() {
var number: Int = 0
private set
fun increment() {
++number
lotsOfProcessings(number)
}
...
}
Bué processamento
Slide 42
Slide 42 text
No content
Slide 43
Slide 43 text
No content
Slide 44
Slide 44 text
● Podemos ter um ViewModel partilhado,
○ Permite comunicar entre uma Activity e um Fragment
○ Permite comunicar entre vários Fragment’s
E ainda...
Slide 45
Slide 45 text
LiveData
Slide 46
Slide 46 text
LiveData
O LiveData é uma classe que contém dados
observáveis. Ao contrário de uma classe observável
normal, o LiveData tem em consideração o ciclo de
vida, ou seja, respeita o ciclo dos componentes como
Activities, Fragments ou Service’s. Esta
consideração assegura que o LiveData só atualiza o
componente que estão num estado ativo.
- Android Developers documentation
Slide 47
Slide 47 text
LiveData
O LiveData é uma classe que contém dados
observáveis. Ao contrário de uma classe observável
normal, o LiveData tem em consideração o ciclo de
vida, ou seja, respeita o ciclo dos componentes como
Activities, Fragments ou Service’s. Esta
consideração assegura que o LiveData só atualiza o
componente que estão num estado ativo.
- Android Developers documentation
Slide 48
Slide 48 text
● Padrão de desenvolvimento de software
● Um objeto (Observable) notifica os interessados (Observers)
○ Quando este valor é alterado
● Os interessados subscrevem para receber as atualizações
Observável?
Observable
Observer
Observer
Observer
...
Slide 49
Slide 49 text
● Assegura que a interface gráfica corresponde ao estado dos dados
● Não tem memory leaks
● Sem crashes por causa de Activities paradas
● Sem manipulação manual do ciclo de vida
● Sempre atualizado
● Partilha de recursos
Vantagens do LiveData
Slide 50
Slide 50 text
dependencies {
...
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0"
}
Como utilizar?
Importar a biblioteca
app/build.gradle
Slide 51
Slide 51 text
class CounterViewModel : ViewModel() {
var number: Int = 0
private set
fun increment() {
++number
}
}
Como utilizar?
Slide 52
Slide 52 text
class CounterViewModel : ViewModel() {
private val _liveNumber = MutableLiveData()
private var number: Int = 0
val liveNumber : LiveData = _liveNumber
fun increment() {
++number
_liveNumber.postValue(number)
}
}
Como utilizar?
No ViewModel
Slide 53
Slide 53 text
class CounterViewModel : ViewModel() {
private val _liveNumber = MutableLiveData()
private var number: Int = 0
val liveNumber : LiveData = _liveNumber
fun increment() {
++number
_liveNumber.postValue(number)
}
}
Como utilizar?
No ViewModel
Slide 54
Slide 54 text
class CounterViewModel : ViewModel() {
private val _liveNumber = MutableLiveData()
private var number: Int = 0
val liveNumber : LiveData = _liveNumber
fun increment() {
++number
_liveNumber.postValue(number)
}
}
Como utilizar?
No ViewModel
Slide 55
Slide 55 text
class CounterActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
...
viewModel = ViewModelProvider(this).get(CounterViewModel::class.java)
val tvCounter = findViewById(R.id.tv_counter)
findViewById(R.id.btn_counter).setOnClickListener {
viewModel.increment()
}
viewModel.liveNumber.observe(this){ number ->
tvCounter.text = number.toString()
}
}
}
Como utilizar?
Na Activity
Slide 56
Slide 56 text
class CounterActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
...
viewModel = ViewModelProvider(this).get(CounterViewModel::class.java)
val tvCounter = findViewById(R.id.tv_counter)
findViewById(R.id.btn_counter).setOnClickListener {
viewModel.increment()
}
viewModel.liveNumber.observe(this){ number ->
tvCounter.text = number.toString()
}
}
}
Como utilizar?
Na Activity
Slide 57
Slide 57 text
class CounterActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
...
viewModel = ViewModelProvider(this).get(CounterViewModel::class.java)
val tvCounter = findViewById(R.id.tv_counter)
findViewById(R.id.btn_counter).setOnClickListener {
viewModel.increment()
}
viewModel.liveNumber.observe(this){ number ->
tvCounter.text = number.toString()
}
}
}
Como utilizar?
Na Activity
Slide 58
Slide 58 text
LiveData
Slide 59
Slide 59 text
● LiveData
○ É uma classe abstrata, não permite atualizar o valor
● MutableLiveData
○ Classe que permite alterar o valor
● Transformations.map
○ Permite transformar o item
● MediatorLiveData
○ Permite juntar dois LiveData
● Transformations.SwitchMap
○ Utiliza o valor de um LiveData para produzir outro
Tipos de LiveData
Slide 60
Slide 60 text
Operações
assíncronas
Slide 61
Slide 61 text
atualizar atualizar atualizar atualizar
desenha o ecrã desenha o ecrã desenha o ecrã
objetivo: 60fps
UI-thread
Operações assíncronas
Slide 62
Slide 62 text
atualizar operação pesada atualizar
desenha o ecrã desenha o ecrã desenha o ecrã
objetivo: 60fps
UI-thread
Operações assíncronas
Slide 63
Slide 63 text
● A aplicação não é fluida
○ Alguns frames podem não ser desenhados
● ANR podem ser lançados pelo sistema
○ ANR = Activity Not Responding
● Má experiência para o utilizador
Operações assíncronas
Counter isn’t responding
Slide 64
Slide 64 text
atualizar atualizar
desenha o ecrã desenha o ecrã desenha o ecrã
objetivo: 60fps
operação pesada
atualizar atualizar
thread secundária
Operações assíncronas
thread de UI
Slide 65
Slide 65 text
atualizar atualizar
desenha o ecrã desenha o ecrã desenha o ecrã
objetivo: 60fps
thread de UI
operação pesada
atualizar atualizar
Operações assíncronas
progresso
thread secundária
Slide 66
Slide 66 text
● A aplicação fica muito mais fluida
○ Não há perda de frames
● Operações pesadas feitas numa thread secundária
● Atualizações de UI são feitas na thread de UI
○ UI = User Interface
● O ecrã é atualizado cada 16 ms
Operações assíncronas
Operações assíncronas
Soluções
executam num
momento exato
downloads via
HTTP
requer a
atenção do
utilizador
resultam de
eventos do
sistema
DownloadManager
ForegroundService
WorkManager
AlarmManager
descarregar os
materiais desta
aula
atualizar a caixa de
entrada de emails
ligar a uma rede
WiFi
o alarme a tocar
que já são horas de
acordar
Slide 71
Slide 71 text
Permissões
Slide 72
Slide 72 text
Permissões manuais Transparência
Limite de permissões
Tipo de permissões
Segurança
Slide 73
Slide 73 text
Ao instalar todas as
permissões requisitadas
são dadas
Android ...
Slide 74
Slide 74 text
Ao instalar todas as
permissões requisitadas
são dadas
Android ...
Slide 75
Slide 75 text
Allow Snapchat to access
this device’s location?
Ao instalar todas as
permissões requisitadas
são dadas
Funcionalidades do sistemas
precisam de autorização
explícita do utilizador
Android ... Android 6.0
Slide 76
Slide 76 text
Allow Snapchat to access
this device’s location?
Ao instalar todas as
permissões requisitadas
são dadas
Funcionalidades do sistemas
precisam de autorização
explícita do utilizador
As permissões vão sendo cada vez mais restritivas:
- Apenas enquanto estamos a utilizar a aplicação
- Permitir apenas uma única vez
Android ... Android 6.0 … Android 10
Slide 77
Slide 77 text
Allow Snapchat to access
this device’s location?
Ao instalar todas as
permissões requisitadas
são dadas
Funcionalidades do sistemas
precisam de autorização
explícita do utilizador
As permissões vão sendo cada vez mais restritivas:
- Apenas enquanto estamos a utilizar a aplicação
- Permitir apenas uma única vez
Android ... Android 6.0 … Android 10 Android 11
Slide 78
Slide 78 text
Android 11
● Permissão única
● O utilizador tem de
permitir o acesso
contínuo à localização
○ Maior segurança
○ Poupança de bateria
● Revogação de permissões
para aplicações que não
são utilizadas
Slide 79
Slide 79 text
Android 10+
● Permissão de leitura/escrita de ficheiros não é
suficiente.
● Ficheiro não criados pela aplicação que sejam
modificados precisam de permissão adicional.
Slide 80
Slide 80 text
val uri = MediaStore.setRequireOriginal(imageUri)
contentResolver.openInputStream(uri).use { inputStream ->
// ...
}
Aceder a um ficheiro
Slide 81
Slide 81 text
val uri = MediaStore.setRequireOriginal(imageUri)
contentResolver.openInputStream(uri).use { inputStream ->
// ...
}
Aceder a um ficheiro
E/AndroidRuntime: FATAL EXCEPTION: main
Process: pt.atp.bobi, PID: 30411
java.lang.RuntimeException: Unable to start activity
ComponentInfo{pt.atp.bobi/pt.atp.toy.CounterActivity}: java.io.FileNotFoundException:
/storage/emulated/0/bobi.mp4: open failed: EACCES (Permission denied)
Slide 82
Slide 82 text
val uri = MediaStore.setRequireOriginal(imageUri)
contentResolver.openInputStream(uri).use { inputStream ->
// ...
}
Aceder a um ficheiro
E/AndroidRuntime: FATAL EXCEPTION: main
Process: pt.atp.bobi, PID: 30411
java.lang.RuntimeException: Unable to start activity
ComponentInfo{pt.atp.bobi/pt.atp.toy.CounterActivity}: java.io.FileNotFoundException:
/storage/emulated/0/bobi.mp4: open failed: EACCES (Permission denied)
Slide 83
Slide 83 text
if(ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
// Não temos permissão ✋
}
Como verificar?
● Pedir a permissão apenas quando o utilizador começa a interagir com a
funcionalidade que necessita dessa permissão.
● Não bloquear o utilizador. Dar a possibilidade ao utilizador de cancelar a
permissão.
● Se o utilizador rejeitar ou revogar a permissão que uma funcionalidade
necessita, devemos permitir que o utilizador continue a utilizar a aplicação,
possivelmente até a desativar a funcionalidade em questão.
Algumas recomendações
Slide 90
Slide 90 text
Passaporte para
a Google
Slide 91
Slide 91 text
Isabel Póvoa
Strategic Cloud Engineer
Google
Slide 92
Slide 92 text
Abre o Android
Studio e vamos
começar a
programar
Slide 93
Slide 93 text
VS
Ronda 4
Slide 94
Slide 94 text
class kennel {
val dog by lazy {
Dog()
}
}
Instanciação Lazy
class Kennel {
private Dog dog;
public Dog getDog() {
if(dog == null){
dog = new Dog();
}
return dog;
}
}
Slide 95
Slide 95 text
class Dog(val name:String,
val color: String,
val legs: int)
val dog = Dog("Bobi", "Verde", 5)
val (name, color, legs) = dog
println(name) // Bobi
println(color) // Verde
println(legs) // 5
♂
Deconstrução de uma variável
Slide 96
Slide 96 text
val (name, color, legs) = dog
if(legs > 4) {
println("O $name, é um cão? ")
}
♂
Deconstrução de uma variável
Slide 97
Slide 97 text
val (name, _, legs) = dog
if(legs > 4) {
println("O $name, é um cão? ")
}
♂
Deconstrução de uma variável
Slide 98
Slide 98 text
val dogs = listOf(...)
for ((name, color, legs) in dogs) {
// ...
}
♂
Deconstrução de uma variável
Slide 99
Slide 99 text
fun `O Bobi é lindo`() {
...
}
Espaços em nomes de funções ♂
♂
Slide 100
Slide 100 text
@Test
fun `O Bobi é lindo`() {
...
}
Espaços em nomes de funções ♂
♂
* Apenas aceitável utilizar isto em testes
Slide 101
Slide 101 text
Sexta-Feira
negra
Photo by Xiaolong Wong on Unsplash
Produto não disponível
(voltamos para a semana)
Slide 102
Slide 102 text
Castanhas quentes e
boas!
Photo by sare . on Unsplash
Slide 103
Slide 103 text
Destruir todas as Activities
Para ativar:
1. Definições
2. Sistema
3. Opções de programador
4. Não manter atividades
Slide 104
Slide 104 text
Destruir todas as Activities
Para ativar:
1. Definições
2. Sistema
3. Opções de programador
4. Não manter atividades
Slide 105
Slide 105 text
Android Studio
Para criar uma pasta:
1. Botão direito na barra lateral
2. “New Project Group”
Para mover um projeto:
1. Botão direito na barra lateral
2. “Move To Group”
Slide 106
Slide 106 text
Android Studio
Modificar as cores do logcat
1. Android Studio (barra no topo)
2. Preferences
3. Pesquisar por logcat
4. Selecionar o tipo e a cor
Slide 107
Slide 107 text
adb
Enviar texto para o telemóvel/emulador
1. Abrir o Terminal/Linha de comandos
2. adb shell input text “Isto é uma mensagem de texto com um emoji ”
http://bit.ly/atp2020-codelabs
Slide 108
Slide 108 text
adb
Enviar texto para o telemóvel/emulador
1. Abrir o Terminal/Linha de comandos
2. adb shell input text “Isto é uma mensagem de texto com um emoji ”
http://bit.ly/atp2020-codelabs
Slide 109
Slide 109 text
Trabalho Para Casa
Slide 110
Slide 110 text
Trabalho para casa
● Adicionar ViewModel
○ Login
○ MainActivity
● Alterar a imagem, pedindo permissão
Slide 111
Slide 111 text
Dúvidas?
Slide 112
Slide 112 text
Continuamos a
responder no
discord
Slide 113
Slide 113 text
Obrigado
♀
Slide 114
Slide 114 text
Android
Training
Program
PORTUGAL
Aula #5
Listas, listas e mais listas
Próxima aula: 18 de Novembro