Eventos do Android LiveData

O LiveData é uma ótima ferramenta para vincular o estado de seus dados e objetos a um ciclo de vida (LifecycleOwner, geralmente um Fragmento ou Atividade).

Normalmente, o LiveData é colocado no ViewModel e usado para atualizar o estado da sua interface do usuário. Freqüentemente, um ViewModel pode sobreviver a um LifecycleOwner e manter um estado LiveData. Esse mecanismo é adequado quando você precisa salvar dados e restaurá-los após algum tempo, por exemplo, após uma alteração na configuração.

Mas e se quisermos usar o mecanismo de eventos, não estados? E é necessário no contexto do ciclo de vida do navegador (LifecycleOwner). Por exemplo, precisamos exibir uma mensagem após uma operação assíncrona, desde que o LifecycleOwner ainda esteja ativo, tenha navegadores ativos e esteja pronto para atualizar sua interface do usuário. Se usarmos o LiveData, receberemos a mesma mensagem após cada alteração na configuração ou com cada novo assinante. Uma das soluções que se sugere, após o processamento dos dados em algum navegador, é redefinir esses dados para o LiveData.

Por exemplo, um código como este:

Observer { handle(it) yourViewModel.liveData.value = null } 

Mas essa abordagem tem várias desvantagens e não atende a todos os requisitos necessários.

Eu gostaria de ter um mecanismo de evento que:

  1. notifica apenas assinantes ativos
  2. no momento da assinatura não notifica sobre dados anteriores,
  3. tem a capacidade de definir o sinalizador manipulado como true para interromper o processamento adicional do evento.

Eu implementei a classe MutableLiveEvent, que possui todas as propriedades acima e que podem funcionar como um LiveData normal.

Como usar:

 //   EventArgs        class MyIntEventArgs(data: Int) : EventArgs<Int>(data) //  viewModel class MainViewModel : ViewModel() { private val myEventMutable = MutableLiveEvent<MyIntEventArgs>() val myEvent = myEventMutable as LiveData<MyIntEventArgs> fun sendEvent(data: Int) { myEventMutable.value = MyIntEventArgs(data) } } val vm = ViewModelProviders.of(this).get(MainViewModel::class.java) vm.myEvent.observe(this, Observer { //   /* *   ,    , *      ,   handled = true */ it.handled = true }) 

Todo o código está disponível no GitHub , e abaixo vou falar um pouco sobre a implementação.

 class MutableLiveEvent<T : EventArgs<Any>> : MutableLiveData<T>() { internal val observers = ArraySet<PendingObserver<in T>>() @MainThread override fun observe(owner: LifecycleOwner, observer: Observer<in T>) { val wrapper = PendingObserver(observer) observers.add(wrapper) super.observe(owner, wrapper) } override fun observeForever(observer: Observer<in T>) { val wrapper = PendingObserver(observer) observers.add(wrapper) super.observeForever(observer) } @MainThread override fun removeObserver(observer: Observer<in T>) { when (observer) { is PendingObserver -> { observers.remove(observer) super.removeObserver(observer) } else -> { val pendingObserver = observers.firstOrNull { it.wrappedObserver == observer } if (pendingObserver != null) { observers.remove(pendingObserver) super.removeObserver(pendingObserver) } } } } @MainThread override fun setValue(event: T?) { observers.forEach { it.awaitValue() } super.setValue(event) } } 

A idéia é que dentro da classe MutableLiveEvent, nos métodos observe e observeForever, agrupe os navegadores em uma classe interna especial PendingObserver, que chama o navegador real apenas uma vez e somente se o sinalizador pendente estiver definido como verdadeiro e o evento ainda não tiver sido processado.

 internal class PendingObserver<T : EventArgs<Any>>(val wrappedObserver: Observer<in T>) : Observer<T> { private var pending = false override fun onChanged(event: T?) { if (pending && event?.handled != true) { pending = false wrappedObserver.onChanged(event) } } fun awaitValue() { pending = true } } 

No PendingObserver, o sinalizador pendente é definido como falso por padrão. Isso resolve o item 2 (não notificar dados antigos) da nossa lista.

E o código em MutableLiveEvent

 override fun setValue(event: T?) { observers.forEach { it.awaitValue() } super.setValue(event) } 

Primeiros conjuntos pendentes para true e somente depois atualizam os dados dentro de si. Isso garante a implementação da reivindicação 1. (alerta apenas assinantes ativos).

O último ponto sobre o qual ainda não falei é o EventArgs. Essa classe é uma generalização na qual há um sinalizador manipulado para interromper o processamento adicional do evento (Cláusula 3).

 open class EventArgs<out T>(private val content: T?) { var handled: Boolean = false val data: T? get() { return if (handled) { null } else { content } } } 

Isso é tudo, obrigado por assistir!

Source: https://habr.com/ru/post/pt468749/


All Articles