Advertisement
  1. Code
  2. Android SDK

Componentes da Arquitetura Android: Lifecycle e LiveModel

Scroll to top
Read Time: 11 min
This post is part of a series called Android Architecture Components.
Introduction to Android Architecture Components
Android Architecture Components: LiveData

() translation by (you can also view the original English article)

Na última parte desta série, Introdução aos Componentes da Arquitetura Android, falamos sobre a nova arquitetura Android e por que ela foi desenvolvida. Basicamente, a nova arquitetura aborda alguns problemas conhecido do Android oferecendo um pacote de componentes feitos sob medida para o sistema. Estes são os blocos de construção da arquitetura. Nós já demos uma rápida olhada para estes componentes, então agora é hora de começar a mergulhar fundo.

Neste tutorial, nós olharemos de perto o Lifecycle  e os componentes LiveModel. Enquanto estivermos  explorando-os, também vamos conferir alguns trechos de código de um aplicativo de exemplo. Já que estamos falando de novos paradigmas do Android, os trechos de código são todos feitos com o incrível Kotlin.

Se você ainda não conhece Kotlin, por favor, não tenha medo de acompanhar; a implementação é extremamente similar ao Java, e estou seguro de que você será capaz de compreendê-la. Se você quer aprender mais sobre Kotlin, Jessica Thornsby escreveu uma excelente série aqui no Tuts + sobre codificação de Apps Android em Kotlin. Você deveria dar uma olhada!

1. O Projeto de Exemplo

Nós fornecemos um pequeno aplicativo para demonstrar os conceitos de que estamos falando neste tutorial. O nome do aplicativo é MyWeatherApp, e permite que o usuário busque o tempo do dia, usando o nome de uma cidade ou a localização do usuário atual. A lógica do aplicativo é bastante simples, mas você pode melhorá-la para criar seu próprio aplicativo.

Sample ApplicationSample ApplicationSample Application

Como você pode ver no diagrama abaixo, a arquitetura está em conformidade com aquela proposta pela Android, e usamos o novo pacote de Componentes de Arquitetura, tanto quanto possível, mantendo as coisas simples o suficiente para uma análise básica. Como um bônus, estamos usando Dagger 2 como uma biblioteca de injeção de dependência. No entanto, não nos aprofundaremos muito em sua implementação, pois ela escaparia do escopo do tutorial.

The WeatherApplicationThe WeatherApplicationThe WeatherApplication

Como Funciona o App?

O aplicativo é tão simples quanto possível. Tem apenas uma atividade, onde o usuário pode obter o tempo pesquisando o nome da cidade ou usando a localização atual do dispositivo. O MainActivity chama o MainModel para obter um observável LiveData e reagir a ele. O MainModel busca de dados meteorológicos do MainRepository e consolida todos os dados como LiveData. O MainRepository obtém seus dados de várias fontes.

Executando o Aplicativo de Exemplo

Baixe ou clone o repositório do nosso GitHub repo e construa-o com Gradle ou abra-o no seu IDE. Você também deve criar uma conta em OpenWeatherMap e obter um novo ID de aplicativo. Adicione a ID de aplicativo em um string resource chamado openWeather.

1
<string name="openWeather">XXXXXXXXXXXXXXX</string>

2. Criação de um Projeto

Uma vez que os Componentes de Arquitetura ainda estão em alpha, você tem que incluir o repositório do Google, que contém algumas bibliotecas experimentais, no build.gradle do projeto.

1
allprojects {
2
    repositories {
3
4
        // add this repository
5
        maven { url 'https://maven.google.com' }
6
    }
7
}

No módulo build.gradle, adicione o seguinte à seção de dependências para adicionar suporte para os Lifecycles, LiveData e ViewModel:

  • compile "android.arch.lifecycle:runtime:1.0.0-alpha5"
  • compile "android.arch.lifecycle:extensions:1.0.0-alpha5"
  • annotationProcessor "android.arch.lifecycle:compiler:1.0.0-alpha5"

Se você estiver usando Kotlin, você também deve adicionar ou substituir o annotationProcessor com kapt, que lida com a anotações em Kotlin.

