Este codelab faz parte do curso Conceitos básicos do Kotlin para Android. Você aproveitará mais o curso se fizer os codelabs em sequência. Todos os codelabs do curso estão listados na página de destino dos codelabs do curso Conceitos básicos do Kotlin para Android.
Introdução
Uma das principais prioridades da criação de uma experiência do usuário impecável para o app é garantir que a IU seja sempre responsiva e funcione bem. Uma forma de melhorar o desempenho da IU é mover tarefas de longa duração, como operações de banco de dados, para o segundo plano.
Neste codelab, você vai implementar a parte voltada para o usuário do app TrackMySleepQuality, usando corrotinas Kotlin para realizar operações de banco de dados fora da linha de execução principal.
O que você já precisa saber
Você precisa:
- Como criar uma interface do usuário (IU) básica usando uma atividade, fragmentos, visualizações e gerenciadores de cliques.
- Navegar entre fragmentos usando
safeArgs
para transmitir dados simples entre eles. - Visualize modelos, veja fábricas de modelo, transformações e
LiveData
. - Como criar um banco de dados
Room
, um DAO e definir entidades. - Ele é útil quando você conhece os conceitos de linha de execução e multiprocessamento.
O que você vai aprender
- Como as linhas de execução funcionam no Android.
- Como usar corrotinas do Kotlin para remover operações do banco de dados da linha de execução principal.
- Como exibir dados formatados em um
TextView
.
Atividades do laboratório
- Estenda o app TrackMySleepQuality para coletar, armazenar e exibir dados no banco de dados e nele.
- Usar corrotinas para executar operações de banco de dados de longa duração em segundo plano.
- Use
LiveData
para acionar a navegação e a exibição de um snackbar. - Use
LiveData
para ativar e desativar botões.
Neste codelab, você criará o modelo de visualização, corrotinas e partes da exibição de dados do app TrackMySleepQuality.
O app tem duas telas, representadas por fragmentos, como mostrado na figura abaixo.
A primeira tela, mostrada à esquerda, tem botões para iniciar e interromper o rastreamento. A tela mostra todos os dados de sono do usuário. O botão Limpar exclui permanentemente todos os dados que o app coletou para o usuário.
A segunda tela, mostrada à direita, serve para selecionar uma classificação de qualidade do sono. No app, a classificação é representada numericamente. Para fins de desenvolvimento, o app mostra os ícones de rostos e os equivalentes numéricos deles.
O fluxo do usuário é o seguinte:
- O usuário abre o app e vê a tela de monitoramento do sono.
- O usuário toca no botão Iniciar. Ele registra o horário de início e o exibe. O botão Iniciar está desativado, e o botão Parar está ativado.
- O usuário toca no botão Parar. Registra o horário de término e abre a tela de qualidade do sono.
- O usuário seleciona um ícone de qualidade do sono. A tela será fechada, e a tela de monitoramento exibirá a hora de término do sono e a qualidade do sono. O botão Parar está desativado, e o botão Iniciar está ativado. O app está pronto para outra noite.
- O botão Limpar será ativado sempre que houver dados no banco de dados. Quando o usuário toca no botão Limpar, todos os dados dele são apagados sem fazer o recurso, e não há a mensagem "Você tem certeza?
Esse app usa uma arquitetura simplificada, conforme mostrado abaixo, no contexto da arquitetura completa. O app usa apenas os seguintes componentes:
- controlador de IU
- Ver modelo e
LiveData
- Um banco de dados da Room
Nesta tarefa, você usará um TextView
para exibir dados formatados de monitoramento do sono. Essa não é a interface final. Você aprenderá de uma maneira melhor em outro codelab.
Você pode continuar com o app TrackMySleepQuality que criou no codelab anterior ou fazer o download do app inicial para este codelab.
Etapa 1: fazer o download e executar o app inicial
- Faça o download do app TrackMySleepQuality-Coroutines-Starter no GitHub.
- Crie e execute o app. O app mostra a IU para o fragmento
SleepTrackerFragment
, mas sem dados. Os botões não respondem a toques.
Etapa 2: inspecionar o código
O código inicial deste codelab é igual ao código da solução do codelab 6.1 Criar um banco de dados da Room.
- Abra res/layout/activity_main.xml. Esse layout contém o fragmento
nav_host_fragment
. Observe também a tag<merge>
.
A tagmerge
pode ser usada para eliminar layouts redundantes ao incluir layouts. É recomendável usá-la. Um exemplo de layout redundante seria o ConstraintLayout > LinearLayout > TextView, em que o sistema pode eliminar o LinearLayout. Esse tipo de otimização pode simplificar a hierarquia de visualização e melhorar o desempenho do app. - Na pasta navigation, abra navigation.xml. É possível ver dois fragmentos e as ações de navegação que os conectam.
- Na pasta layout, clique duas vezes no fragmento de rastreador de sono para ver o layout XML. Observe o seguinte:
- Os dados de layout são agrupados em um elemento
<layout>
para ativar a vinculação de dados. ConstraintLayout
e as outras visualizações são organizadas dentro do elemento<layout>
.- O arquivo tem uma tag
<data>
de marcador.
O app inicial também oferece dimensões, cores e estilo para a IU. O app contém um banco de dados Room
, um DAO e uma entidade SleepNight
. Se você não concluiu o codelab anterior, experimente esses aspectos do código.
Agora que você tem um banco de dados e uma IU, é necessário coletar, adicionar e exibir os dados no banco de dados. Tudo isso é feito no modelo de visualização. Seu modelo de visualização do rastreador de sono gerenciará os cliques nos botões, interagirá com o banco de dados usando o DAO e fornecerá os dados à IU usando a LiveData
. Todas as operações do banco de dados precisarão ser executadas na linha de execução de IU principal, e você fará isso usando corrotinas.
Etapa 1: adicionar o SleepTrackerViewModel
- No pacote sleeptracker, abra SleepTrackerViewModel.kt.
- Inspecione a classe
SleepTrackerViewModel
, que é fornecida no app inicial e também é mostrada abaixo. A classe estendeAndroidViewModel()
. Essa classe é igual àViewModel
, mas usa o contexto do aplicativo como um parâmetro e o disponibiliza como uma propriedade. Você precisará dele mais tarde.
class SleepTrackerViewModel(
val database: SleepDatabaseDao,
application: Application) : AndroidViewModel(application) {
}
Etapa 2: adicionar SleepTrackerViewModelFactory
- No pacote sleeptracker, abra SleepTrackerViewModelFactory.kt.
- Examine o código que você recebeu para a fábrica, que é mostrado abaixo:
class SleepTrackerViewModelFactory(
private val dataSource: SleepDatabaseDao,
private val application: Application) : ViewModelProvider.Factory {
@Suppress("unchecked_cast")
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(SleepTrackerViewModel::class.java)) {
return SleepTrackerViewModel(dataSource, application) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
Observações:
- O
SleepTrackerViewModelFactory
fornecido usa o mesmo argumento que aViewModel
e estende oViewModelProvider.Factory
. - Na fábrica, o código modifica
create()
, que usa qualquer tipo de classe como argumento e retorna umViewModel
. - No corpo de
create()
, o código verifica se há uma classeSleepTrackerViewModel
disponível e, se houver, retorna uma instância dela. Caso contrário, o código vai gerar uma exceção.
Etapa 3: atualizar o SleepTrackerFragment
- No
SleepTrackerFragment
, acesse uma referência ao contexto do aplicativo. Coloque a referência emonCreateView()
, abaixo debinding
. Você precisa de uma referência ao app a que esse fragmento está anexado para transmitir ao provedor de fábrica de modelo de visualização.
A função KotlinrequireNotNull
gerará umaIllegalArgumentException
se o valor fornull
.
val application = requireNotNull(this.activity).application
- Você precisa de uma referência à sua fonte de dados com uma referência ao DAO. Em
onCreateView()
, antes dereturn
, defina umdataSource
. Para acessar uma referência ao DAO do banco de dados, useSleepDatabase.getInstance(application).sleepDatabaseDao
.
val dataSource = SleepDatabase.getInstance(application).sleepDatabaseDao
- No
onCreateView()
, antes doreturn
, crie uma instância doviewModelFactory
. Você precisa transmitir adataSource
e oapplication
.
val viewModelFactory = SleepTrackerViewModelFactory(dataSource, application)
- Agora que você tem uma fábrica, acesse uma referência ao
SleepTrackerViewModel
. O parâmetroSleepTrackerViewModel::class.java
refere-se à classe Java de tempo de execução desse objeto.
val sleepTrackerViewModel =
ViewModelProviders.of(
this, viewModelFactory).get(SleepTrackerViewModel::class.java)
- O código finalizado ficará assim:
// Create an instance of the ViewModel Factory.
val dataSource = SleepDatabase.getInstance(application).sleepDatabaseDao
val viewModelFactory = SleepTrackerViewModelFactory(dataSource, application)
// Get a reference to the ViewModel associated with this fragment.
val sleepTrackerViewModel =
ViewModelProviders.of(
this, viewModelFactory).get(SleepTrackerViewModel::class.java)
Veja o método onCreateView()
até agora:
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Get a reference to the binding object and inflate the fragment views.
val binding: FragmentSleepTrackerBinding = DataBindingUtil.inflate(
inflater, R.layout.fragment_sleep_tracker, container, false)
val application = requireNotNull(this.activity).application
val dataSource = SleepDatabase.getInstance(application).sleepDatabaseDao
val viewModelFactory = SleepTrackerViewModelFactory(dataSource, application)
val sleepTrackerViewModel =
ViewModelProviders.of(
this, viewModelFactory).get(SleepTrackerViewModel::class.java)
return binding.root
}
Etapa 4: adicionar uma vinculação de dados ao modelo de visualização
Com a ViewModel
básica no lugar, você precisa terminar de configurar a vinculação de dados no SleepTrackerFragment
para conectar a ViewModel
à IU.
No arquivo de layout fragment_sleep_tracker.xml
:
- No bloco
<data>
, crie uma<variable>
que faça referência à classeSleepTrackerViewModel
.
<data>
<variable
name="sleepTrackerViewModel"
type="com.example.android.trackmysleepquality.sleeptracker.SleepTrackerViewModel" />
</data>
Em SleepTrackerFragment
:
- Defina a atividade atual como a proprietária do ciclo de vida da vinculação. Adicione este código ao método
onCreateView()
, antes da instruçãoreturn
:
binding.setLifecycleOwner(this)
- Atribua a variável de vinculação
sleepTrackerViewModel
àsleepTrackerViewModel
. Coloque este código dentro deonCreateView()
, abaixo do código que cria oSleepTrackerViewModel
:
binding.sleepTrackerViewModel = sleepTrackerViewModel
- Você provavelmente verá um erro, porque precisará recriar o objeto de vinculação. Limpe e recrie o projeto para eliminar o erro.
- Por fim, como sempre, verifique se o código é compilado e executado sem erros.
No Kotlin, as corrotinas são a maneira de processar tarefas de longa duração de maneira elegante e eficiente. As corrotinas do Kotlin permitem converter códigos baseados em callback em códigos sequenciais. O código escrito de forma sequencial geralmente é mais fácil de ler e pode até mesmo usar recursos de linguagem, como exceções. No fim, corrotinas e callbacks fazem a mesma coisa: aguardam a disponibilidade de um resultado de uma tarefa de longa duração e continuam a execução.
As corrotinas têm as seguintes propriedades:
- As corrotinas são assíncronas e sem bloqueio.
- As corrotinas usam funções de suspensão para tornar o código assíncrono sequencial.
As corrotinas são assíncronas.
Uma corrotina é executada independentemente das principais etapas de execução do programa. Isso pode ser paralelo ou em um processador separado. Pode ser que, enquanto o restante do app estiver aguardando a entrada, você demora um pouco para processar. Um dos aspectos importantes do assíncrono é que você não vai esperar que o resultado esteja disponível até esperar explicitamente.
Por exemplo, digamos que você tenha uma pergunta que requer pesquisa e peça a um colega para encontrar a resposta. Eles trabalham e trabalham nisso, o que é como fazer o trabalho "de forma assíncrona" e "em uma conversa separada". Você pode continuar fazendo outro trabalho que não dependa da resposta até que seu colega retorne e diga qual é a resposta.
As corrotinas não causam bloqueios.
Sem bloqueio significa que uma corrotina não bloqueia a linha de execução principal ou de IU. Portanto, com corrotinas, os usuários sempre têm a experiência mais tranquila possível, porque a interação da IU sempre tem prioridade.
As corrotinas usam funções de suspensão para tornar o código assíncrono sequencial.
A palavra-chave suspend
é a maneira como o Kotlin marca uma função ou tipo de função como disponível para corrotinas. Quando uma corrotina chama uma função marcada com suspend
, em vez de bloquear até que ela retorne como uma chamada de função normal, a corrotina suspende a execução até que o resultado esteja pronto. Em seguida, a corrotina retoma de onde parou, com o resultado.
Enquanto a corrotina está suspensa e aguardando um resultado, ela desbloqueia a linha de execução em que está sendo executada. Dessa forma, outras funções ou corrotinas podem ser executadas.
A palavra-chave suspend
não especifica a linha de execução em que o código é executado. Uma função de suspensão pode ser executada em uma linha de execução em segundo plano ou na linha de execução principal.
Para usar corrotinas no Kotlin, você precisa de três itens:
- Uma vaga
- Um expedidor
- Um escopo
Vaga: basicamente, qualquer job pode ser cancelado. Cada corrotina tem um job, e você pode usar esse job para cancelar a corrotina. Os jobs podem ser organizados em hierarquias pai-filho. Cancelar um job pai cancela imediatamente todos os filhos dele, o que é muito mais conveniente do que cancelar manualmente cada corrotina.
Dispatcher: o agente envia corrotinas para serem executadas em várias linhas de execução. Por exemplo, Dispatcher.Main
executa tarefas na linha de execução principal, e Dispatcher.IO
descarrega tarefas de bloqueio de E/S para um pool compartilhado de linhas de execução.
Escopo: um escopo de corrotina define o contexto em que a corrotina é executada. Um escopo combina informações sobre o job e o agente de uma corrotina. Os escopos rastreiam as corrotinas. Quando você inicia uma corrotina, ela se limita a um escopo, o que significa que você indicou qual escopo rastreia a corrotina.
Você quer que o usuário interaja com os dados de sono das seguintes maneiras:
- Quando o usuário toca no botão Iniciar, o app cria uma nova noite de sono e armazena a noite no banco de dados.
- Quando o usuário toca no botão Stop, o app atualiza a noite com um horário de término.
- Quando o usuário toca no botão Limpar, o app apaga os dados no banco de dados.
Essas operações de banco de dados podem levar muito tempo, então devem ser executadas em uma linha de execução separada.
Etapa 1: configurar corrotinas para operações de banco de dados
Quando o botão Iniciar no app Tracker de sono é tocado, você quer chamar uma função no SleepTrackerViewModel
para criar uma nova instância de SleepNight
e armazenar a instância no banco de dados.
O toque em qualquer botão aciona uma operação de banco de dados, como a criação ou atualização de um SleepNight
. Por esse e outros motivos, você usa corrotinas para implementar gerenciadores de cliques para os botões do app.
- Abra o arquivo
build.gradle
no nível do app e encontre as dependências das corrotinas. Para usar corrotinas, você precisa dessas dependências, que foram adicionadas para você.
O$coroutine_version
é definido no arquivobuild.gradle
do projeto comocoroutine_version =
'1.0.0'
.
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutine_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutine_version"
- Abra o arquivo
SleepTrackerViewModel
. - No corpo da classe, defina
viewModelJob
e atribua a ela uma instância deJob
. EsseviewModelJob
permite cancelar todas as corrotinas iniciadas por esse modelo de visualização quando ele não for mais usado e for destruído. Dessa forma, você não terá corrotinas que não retornem a lugar nenhum.
private var viewModelJob = Job()
- No final do corpo da classe, substitua
onCleared()
e cancele todas as corrotinas. Quando oViewModel
é destruído, oonCleared()
é chamado.
override fun onCleared() {
super.onCleared()
viewModelJob.cancel()
}
- Logo abaixo da definição de
viewModelJob
, defina umuiScope
para as corrotinas. O escopo determina em qual linha de execução a corrotina será executada, além do escopo que precisa saber sobre o job. Para conseguir um escopo, peça uma instância deCoroutineScope
e transmita um agente e um job.
Usar Dispatchers.Main
significa que as corrotinas iniciadas em uiScope
serão executadas na linha de execução principal. Isso é útil para muitas corrotinas iniciadas por um ViewModel
porque, depois que elas fazem algum processamento, resultam na atualização da IU.
private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
- Abaixo da definição de
uiScope
, defina uma variável com o nometonight
para armazenar a noite atual. Torne a variávelMutableLiveData
, porque você precisará observar os dados e alterá-los.
private var tonight = MutableLiveData<SleepNight?>()
- Para inicializar a variável
tonight
o mais rápido possível, crie um blocoinit
abaixo da definição detonight
e chameinitializeTonight()
. Você definiráinitializeTonight()
na próxima etapa.
init {
initializeTonight()
}
- Implemente
initializeTonight()
abaixo do blocoinit
. NouiScope
, inicie uma corrotina. Dentro dele, receba o valor detonight
do banco de dados chamandogetTonightFromDatabase()
e atribua o valor atonight.value
. Você definirágetTonightFromDatabase()
na próxima etapa.
private fun initializeTonight() {
uiScope.launch {
tonight.value = getTonightFromDatabase()
}
}
- implementar
getTonightFromDatabase()
; Defina-o como uma funçãoprivate suspend
que retorna umSleepNight
anulável, se não houver umSleepNight
iniciado. Isso causa um erro, já que a função precisa retornar algo.
private suspend fun getTonightFromDatabase(): SleepNight? { }
- No corpo da função de
getTonightFromDatabase()
, retorne o resultado de uma corrotina que é executada no contextoDispatchers.IO
. Use o agente de E/S porque o acesso aos dados do banco de dados é uma operação de E/S e não tem nada a ver com a IU.
return withContext(Dispatchers.IO) {}
- Dentro do bloco de retorno, deixe a corrotina chegar à noite (a noite mais recente) do banco de dados. Se os horários de início e de término não forem iguais, ou seja, se a noite já tiver sido concluída, retorne
null
. Caso contrário, retorne à noite.
var night = database.getTonight()
if (night?.endTimeMilli != night?.startTimeMilli) {
night = null
}
night
A função de suspensão concluída do getTonightFromDatabase()
ficará assim: Não haverá mais erros.
private suspend fun getTonightFromDatabase(): SleepNight? {
return withContext(Dispatchers.IO) {
var night = database.getTonight()
if (night?.endTimeMilli != night?.startTimeMilli) {
night = null
}
night
}
}
Etapa 2: adicionar o gerenciador de cliques para o botão "Iniciar"
Agora, você pode implementar onStartTracking()
, o gerenciador de cliques do botão Iniciar. É necessário criar um novo SleepNight
, inseri-lo no banco de dados e atribuí-lo à tonight
. A estrutura da onStartTracking()
será muito semelhante à initializeTonight()
.
- Comece com a definição de função para
onStartTracking()
. É possível colocar os gerenciadores de clique acima deonCleared()
no arquivoSleepTrackerViewModel
.
fun onStartTracking() {}
- No
onStartTracking()
, inicie uma corrotina nouiScope
, porque você precisa desse resultado para continuar e atualizar a IU.
uiScope.launch {}
- Dentro da corrotina, crie um novo
SleepNight
, que capture o horário atual como o horário de início.
val newNight = SleepNight()
- Ainda na inicialização da corrotina, chame
insert()
para inserirnewNight
no banco de dados. Você verá um erro, porque ainda não definiu essa função de suspensãoinsert()
. Esta não é a função DAO de mesmo nome.
insert(newNight)
- Também na inicialização da corrotina, atualize a
tonight
.
tonight.value = getTonightFromDatabase()
- Abaixo de
onStartTracking()
, definainsert()
como uma funçãoprivate suspend
que usa umSleepNight
como argumento.
private suspend fun insert(night: SleepNight) {}
- Para o corpo de
insert()
, inicie uma corrotina no contexto de E/S e insira a noite no banco de dados chamandoinsert()
do DAO.
withContext(Dispatchers.IO) {
database.insert(night)
}
- No arquivo de layout
fragment_sleep_tracker.xml
, adicione o gerenciador de cliques daonStartTracking()
aostart_button
usando a mágica da vinculação de dados configurada anteriormente. A notação de função@{() ->
cria uma função lambda que não aceita argumentos e chama o gerenciador de cliques nosleepTrackerViewModel
.
android:onClick="@{() -> sleepTrackerViewModel.onStartTracking()}"
- Compile e execute o app. Toque no botão Iniciar. Essa ação cria dados, mas você ainda não pode ver nada. Isso será corrigido em seguida.
fun someWorkNeedsToBeDone { uiScope.launch { suspendFunction() } } suspend fun suspendFunction() { withContext(Dispatchers.IO) { longrunningWork() } }
Etapa 3: exibir os dados
No SleepTrackerViewModel
, a variável nights
faz referência a LiveData
porque getAllNights()
no DAO retorna LiveData
.
Ele é um recurso Room
que sempre que os dados no banco de dados mudam, o LiveData
nights
é atualizado para mostrar os dados mais recentes. Você nunca precisa definir ou atualizar explicitamente a LiveData
. Room
atualiza os dados para que correspondam ao banco de dados.
No entanto, se você exibir nights
em uma visualização de texto, a referência do objeto será exibida. Para ver o conteúdo do objeto, transforme os dados em uma string formatada. Use um mapa Transformation
que é executado sempre que nights
recebe novos dados do banco de dados.
- Abra o arquivo
Util.kt
e remova a marca de comentário do código da definição deformatNights()
e as instruçõesimport
associadas. Para remover a marca de comentário do código no Android Studio, selecione todo o código marcado com//
e pressioneCmd+/
ouControl+/
. - O
formatNights()
retorna um tipoSpanned
, que é uma string formatada em HTML. - Abra strings.xml. Observe o uso de
CDATA
para formatar os recursos de string para exibir os dados de sono. - Abra o SleepTrackerViewModel. Na classe
SleepTrackerViewModel
, abaixo da definição deuiScope
, defina uma variável com o nomenights
. Receba todas as noites do banco de dados e atribua-as à variávelnights
.
private val nights = database.getAllNights()
- Logo abaixo da definição de
nights
, adicione o código para transformar anights
em umanightsString
. Use a funçãoformatNights()
daUtil.kt
.
Transmitanights
pela funçãomap()
da classeTransformations
. Para ter acesso aos recursos da string, defina a função de mapeamento como chamandoformatNights()
. Forneçanights
e um objetoResources
.
val nightsString = Transformations.map(nights) { nights ->
formatNights(nights, application.resources)
}
- Abra o arquivo de layout
fragment_sleep_tracker.xml
. EmTextView
, na propriedadeandroid:text
, é possível substituir a string de recursos por uma referência anightsString
.
"@{sleepTrackerViewModel.nightsString}"
- Recrie seu código e execute o app. Todos os dados de sono com horários de início serão exibidos agora.
- Toque no botão Iniciar mais algumas vezes e você verá mais dados.
Na próxima etapa, ative o recurso do botão Parar.
Etapa 4: adicionar o gerenciador de cliques para o botão "Stop"
Com o mesmo padrão da etapa anterior, implemente o gerenciador de clique do botão Stop no SleepTrackerViewModel.
.
- Adicione
onStopTracking()
aoViewModel
. Inicie uma corrotina nouiScope
. Se o horário de término ainda não tiver sido definido, definaendTimeMilli
como o horário atual do sistema e chameupdate()
com os dados noturnos.
No Kotlin, a sintaxe dereturn@
label
especifica a função que retorna essa instrução, entre várias funções aninhadas.
fun onStopTracking() {
uiScope.launch {
val oldNight = tonight.value ?: return@launch
oldNight.endTimeMilli = System.currentTimeMillis()
update(oldNight)
}
}
- Implemente
update()
usando o mesmo padrão usado para implementarinsert()
.
private suspend fun update(night: SleepNight) {
withContext(Dispatchers.IO) {
database.update(night)
}
}
- Para conectar o gerenciador de cliques à IU, abra o arquivo de layout
fragment_sleep_tracker.xml
e adicione-o aostop_button
.
android:onClick="@{() -> sleepTrackerViewModel.onStopTracking()}"
- Crie e execute seu aplicativo.
- Toque em Iniciar e em Parar. Você verá o horário de início e de término, a qualidade do sono sem valor e o tempo de sono.
Etapa 5: adicionar o gerenciador de cliques para o botão "Limpar"
- Da mesma forma, implemente
onClear()
eclear()
.
fun onClear() {
uiScope.launch {
clear()
tonight.value = null
}
}
suspend fun clear() {
withContext(Dispatchers.IO) {
database.clear()
}
}
- Para conectar o gerenciador de cliques à IU, abra
fragment_sleep_tracker.xml
e adicione-o aoclear_button
.
android:onClick="@{() -> sleepTrackerViewModel.onClear()}"
- Crie e execute seu aplicativo.
- Toque em Limpar para eliminar todos os dados. Em seguida, toque em Iniciar e Parar para criar novos dados.
Projeto do Android Studio: TrackMySleepQualityCoroutines
- Use
ViewModel
,ViewModelFactory
e a vinculação de dados para configurar a arquitetura da IU para o app. - Para manter a IU funcionando sem problemas, use corrotinas para tarefas de longa duração, como todas as operações de banco de dados.
- As corrotinas são assíncronas e sem bloqueio. Elas usam as funções
suspend
para tornar o código assíncrono sequencial. - Quando uma corrotina chama uma função marcada com
suspend
, em vez de bloquear até que ela retorne como uma chamada de função normal, ela suspende a execução até que o resultado esteja pronto. Em seguida, ele continua de onde parou com o resultado. - A diferença entre bloqueio e suspensão é que, se uma conversa for bloqueada, nenhum outro trabalho acontecerá. Se a linha de execução estiver suspensa, outras tarefas acontecerão até que o resultado seja disponibilizado.
Para iniciar uma corrotina, você precisa de um job, um agente e um escopo:
- Basicamente, um job é qualquer coisa que pode ser cancelada. Cada corrotina tem um job, e é possível usá-lo para cancelar uma corrotina.
- O agente envia corrotinas para serem executadas em várias linhas de execução. O
Dispatcher.Main
executa tarefas na linha de execução principal, eDispartcher.IO
serve para descarregar as tarefas de bloqueio de E/S para um pool compartilhado de linhas de execução. - O escopo combina informações, incluindo um job e um agente, para definir o contexto em que a corrotina é executada. Os escopos rastreiam as corrotinas.
Para implementar gerenciadores de cliques que acionam operações do banco de dados, siga este padrão:
- Iniciar uma corrotina que é executada na linha de execução principal ou de IU, porque o resultado afeta a IU.
- Chame uma função de suspensão para fazer o trabalho de longa duração. Assim, você não bloqueará a linha de execução de IU enquanto aguarda o resultado.
- O trabalho de longa duração não tem nada a ver com a IU. Portanto, alterne para o contexto de E/S. Dessa forma, o trabalho pode ser executado em um pool de linhas de execução que seja otimizado e reservado para esses tipos de operações.
- Em seguida, chame a função de banco de dados para realizar o trabalho.
Use um mapa Transformations
para criar uma string de um objeto LiveData
sempre que o objeto mudar.
Curso da Udacity:
- Como desenvolver apps Android com Kotlin (link em inglês)
Documentação do desenvolvedor Android:
RoomDatabase
- Reutilizar layouts com <include/>
ViewModelProvider.Factory
SimpleDateFormat
HtmlCompat
Outros documentos e artigos:
- Padrão de fábrica
- Codelab de corrotinas
- Documentação oficial de corrotinas (link em inglês)
- Contexto e agentes de corrotinas (link em inglês)
Dispatchers
- Exceder o limite de velocidade do Android
Job
launch
- Retorna e pula no Kotlin
- CDATA significa dados de caracteres (em inglês). CDATA significa que os dados entre essas strings incluem dados que poderiam ser interpretados como marcação XML, mas não deveriam ser.
Esta seção lista as possíveis atividades para os alunos que estão trabalhando neste codelab como parte de um curso ministrado por um instrutor. Cabe ao instrutor fazer o seguinte:
- Se necessário, atribua o dever de casa.
- Informe aos alunos como enviar o dever de casa.
- Atribua nota aos trabalhos de casa.
Os professores podem usar essas sugestões o quanto quiserem, e eles devem se sentir à vontade para passar o dever de casa como achar adequado.
Se você estiver fazendo este codelab por conta própria, use essas atividades para testar seu conhecimento.
Responda a estas perguntas.
Pergunta 1
Quais das opções a seguir são vantagens das corrotinas:
- Elas não causam bloqueios
- Elas são executadas de forma assíncrona.
- Elas podem ser executadas em uma linha de execução diferente da principal.
- Elas sempre tornam os apps mais rápidos.
- Eles podem usar exceções.
- Elas podem ser escritas e lidas como código linear.
Pergunta 2
O que é uma função de suspensão?
- Uma função comum, anotada com a palavra-chave
suspend
. - Uma função que pode ser chamada dentro de corrotinas.
- Enquanto uma função de suspensão estiver em execução, a linha de execução de chamada será suspensa.
- As funções de suspensão precisam ser sempre executadas em segundo plano.
Pergunta 3
Qual é a diferença entre bloquear e suspender uma conversa? Marque as afirmações verdadeiras.
- Quando a execução é bloqueada, nenhum outro trabalho pode ser executado na linha de execução bloqueada.
- Quando a execução é suspensa, a linha de execução pode fazer outro trabalho enquanto aguarda a conclusão do trabalho transferido.
- A suspensão é mais eficiente, porque as linhas de execução podem não estar esperando e não fazem nada.
- Seja bloqueada ou suspensa, a execução ainda aguardará o resultado da corrotina antes de continuar.
Vá para a próxima lição:
Para ver links de outros codelabs neste curso, consulte a página de destino dos codelabs do curso Conceitos básicos do Kotlin para Android.