-
Notifications
You must be signed in to change notification settings - Fork 523
DataProvider & LiveData
- DataProviders
- Transferring data to UI via LiveData
- Best practices/antipractices
- DataProvider simplifications
- DataProviders utility
- LiveData transformations
DataProviders are gateways to safely receiving an asynchronous result from an operation. They support notifications for when the DataProvider has new data to be retrieved, force usage of suspend functions to encourage coroutine use, have utilities for simplifying their usage, and provide an easy way to pass data to the UI via LiveData.
You should generally never need to create a new DataProvider since there are existing bridges for most asynchronous operations, but if you do make sure to follow other DataProviders for a reference to make sure you're implementing it correctly.
LiveData
is a lifecycle-aware stateful concurrency primitive that was added to Android Jetpack. The team prefers using LiveData
for a few reasons:
- It supports receiving data from a background thread via an Android
Handler
post - It's lifecycle-aware (e.g. it ensures that the background data passed from (1) does not trigger logic for an activity that's being torn down due to the user exiting it or a configuration change)
- It integrates trivially with Android databinding which the team uses to simplify UI development
All DataProvider
s are convertible to LiveData
using an extension function: toLiveData()
.
- Prefer to start with a
DataProvider
(e.g. in-memory, file-provided, or network-provided) and then transform/combine it as needed rather than creating new dispatchers - Never use coroutines outside of the domain layer
- Never perform any multi-threading operations outside of coroutines (except when unavoidable--see the 'other cases for background processing' section)
- Never use locks within coroutines
- Prefer using concurrent data structures over atomics
- Never send data to the UI without using a
DataProvider
+LiveData
-
Do make use of
TestCoroutineDispatchers
when writing tests to ensure proper synchronization - When integrating a new library that has asynchronous support, make sure it's configurable and that its executors/dispatchers are set up to use the common dispatchers
- Prefer using existing
DataProvider
s rather than creating new ones - Never use
observeForever
on aLiveData
in the UI, and in cases when it's used elsewhere make sure the observer is removed - Prefer conducting transformations in
DataProvider
s rather thanLiveData
, except when impossible (e.g. extracting values from the finalAsyncResult
passed from the data provider) - Never combine data through
LiveData
(e.g. usingMediatorLiveData
); prefer combining data throughDataProvider
s instead and convert toLiveData
at the end - Never use
SharedPreferences
--usePersistentCacheStore
instead since it never blocks the main thread
There are a number of preexisting DataProvider
s & utilities to simplify development.
DataProviders
is an injectable class that provides a number of helpful functions:
- Transforming data providers (e.g. converting their type into a new type, or performing some operation once data is available)
- Combining two or more data providers together (which will block on all data providers being ready)
- Converting data providers to
LiveData
- Creating an in-memory data provider (to, for example, start a data provider chain)
LiveData
supports Transformations
& other utilities (like MediatorLiveData
) which can be used to transform and/or combine LiveData
objects similarly to DataProvider
s. The team generally makes use of transformations to perform boundary error checking/value retrieval from AsyncResult
, but otherwise prefers to leverage DataProvider
s for more complex combines/transformations. LiveData
's utilities have some shortcomings and are fairly easy to get wrong, so prefer to use DataProvider
s when possible.
Have an idea for how to improve the wiki? Please help make our documentation better by following our instructions for contributing to the wiki.
Core documentation
Developing Oppia
- Contributing to Oppia Android
- Bazel
- Key Workflows
- Testing
- Developing Skills
- Frequent Errors and Solutions
- RTL Guidelines
- Working on UI
- Writing Design Docs
Developer Reference
- Code style
- Background Processing
- Dark mode
- Buf Guide
- Firebase Console Guide
- Platform Parameters & Feature Flags
- Work Manager
- Dependency Injection with Dagger
- Revert & regression policy
- Upgrading target SDK version
- Spotlight Guide
- Triaging Process
- Bazel
- Internationalization
- Terminology in Oppia
- Past Events