1
kapt "android.arch.lifecycle:compiler:1.0.0-alpha5"

Para habilitar o kapt, adicione o seguinte na raiz do módulo build.gradle.

1
kapt {
2
    generateStubs = true
3
}

3. O Componente Lifecycle

Cada desenvolvedor Android está familiarizado com o conceito de ciclo de vida. O sistema conduz o ciclo de vida de Aplicações, Atividades, Fragmentos e assim por diante, sem o controle do desenvolvedor. Este conceito é um dos paradigmas do Android e, até recentemente, não era fácil de trabalhar com ele, uma vez que não era possível verificar diretamente o status atual do ciclo de vida de um componente. O que nós podíamos fazer era reagir a certos métodos, como o onCreate e onDestroy, acionados por eventos do ciclo de vida.

Activity LifecyleActivity LifecyleActivity Lifecyle

Isso tudo mudou desde o anúncio do pacote de Componentes de Arquitetura, que introduziu um componente chamado de Lifecycle. Agora, alguns objetos Android têm um Lifecycle  anexado a eles, e isso muda muitas coisas para os desenvolvedores. É possível consultar o estado do Lifecycle a qualquer momento, e também é possível reagir a eventos de Lifecycle usando anotações. Na verdade, a espinha dorsal dos novos Componentes de Arquitetura do Android é o componente Lifecycle.

Todos os elementos do pacote android.arch.lifecycle são importantes para o conceito de ciclo de vida, mas dois deles merecem mais atenção: LifecycleOwner e LifecycleObserver. Eles criam a possibilidade de trabalhar com o Lifecycle, observando e reagindo a eventos que ocorrem em Atividades, Fragmentos, Serviços e assim por diante.

O LifecycleOwner

O LifecycleOwner é uma interface de método único para classes que contêm um Lifecycle. Ela abstrai a posse de um Lifecycle, permitindo-lhe escrever componentes que podem trabalhar com ele. Pelos novos padrões, as Atividades e os Fragmentos são LifecycleOwners. No entanto, até a versão final dos componentes de arquitetura ser lançada, você deve usar algumas classes especiais: ActivityLifecycle, FragmentLifecycle e LifecycleService.

1
class MainActivity : LifecycleActivity() {
2
    override fun onCreate(savedInstanceState: Bundle?) {
3
        super.onCreate(savedInstanceState)
4
    }
5
}

Não há alteração significativa na implementação dessas classes quando comparada com o padrão de Atividades e Fragmentos. Quando uma classe estende a qualquer uma delas, vai ter um Lifecycle anexado, que pode ser buscado a qualquer momento com o método getLifecycle(). Outra possibilidade interessante é que podemos verificar o estado atual do ciclo de vida com getCurrentState, que retorna um Lifecycle.State.

Há cinco estados diferentes do Lifecycle:

  • INITIALIZED: para um objeto que foi chamado, mas que ainda não está "ativo". É o equivalente a um estado antes do método Activity.onCreate.
  • CREATED: para objetos que foram recém criados. É chamado após o método onCreate e também chamado antes do método onStop.
  • STARTED: chamado após onStart e antes do método onPause.
  • RESUMED: O estado ativo ou o estado retomado para um LifecycleOwner. Chamado após o método onResume.
  • DESTROYED: para um objeto LifecycleOwner destruído. Este Lifecycle não vai despachar mais eventos. Este evento é alcançado antes do método onDestroy.
LifecycleOwner EventsLifecycleOwner EventsLifecycleOwner Events

O LifecycleObserver

Uma das propriedades mais interessantes do Lifecycle é que ele pode facilmente ser observado. As classes LifecycleObserver  podem observar os componentes LifecycleOwner, como Atividades e Fragmentos. Ele recebe o LifecycleOwner.Events e pode reagir a eles através da anotação  @OnLifeCycleEvent (Lifecycle.Event).

1
class MainObserver : LifecycleObserver, AnkoLogger {
2
    @OnLifecycleEvent( Lifecycle.Event.ON_RESUME )
3
    fun onResult() {
4
        info("onResult")
5
    }
6
    @OnLifecycleEvent( Lifecycle.Event.ON_STOP )
7
    fun onStop() {
8
        info("onStop")
9
    }
10
}

