AndroidX, LiveData and the Observable Pattern
Tags: android, java
Published: 2020-01-27 19:39:53

### Android has come a loooong way

I was really pleased when returning to Android development after being away for a while at how much better it has gotten with AndroidX. Now this isn’t breaking news to most, but Android Jetpack and the `androidx.*` package libraries not only solved one of the most confusing aspects of Android development ( those pesky versioned Android Support Libraries ) but also brought the observable pattern front and center with `LiveData` and `ViewModel`. While LiveData is not quite as robust or generic as RxJava, it is effectively a basic implementation of the Observable pattern with the added benefit of being **Lifecycle Aware**.

That last bit there is huge. Being lifecycle aware means that you don’t have to worry about memory leaks or hard-to-debug NPEs because your observables live and die with their observers. Activities and their layouts go in and out of memory as user's navigate around. Ensuring that our asynchronous data loads don't try and update screens long gone is especially important.

### It’s all about the `observe` method.

`public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer);` takes two parameters: an owner with a Lifecycle and an Observer for trigger when the underlying value changes. Pretty straightforward.

Coupling this with Android’s `Data Binding Library` means screens that handling swiping full screen content layouts is a really clean and pleasant exercise. One of our latest client projects involved adding the capability for their customers to take the physical gift cards purchased in their stores and add them to a digital wallet. We wanted to build a way for customers to be able to view all the details of an individual card (card number, barcode, terms and conditions, transaction history, etc..) We decided that a full screen view of each card was required for all this information but wanted users to be able to swipe left and right to horizontally page between each of the cards in their wallet.

We decided to effectively use a `ViewPager` and `PagerAdapter` to accomplish the horizontal paging between gift cards. That, combined with a `ViewModel` to track the current page meant that the our observe callback just had to update the data binding backing the view after loading from our API.

Here is a simplified example of the Activity:

```java
public class GiftCardDetailsActivity extends AppCompatActivity implements GiftCardDetailsPagerListener {
    private GiftCardsDetailViewModel giftCardsViewModel;
    private GiftCardDetailsActivityBinding binding;
    private GiftCardTransactionAdapter giftCardTransactionAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        int initialPosition = (int) getIntent().getSerializableExtra(GIFT_CARD_INITIAL_POSITION_EXTRA);

        giftCardsViewModel = ViewModelProviders.of(this).get(GiftCardsDetailViewModel.class);
        giftCardTransactionAdapter = new GiftCardTransactionAdapter();
        binding = DataBindingUtil.setContentView(this, R.layout.gift_card_details_activity);
        binding.giftCardViewPager.setAdapter(new GiftCardDetailsPagerAdapter(this, initialPosition);

        giftCardsViewModel.getCurrentGiftCard().observe(this, currentGiftCard -> {
            binding.setGiftCard(currentGiftCard);
            binding.barcodeImage.setImageBitmap(BarcodeGenerator.generateBarcodeImage(getResources(), currentGiftCard.getNumber()));
            giftCardTransactionAdapter.setGiftCardTransactions(currentGiftCard.getTransactions());
            giftCardTransactionAdapter.notifyDataSetChanged();
        });

        giftCardsViewModel.setCurrentGiftCardPosition(initialPosition);
    }

    @Override
    public void onPageSelected(int position) {
        giftCardsViewModel.setCurrentGiftCardPosition(position);
    }
}

``` 

And the ViewModel:

```java
public class GiftCardsDetailViewModel extends AsyncLoadingViewModel implements GiftCardsApiResponseListener {

    private GiftCardApiService giftCardApiService;
    private MutableLiveData<GiftCard> currentGiftCard = new MutableLiveData<>(new GiftCard());
    private List<GiftCard> giftCards;
    private int initialPosition;

    LiveData<GiftCard> getCurrentGiftCard() {
        if (giftCards == null) {
            setIsLoading(true);
            giftCardsApiService.fetch(this);
        }

        return currentGiftCard;
    }

    void setCurrentGiftCardPosition(int position) {
        if (giftCards == null || position > giftCards.size()-1) {
            initialPosition = position;
            return;
        }

        currentGiftCard.setValue(giftCards.get(position));
    }

    @Override
    public void onGiftCardsApiResponseSuccess(List<GiftCard> giftCards) {
        setIsLoading(false);
        this.giftCards = giftCards;
        this.currentGiftCard.setValue(giftCards.get(initialPosition));
    }

    @Override
    public void onGiftCardsApiResponseFailure(String errorMessage) {
        setIsLoading(false);
        // classic "error handling here" blog post comment...
    }
}

```

On of the nice things about this implementation is that there is only one way, and exactly one way, in which data is set on the view binding. Our Activity’s `onCreate()` is responsible for instantiating the `ViewModel` that tracks the current page, and holds onto the full list of gift cards that are asynchronously loaded from our server’s API. Anytime the instance of the `ViewModel`’s underlying `LiveData` member's change, our observable callback is triggered and we simply update the bindings gift card. Notice that the first argument to the `observe()` call is an implementation of `LifecycleAware`. In this case, our Activity itself.

A few other noteworthy details:
1. While our ViewModel's private member `currentGiftCard` is of type `MutableLiveData` we only expose `LiveData` to observers. This is important because it isolates the responsibility of managing that state to our `ViewModel`.
1. The `AsyncLoadingViewModel` is just a parent class that extends Android's `ViewModel` class and manages a single piece of state for loading masks of type `LiveData<Boolean>`
1. The `ViewModel` holds onto the initial position until the initial data load is completed.

### Followup

I should note here that there are a few other ways to accomplish similar behavior by leveraging more of the AndroidX library, including `Two-way data binding`, `@BindingAdapters`, and `BaseObservables`. Given we are not taking any user input on these largely view-only screens, this felt like overkill. This simple pattern seemed to work well for the initial version of these screens.

Perhaps a follow-up post to compare / contrast is in order!