Skip to content

Extensions | LiveData

Davide Steduto edited this page Oct 28, 2017 · 3 revisions

This extension is a complement to the new Android Architecture Components able to simplify and customize the implementation of the ViewModel responsible for preparing data (items) for any Adapter (not only FlexibleAdapter).
As we know, the ViewModel objects are automatically retained during configuration changes so that data they hold is immediately available to the next activity or fragment instance that in turn supplies the Adapter.

How it works

The following sequence diagram evidences the interaction between the various key components that a modern Android App should implement. The provided extension is at the center of logic where the user implements only 4 methods with 1 line each (see the example below).

LiveData sequence diagram

A real example

▶️ Exercise: Load from any repository type a list of messages of type Message of a conversation identified by the threadId of type Long and provide a list of type HolderMessageItem for any Adapter.

import android.arch.lifecycle.LiveData;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import java.util.List;

import javax.inject.Inject;

import eu.davidea.blueapp.persistence.repositories.MessageRepository;
import eu.davidea.blueapp.ui.items.HolderMessageItem;
import eu.davidea.flexibleadapter.livedata.FlexibleFactory;
import eu.davidea.flexibleadapter.livedata.FlexibleItemProvider;
import eu.davidea.flexibleadapter.livedata.FlexibleViewModel;
import timber.log.Timber;

public class MessageViewModel
        extends FlexibleViewModel<List<Message>, HolderMessageItem, Long> {

    private MessageRepository repository;
    private MessageItemFactory itemFactory;

    @Inject
    public MessageViewModel(MessageRepository repository) {
        super(); // super() must be called!
        Timber.d("Init MessageViewModel");
        this.itemFactory = new MessageItemFactory();
        this.repository = repository;
    }

    /*---------------------------*/
    /* VIEW MODEL IMPLEMENTATION */
    /*---------------------------*/

    /**
     * Provides "live" items to observe as input for the Adapter.
     * To call from the UI Controller.
     * 
     * Note: you don't need to override this function, it's here only for the
     * purpose of the extension documentation.
     *
     * @return the LiveData to observe with the list of items for the any Adapter.
     */
    @NonNull
    @Override
    public LiveData<List<HolderMessageItem>> getLiveItems() {
        return super.getLiveItems();
    }

    /**
     * Triggers the loading of the source.
     * To call from the UI Controller.
     * 
     * @param threadId the Source identifier to provide to the repository
     */
    public void loadMessages(Long threadId) {
        super.loadSource(threadId);
    }

    /**
     * Retrieves the LiveData coming from Local or Remote repository.
     *
     * @param threadId the Source identifier to provide to the repository
     * @return the LiveData, input for the mapping
     */
    @NonNull
    @Override
    protected LiveData<List<Message>> getSource(@NonNull Long threadId) {
        return this.repository.loadLiveConversation(threadId);
    }

    /**
     * Checks if resource is valid before mapping the items.
     * Should be implemented by checking, at least, if the Source is not null
     * and if the original list is not empty.
     *
     * @param messages the type of input Source containing the original list
     * @return true if source is valid, false otherwise
     */
    @Override
    protected boolean isSourceValid(@Nullable List<Message> messages) {
        return messages != null && !messages.isEmpty();
    }

    /**
     * Maps the Source containing the original list to a list suitable for
     * the Adapter.
     * Tip: User can use a custom implementation of FlexibleItemProvider.Factory
     * that together with FlexibleItemProvider will help to map the model to
     * an Adapter item.
     *
     * @param messages the type of input Source containing the original list
     * @return the mapped list suitable for the Adapter.
     */
    @Override
    protected List<HolderMessageItem> map(@NonNull List<Message> messages) {
        return FlexibleItemProvider
                .with(itemFactory)
                .from(messages);
    }
    
    /*---------------*/
    /* INNER CLASSES */
    /*---------------*/

    static class MessageItemFactory
            implements FlexibleItemProvider.Factory<Message, HolderMessageItem> {
        @NonNull
        @Override
        public HolderMessageItem create(Message message) {
            // Equivalent of: new HolderMessageItem(message);
            return FlexibleFactory.create(HolderMessageItem.class, message);
        }
    }
}
Clone this wiki locally