Os métodos anotados com @OnLifecycleEvent não precisam de argumentos, mas se usados, o primeiro argumento deve ser o LifecycleOwner. Quando a anotação usa Lifecycle.Event.ON_ANY, o método deve esperar dois argumentos: LifecycleOwner e Lifecycle.Event.

1
@OnLifecycleEvent( Lifecycle.Event.ON_ANY )
2
fun onEvent( owner: LifecycleOwner, event: Lifecycle.Event )
3
{
4
    info("onEvent: ownerState: ${owner.lifecycle.currentState}")
5
    info("onEvent: event: $event")
6
}

Para ativar a anotação de @OnLifecycleEvent, o LifecycleObserver deve estar observando um Lifecycle, caso contrário ele não vai receber o evento. Para fazer isso funcionar, chame o Lifecycle.addObserver(LifecycleOwner) e o LifecycleOwner posteriormente será capaz de reagir a Lifecycle.Event. Também é possível chamar Lifecycle.removeObsever(LifecycleObserver) para remover um observador.

1
class MainActivity : LifecycleActivity(), AnkoLogger {
2
    @Inject lateinit var mainObserver: MainObserver
3
    
4
    override fun onCreate(savedInstanceState: Bundle?) {
5
        // ...
6
        // On Kotlin, instead of getLifecycle,
7
        // we can call lifecycle directly
8
        lifecycle.addObserver( mainObserver )
9
    }
10
    
11
    override fun onDestroy() {
12
        // ...
13
        lifecycle.removeObserver( mainObserver )
14
    }
15
}

Existem vários casos de uso interessantes para o LifecycleObserver. Por exemplo, ele pode ser usado para criar uma camada Presenter do padrão de arquitetura Model View Presenter. Ele também pode ser usado para criar os ouvintes(listeners) que podem parar de ouvir quando o ciclo de vida é desabilitado.

4. O Componente LiveModel

Projetado para funcionar em conjunto com a camada de interface do usuário, o componente  ViewModel fecha uma lacuna que existia no Android desde o início: fornecendo uma maneira elegante de gerenciar e armazenar objetos de dados relacionados com o modo de exibição. O componente mantém a integridade de dados entre as alterações de configuração, pode ser compartilhado entre Atividades e Fragmentos e é uma excelente ferramenta para evitar completamente os vazamentos de memória.

O ViewModel é criado sempre em estreita relação com um escopo específico, Atividade ou um Fragmento. O escopo é mantido enquanto a Atividade ou o Fragmento está ativo. Em termos práticos, o ViewModel reconecta com a view após as alterações de configuração, mantendo-se até a view principal ser destruída. De acordo com a documentação oficial:

A finalidade do ViewModel é adquirir e manter a informação que é necessária para uma Atividade ou um Fragmento.

Além de tudo isso, o ViewModel facilita a separação de conceitos no processo de desenvolvimento do Android. Movendo todas as operações relacionadas a dados para este componente e deixando-o a lidar com a lógica, a testabilidade do aplicativo e a facilidade de manutenção é muito maior. Com o ViewModel, é possível facilmente adotar a Arquitetura Android  proposta na Google I/O de 2017. Você ainda pode usá-la para adotar padrões de arquitetura mais sofisticados, como MVP ou MVVM.

Implementando uma ViewModel

Existem duas maneiras de implementar uma ViewModel. O padrão é estender a classe, fornecendo um construtor sem argumentos. Esta é a maneira mais fácil, mas ele não funciona bem com Injeção de Dependência.

1
class MainViewModel : ViewModel() {
2
    init {
3
        // initialize some behavior
4
    }
5
    
6
    fun getData() : LiveData<String> {
7
        // get some data
8
    }
9
10
    override fun onCleared() {
11
        super.onCleared()
12
        // called before its destruction
13
    }
14
}

Para obter uma ViewModel construída com essa técnica a partir de uma Atividade ou Fragmento, simplesmente chame ViewModelProviders.of (FragmentActivity) .get(class<T>).</T> O último argumento deve conter a classe ViewModel. A mesma instância de ViewModel será obtida pela view e conterá todos os dados para essa view.

