Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Android Training Program - Portugal, Aula 4

9e9eb825c69d719f2d3c32bdd3bc971e?s=47 ATP Portugal
November 11, 2020

Android Training Program - Portugal, Aula 4

Aula #4: Fundações III 💪

Para a melhor experiência possível queremos que a nossa interface seja o mais rápida e fluida possível - para isso, operações pesadas devem ser delegadas para threads secundárias.

- Architecture Components
- Operações assíncronas
- Live data
- ViewModel
- Permissões

9e9eb825c69d719f2d3c32bdd3bc971e?s=128

ATP Portugal

November 11, 2020
Tweet

Transcript

  1. Android Training Program PORTUGAL Aula #4 Fundações III

  2. • 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
  3. Andres-Leonardo Martinez-Ortiz Google Carlos Mota Formador Renato Almeida Formador @davilagrau

    @cafonsomota @tallnato Equipa Daniela Ferreira Gestora de comunidades
  4. • 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
  5. #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 ✅
  6. Photo by Mollie Sivaram on Unsplash

  7. Photo by Mollie Sivaram on Unsplash ‍ ~5000 participantes ~2600

    subscrições no YouTube ~20000 visualizações Obrigado! ‍♀
  8. 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!
  9. http://events.withgoogle.com/atp2020 ✉ atp-suporte@googlegroups.com http://bit.ly/atp2020-youtube http://bit.ly/atp2020-discord Links

  10. http://bit.ly/atp2020-live

  11. http://bit.ly/atp2020-codelabs

  12. http://bit.ly/kahoot-aula4

  13. Resumo da Aula #3

  14. Layouts +351990000001

  15. Views +351990000001

  16. Resolução medium resolution (mdpi) HTC Wildfire S high resolution (hdpi)

    Samsung Galaxy S2 extra extra extra high resolution (xxxhdpi) Pixel 5
  17. +351990000001

  18. 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
  19. Aula #4

  20. e… o que acontece quando rodamos o ecrã?

  21. e… o que acontece quando rodamos o ecrã?

  22. None
  23. None
  24. Architecture components

  25. LiveData ViewModel Room

  26. ViewModel

  27. 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
  28. • 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
  29. Ciclo de vida Activity onCreate onResume onPause onDestroy ecrã rodado

    onCreate onResume activity recriada finish() onPause onDestroy activity destruída ViewModel onCleared() Activity activity criada
  30. dependencies { ... implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0" } Como utilizar? Importar a

    biblioteca app/build.gradle
  31. class CounterViewModel : ViewModel() { } Como utilizar? Implementar numa

    class CounterViewModel.kt
  32. class CounterViewModel : ViewModel() { var number: Int = 0

    private set fun increment() { ++number } } Como utilizar? Lógica no ViewModel CounterViewModel.kt
  33. 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
  34. 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
  35. 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
  36. class CounterActivity : AppCompatActivity() { ... override fun onCreate(savedInstanceState: Bundle?)

    { ... findViewById<Button>(R.id.btn_counter).setOnClickListener { viewModel.increment() Toast.makeText(this, "Number: ${viewModel.number}", LENGTH_SHORT).show() } } } Como utilizar? Lógica na Activity CounterActivity.kt
  37. None
  38. class CounterActivity : AppCompatActivity() { ... override fun onCreate(savedInstanceState: Bundle?)

    { ... val tvCounter = findViewById<TextView>(R.id.tv_counter) findViewById<Button>(R.id.btn_counter).setOnClickListener { viewModel.increment() tvCounter.text = viewModel.number.toString() } } } CounterActivity.kt Como utilizar? Actualizar uma TextView
  39. None
  40. LiveData

  41. class CounterViewModel : ViewModel() { var number: Int = 0

    private set fun increment() { ++number lotsOfProcessings(number) } ... } Bué processamento
  42. None
  43. None
  44. • Podemos ter um ViewModel partilhado, ◦ Permite comunicar entre

    uma Activity e um Fragment ◦ Permite comunicar entre vários Fragment’s E ainda...
  45. LiveData

  46. 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
  47. 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
  48. • 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 ...
  49. • 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
  50. dependencies { ... implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0" } Como utilizar? Importar a

    biblioteca app/build.gradle
  51. class CounterViewModel : ViewModel() { var number: Int = 0

    private set fun increment() { ++number } } Como utilizar?
  52. class CounterViewModel : ViewModel() { private val _liveNumber = MutableLiveData<Int>()

    private var number: Int = 0 val liveNumber : LiveData<Int> = _liveNumber fun increment() { ++number _liveNumber.postValue(number) } } Como utilizar? No ViewModel
  53. class CounterViewModel : ViewModel() { private val _liveNumber = MutableLiveData<Int>()

    private var number: Int = 0 val liveNumber : LiveData<Int> = _liveNumber fun increment() { ++number _liveNumber.postValue(number) } } Como utilizar? No ViewModel
  54. class CounterViewModel : ViewModel() { private val _liveNumber = MutableLiveData<Int>()

    private var number: Int = 0 val liveNumber : LiveData<Int> = _liveNumber fun increment() { ++number _liveNumber.postValue(number) } } Como utilizar? No ViewModel
  55. class CounterActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) {

    ... viewModel = ViewModelProvider(this).get(CounterViewModel::class.java) val tvCounter = findViewById<TextView>(R.id.tv_counter) findViewById<Button>(R.id.btn_counter).setOnClickListener { viewModel.increment() } viewModel.liveNumber.observe(this){ number -> tvCounter.text = number.toString() } } } Como utilizar? Na Activity
  56. class CounterActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) {

    ... viewModel = ViewModelProvider(this).get(CounterViewModel::class.java) val tvCounter = findViewById<TextView>(R.id.tv_counter) findViewById<Button>(R.id.btn_counter).setOnClickListener { viewModel.increment() } viewModel.liveNumber.observe(this){ number -> tvCounter.text = number.toString() } } } Como utilizar? Na Activity
  57. class CounterActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) {

    ... viewModel = ViewModelProvider(this).get(CounterViewModel::class.java) val tvCounter = findViewById<TextView>(R.id.tv_counter) findViewById<Button>(R.id.btn_counter).setOnClickListener { viewModel.increment() } viewModel.liveNumber.observe(this){ number -> tvCounter.text = number.toString() } } } Como utilizar? Na Activity
  58. LiveData

  59. • 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
  60. Operações assíncronas

  61. atualizar atualizar atualizar atualizar desenha o ecrã desenha o ecrã

    desenha o ecrã objetivo: 60fps UI-thread Operações assíncronas
  62. atualizar operação pesada atualizar desenha o ecrã desenha o ecrã

    desenha o ecrã objetivo: 60fps UI-thread Operações assíncronas
  63. • 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
  64. 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
  65. 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
  66. • 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
  67. • Service • Threads • IntentService • AsyncTasks • WorkManager

    • JobScheduler • DownloadManager • AlarmManager • Coroutines Operações assíncronas Soluções
  68. • Service • Threads • IntentService • AsyncTasks • WorkManager

    • JobScheduler • DownloadManager • AlarmManager • Coroutines Operações assíncronas Soluções
  69. … e qual é que devemos mesmo escolher?

  70. 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
  71. Permissões

  72. Permissões manuais Transparência Limite de permissões Tipo de permissões Segurança

  73. Ao instalar todas as permissões requisitadas são dadas Android ...

  74. Ao instalar todas as permissões requisitadas são dadas Android ...

  75. 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
  76. 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
  77. 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
  78. 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
  79. 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.
  80. val uri = MediaStore.setRequireOriginal(imageUri) contentResolver.openInputStream(uri).use { inputStream -> // ...

    } Aceder a um ficheiro
  81. 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)
  82. 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)
  83. if(ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { // Não temos permissão ✋

    } Como verificar?
  84. if(ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), REQUEST_CODE) } Como

    pedir?
  85. if(ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), REQUEST_CODE) } Como

    pedir?
  86. override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String?>, grantResults: IntArray) { if

    (requestCode == REQUEST_CODE) { if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // Acesso a tudo } else { // Ainda não dá ✋ } } else { super.onRequestPermissionsResult(requestCode, permissions, grantResults) } } Resultado
  87. if(ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, arrayOf( Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.CAMERA ),

    REQUEST_CODE) } Várias permissões
  88. <manifest ...> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> </manifest> E

    ainda...
  89. • 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
  90. Passaporte para a Google

  91. Isabel Póvoa Strategic Cloud Engineer Google

  92. Abre o Android Studio e vamos começar a programar ‍‍

  93. VS Ronda 4

  94. 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; } }
  95. 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
  96. val (name, color, legs) = dog if(legs > 4) {

    println("O $name, é um cão? ") } ‍♂ Deconstrução de uma variável
  97. val (name, _, legs) = dog if(legs > 4) {

    println("O $name, é um cão? ") } ‍♂ Deconstrução de uma variável
  98. val dogs = listOf<Dog>(...) for ((name, color, legs) in dogs)

    { // ... } ‍♂ Deconstrução de uma variável
  99. fun `O Bobi é lindo`() { ... } Espaços em

    nomes de funções ‍♂ ‍♂
  100. @Test fun `O Bobi é lindo`() { ... } Espaços

    em nomes de funções ‍♂ ‍♂ * Apenas aceitável utilizar isto em testes
  101. Sexta-Feira negra Photo by Xiaolong Wong on Unsplash Produto não

    disponível (voltamos para a semana)
  102. Castanhas quentes e boas! Photo by sare . on Unsplash

  103. Destruir todas as Activities Para ativar: 1. Definições 2. Sistema

    3. Opções de programador 4. Não manter atividades
  104. Destruir todas as Activities Para ativar: 1. Definições 2. Sistema

    3. Opções de programador 4. Não manter atividades
  105. 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”
  106. 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
  107. 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
  108. 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
  109. Trabalho Para Casa ‍‍

  110. Trabalho para casa • Adicionar ViewModel ◦ Login ◦ MainActivity

    • Alterar a imagem, pedindo permissão
  111. Dúvidas?

  112. Continuamos a responder no discord

  113. Obrigado ‍♀

  114. Android Training Program PORTUGAL Aula #5 Listas, listas e mais

    listas Próxima aula: 18 de Novembro