androiduser-interfaceandroid-architecture-componentsandroid-jetpacklong-running-processes

Best practices to show a progress for Android JetPack?


We should init our ViewModel asynchronously. Loading the data right from a local SQLite DB could be quite fast(but not always). If we will pump data from some remote source it could be pretty notable delay. So user needs some visual feedback on it and main UI must not be available.

What are the best practices to show progress while data is preparing for ViewModel or when we send some data for processing(waiting for changes in the ViewModel)?

E.g., if LiveData value is null switch onto the Loading Progress fragment and prepare the ViewModel there and switch back when it will be ready? But ViewModel use to be bound to particular fragment...

Just make some root view invisible while data is loaded/processed? Other words add into each fragment some progress section to show it instead of main content? But this approach requires too much boilerplate code for a lot of layouts.

Should we ever take care on it if know that data expected to be loaded almost immediately?

How do you handle long running operations UI in your JetPack apps?


Solution

  • I found the answer in the official docs and sample. Here are 2 parts:

    1. How to feedback the progress using LiveData
    2. How to render the progress on UI

    1)The answer is found at the end of Guide to app architecture

    Scroll down to the "Addendum: exposing network status" topic. They suggest to use some LiveData wrapper based on the MediatorLiveData

    There is a lot of boilerplate code you have to write and maintain wrapping any of your LiveData which needs to track the progress or loading errors.

    2)In the sample Android Architecture Components Basic Sample you can find the suggested approach how to feedback the loading process on UI. This sample is really basic so they just hide the UI widgets while the loading is going.

    Each fragment layout should have some progress support stuff. Here we have just the variable:

    <data>
            <variable
                name="isLoading"
                type="boolean" />
    </data>
    

    And each widget(view) we want to hide during the loading process should have the attribute:

    app:visibleGone="@{isLoading}"
    

    Since it is the app defined attribute we must take care on it somewhere. So there should be adapter to support visibleGone:

    public class BindingAdapters {
        @BindingAdapter("visibleGone")
        public static void showHide(View view, boolean show) {
            view.setVisibility(show ? View.VISIBLE : View.GONE);
        }
    }
    

    Of course we can use something like FrameLayout and put some progress panel which will obscure our controls and show some rolling progress wheel(instead of simple hiding it and showing the empty screen). But the problem is you have to take care on it for each your fragment layout. And if you have some complicated screens(e.g. Tabs) it just could complicate your code even more.

    Conclusion: #2 is more or less workable solution. Although I would prefer some separate progress fragment screen for all cases and do not bloat up each of my fragment layouts with the loading progress and error handling stuff.

    But #1 make me ask what is the real benefit of using the LiveData? To do things right with a good error handling, progress feedback we need to maintain too much boilerplate code. They claim right at the own guide:

    In the recommended app architecture section above, we omitted network error and loading states to keep the code snippets simple.

    Because it seems the design of LiveData is not intended for easy progress and errors handling.