1
val viewModel: MainViewModel = 
2
    ViewModelProviders.of(this).get(MyViewModel::class.java)

Observe que, uma vez que o ViewModel é tirado do método ViewModelProviders.of, seu construtor não pode receber argumentos. Como alternativa, você pode implementar um ViewModelProvider.Factory. Na verdade, esta é a mesma técnica que usamos para injetar o ViewModel.

Injetando uma ViewModel

Ao usar o DI, as coisas ficam um pouco mais complicadas. Você precisará implementar um ViewModelProvider.Factory. As etapas a seguir podem ser usadas para injetar um ViewModel usando Dagger. A ViewModelFactory é uma classe utilitária que fornece uma ViewModel para um escopo.

1
@Suppress("UNCHECKED_CAST")
2
@Singleton
3
class ViewModelFactory
4
@Inject
5
constructor(
6
        private val creators: Map<Class<out ViewModel>,
7
                @JvmSuppressWildcards Provider<ViewModel>>
8
) : ViewModelProvider.Factory {
9
10
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
11
        var creator: Provider<out ViewModel>? = creators[modelClass]
12
        if (creator == null) {
13
            for ((key, value) in creators) {
14
                if (modelClass.isAssignableFrom(key)) {
15
                    creator = value
16
                    break
17
                }
18
            }
19
        }
20
        if (creator == null) {
21
            throw IllegalArgumentException("unknown model class " + modelClass)
22
        }
23
        try {
24
            return creator.get() as T
25
        } catch (e: Exception) {
26
            throw RuntimeException(e)
27
        }
28
29
    }
30
}

O Dagger também precisa de um @MapKey definido para ViewModel e um binder para cada modelo e para a factory no módulo.

1
// @MapKey
2
@MustBeDocumented
3
@Target(
4
        AnnotationTarget.FUNCTION,
5
        AnnotationTarget.PROPERTY_GETTER,
6
        AnnotationTarget.PROPERTY_SETTER
7
)
8
@kotlin.annotation.Retention()
9
@MapKey
10
internal annotation class ViewModelKey(
11
        val value: KClass<out ViewModel>)
12
        
13
// ViewModel Module
14
@Module
15
abstract class ViewModelsModule {
16
    // Bind each ViewModel
17
    @Binds
18
    @IntoMap
19
    @ViewModelKey( MainViewModel::class )
20
    abstract fun bindMainViewModel( mainViewModel: MainViewModel ) : ViewModel
21
22
    // ViewModel factory binding
23
    @Binds
24
    abstract fun bindViewModelFactory( factory: ViewModelFactory ) : ViewModelProvider.Factory
25
}

Depois disso, siga os procedimentos normais do Dagger e você será capaz de criar um ViewModel capaz de injetar argumentos em seu construtor. Para criar uma nova instância, pegue o ViewModelFactory e obtenha a ViewModel desejada dele.

1
// Get the ViewModel factory
2
@Inject lateinit var viewModelFactory: 
3
    ViewModelProvider.Factory
4
    
5
// Get ViewModel
6
val viewModel = ViewModelProviders.of(this, viewModelFactory)
7
    .get(MainViewModel::class.java)

Em nosso projeto de exemplo, você pode dar uma olhada no DI usando o Dagger. Também forneci-lhe  uma pasta de exemplos do tutorial do repositório GitHub com trechos mostrando como configurar ViewModels no sistema de Dagger usando Kotlin.

1
class MainViewModel
2
@Inject
3
constructor(
4
        private val repository: MainRepository
5
)
6
    : ViewModel(), AnkoLogger {
7
    
8
    // ... code goes here
9
}

Conclusão

Até agora, nossa jornada através dos novos Componentes de Arquitetura Android tem sido muito produtiva. No entanto, ainda temos algum caminho a percorrer. No próximo tutorial, vamos falar sobre o incrível componente LiveData, investigando suas características básicas e avançadas, e aplicar esses conceitos ao nosso aplicativo de exemplo.

Te vejo logo! E enquanto isso, confira alguns dos nossos outros posts sobre o desenvolvimento de apps Android!

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.