Weitere ähnliche Inhalte Ähnlich wie LiveData on Steroids - Giora Shevach + Shahar Ben Moshe, Climacell (20) Mehr von DroidConTLV (20) Kürzlich hochgeladen (20) LiveData on Steroids - Giora Shevach + Shahar Ben Moshe, Climacell5. 5
LiveData (vanilla)
● Observable data holder
● Lifecycle-aware
○ Auto handling of lifecycle-related stuff
○ Avoids stupid crashes
○ Avoids memory leaks
○ App doesn’t do more work than it
6. 6
LiveData (vanilla)
● Observable data holder
● Lifecycle-aware
○ Auto handling of lifecycle-related stuff
○ Avoids stupid crashes
○ Avoids memory leaks
○ App doesn’t do more work than it
9. class WeatherDashboardActivity : Activity() {
private lateinit viewModel: WeatherDashboardViewModel
override fun onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProviders.of(this)
.get(WeatherDashboardViewModel::java.class)
observeWeather()
}
private fun observeWeather() {
viewModel.weather.observe(this, Observer { // it: Weather
showTemperatureInBig(it.temperature)
})
}
}
class WeatherDashboardViewModel : ViewModel {
val weather: LiveData<Weather> = weatherRepo.weather
}
class WeatherRepo {
val weather = MutableLiveData<Weather>()
// code to manipulate weather
}
10. class WeatherDashboardActivity : Activity() {
private lateinit viewModel: WeatherDashboardViewModel
override fun onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProviders.of(this)
.get(WeatherDashboardViewModel::java.class)
observeWeather()
}
private fun observeWeather() {
viewModel.weather.observe(this, Observer { // it: Weather
showTemperatureInBig(it.temperature)
})
}
}
class WeatherDashboardViewModel : ViewModel {
val weather: LiveData<Weather> = weatherRepo.weather
}
class WeatherRepo {
val weather = MutableLiveData<Weather>()
// code to manipulate weather
}
11. class WeatherDashboardActivity : Activity() {
private lateinit viewModel: WeatherDashboardViewModel
override fun onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProviders.of(this)
.get(WeatherDashboardViewModel::java.class)
observeWeather()
}
private fun observeWeather() {
viewModel.weather.observe(this, Observer { // it: Weather
showTemperatureInBig(it.temperature)
})
}
}
class WeatherDashboardViewModel : ViewModel {
val weather: LiveData<Weather> = weatherRepo.weather
}
class WeatherRepo {
val weather = MutableLiveData<Weather>()
// code to manipulate weather
}
12. class WeatherDashboardActivity : Activity() {
private lateinit viewModel: WeatherDashboardViewModel
override fun onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProviders.of(this)
.get(WeatherDashboardViewModel::java.class)
observeWeather()
}
private fun observeWeather() {
viewModel.weather.observe(this, Observer { // it: Weather
showTemperatureInBig(it.temperature)
})
}
}
class WeatherDashboardViewModel : ViewModel {
val weather: LiveData<Weather> = weatherRepo.weather
}
class WeatherRepo {
val weather = MutableLiveData<Weather>()
// code to manipulate weather
}
13. class WeatherDashboardActivity : Activity() {
private lateinit viewModel: WeatherDashboardViewModel
override fun onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProviders.of(this)
.get(WeatherDashboardViewModel::java.class)
observeWeather()
}
private fun observeWeather() {
viewModel.weather.observe(this, Observer { // it: Weather
showTemperatureInBig(it.temperature)
})
}
}
class WeatherDashboardViewModel : ViewModel {
val weather: LiveData<Weather> = weatherRepo.weather
}
class WeatherRepo {
val weather = MutableLiveData<Weather>()
// code to manipulate weather
}
16. fun observeWeatherAndOtherThingsAsWell() {
viewModel.weather.observe(this, Observer { weather -> // : Weather
viewModel.weatherError.observe(this, Observer { weatherError -> // : Exception
viewModel.weatherLoading.observe(this, Observer {weatherLoading -> // : Boolean
if (weatherError != null && weatherError.data != lastError) {
showErrorMessage()
lastError = weatherError.data
} else {
if (weatherLoading) {
showLoader()
} else {
if (weather != null) {
showTemperatureInBig(weather.temperature)
} else {
showErrorMessage()
}
}
}
})
})
})
19. 19
StatefulLiveData
⬢ How we built it
⬢ How to use it
⬢ How it solves common scenarios in daily app operation
⬢ Integration in existing code & libraries
⬢ Super powers
20. 20
❏ Get updates upon data changes
❏ Be lifecycle-aware
❏ Support every data type
❏ Know when the data is retrieved successfully and ready to
use
❏ Know when the data is loading, and supply some partial data
❏ Know when an error occurred, and know what the error is
Requirements
StatefulLiveData
21. StatefulData
abstract class StatefulData<T> {
class Success<T>(val data: T) : StatefulData<T>()
class Error<T>(val throwable: Throwable) : StatefulData<T>()
class Loading<T>(val loadingData: Any?) : StatefulData<T>()
}
❏ Get updates upon data changes
❏ Be lifecycle-aware
❏ Support every data type
❏ Know when the data is retrieved successfully and ready to use
❏ Know when the data is loading, and supply some partial data
❏ Know when an error occurred, and know what the error is
22. StatefulData
abstract class StatefulData<T> {
class Success<T>(val data: T) : StatefulData<T>()
class Error<T>(val throwable: Throwable) : StatefulData<T>()
class Loading<T>(val loadingData: Any?) : StatefulData<T>()
}
❏ Get updates upon data changes
❏ Be lifecycle-aware
✓ Support every data type
✓ Know when the data is retrieved successfully and ready to use
✓ Know when the data is loading, and supply some partial data
✓ Know when an error occurred, and know what the error is
27. class WeatherDashboardActivity : Activity()
private fun observeWeather() {
viewModel.weather.observe(this, Observer { //it: StatefulData<Weather>
When (it) {
is Success -> { // it.data: Weather
showTemperatureInBig(it.data.temperature)
}
is Loading -> { // it.loadingData: Any?
showLocationName(it.loadingData as? String)
showLoader()
}
is Error -> { // it.throwable: Throwable
showErrorMessage()
log(it.throwable)
}
}
})
}
}
28. class WeatherDashboardActivity : Activity()
private fun observeWeather() {
viewModel.weather.observe(this, Observer { //it: StatefulData<Weather>
When (it) {
is Success -> { // it.data: Weather
showTemperatureInBig(it.data.temperature)
}
is Loading -> { // it.loadingData: Any?
showLocationName(it.loadingData as? String)
showLoader()
}
is Error -> { // it.throwable: Throwable
showErrorMessage()
log(it.throwable)
}
}
})
}
}
29. class WeatherDashboardActivity : Activity()
private fun observeWeather() {
viewModel.weather.observe(this, Observer { //it: StatefulData<Weather>
When (it) {
is Success -> { // it.data: Weather
showTemperatureInBig(it.data.temperature)
}
is Loading -> { // it.loadingData: Any?
showLocationName(it.loadingData as? String)
showLoader()
}
is Error -> { // it.throwable: Throwable
showErrorMessage()
log(it.throwable)
}
}
})
}
}
30. class WeatherDashboardActivity : Activity()
private fun observeWeather() {
viewModel.weather.observe(this, Observer { //it: StatefulData<Weather>
When (it) {
is Success -> { // it.data: Weather
showTemperatureInBig(it.data.temperature)
}
is Loading -> { // it.loadingData: Any?
showLocationName(it.loadingData as? String)
showLoader()
}
is Error -> { // it.throwable: Throwable
showErrorMessage()
log(it.throwable)
}
}
})
}
}
33. class WeatherDasboardActivity : Activity() {
private fun observeWeather() {
viewModel.weather.observe(this, Observer { //it: StatefulData<Weather>
When (it) {
is Success -> showTemperatureInBig(it.data.temperature) // it.data: Weather
is Loading -> {
showLocationName(it.loadingData as? String) // it.loadingData: Any?
showLoader()
}
is Error -> {
showErrorMessage()
log(it.throwable) // it.throwable: Throwable
}
}
})
}
}
35. 35
observeLoading, observeError
class WeatherDasboardActivity : Activity() {
private fun observeWeather() {
viewModel.weather.observeLoading(this, Observer { //it: Any?
showLocationName(it as? String)
showLoader()
})
}
}
class WeatherDasboardActivity : Activity() {
private fun observeWeather() {
viewModel.weather.observeError(this, Observer { //it: Throwable
showErrorMessage()
log(it)
})
}
}
37. class WeatherDasboardActivity : Activity() {
private fun observeWeather() {
viewModel.weather.observe(this, Observer { //it: StatefulData<Weather>
When (it) {
is Success -> showTemperatureInBig(it.data.temperature) // it.data: Weather
is Loading -> {
showLocationName(it.loadingData as? String) // it.loadingData: Any?
showLoader()
}
is Error -> {
showErrorMessage()
log(it.throwable) // it.throwable: Throwable
}
}
})
}
}
38. class WeatherDasboardActivity : Activity() {
private fun observeWeather() {
viewModel.weather.subscribe(this)
.onSuccess{ // it: Weather
showTemperatureInBig(it.temperature)
}
.onError{ // it: Throwable
showErrorMessage()
log(it)
}
.observe()
}
}
39. class WeatherDasboardActivity : Activity() {
private fun observeWeather() {
viewModel.weather.subscribe(this)
.onSuccess{ // it: Weather
showTemperatureInBig(it.temperature)
}
.onError{ // it: Throwable
showErrorMessage()
log(it)
}
.observe()
}
}
40. class WeatherDasboardActivity : Activity() {
private fun observeWeather() {
viewModel.weather.subscribe(this)
.onSuccess{ // it: Weather
showTemperatureInBig(it.temperature)
}
.onError{ // it: Throwable
showErrorMessage()
log(it)
}
.observe()
}
}
41. class WeatherDasboardActivity : Activity() {
private fun observeWeather() {
viewModel.weather.subscribe(this)
.onSuccess{ // it: Weather
showTemperatureInBig(it.temperature)
}
.onError{ // it: Throwable
showErrorMessage()
log(it)
}
.observe()
}
}
42. class WeatherDasboardActivity : Activity() {
private fun observeWeather() {
viewModel.weather.subscribe(this)
.onSuccess{ // it: Weather
showTemperatureInBig(it.temperature)
}
.onError{ // it: Throwable
showErrorMessage()
log(it)
}
.observe()
}
}
45. 45
Transformations.Map
class MyViewModel() : ViewModel() {
val temperature: StatefulLiveData<Temperature> = repo.weather.map { // it : Weather
It.temperature ^map
}
}
Transformations.SwitchMap
class MyViewModel() : ViewModel() {
val radarImage: StatefulLiveData<List<RadarImage>> =
repo.weather.switchMap { // it : Weather
repo.getRadarImages(it.latLng) // ^switchMap
}
}
46. 46
class MyViewModel() : ViewModel() {
val temperature: StatefulLiveData<Temperature> =
repo.weather.map(myCoroutineScope) { // it : Weather
It.temperature ^map
}
}
Transformations.MapWithCoroutine
49. 49
typealias MediatorStatefulLiveData<T> = MediatorLiveData<StatefulData<T>>
StatefulLiveDataMediator
class MyViewModel() : ViewModel() {
val fusedForecast = MediatorStatefulLiveData<Forecast>().apply {
addSource(repo.todayForecast) { // it: Forecast
// handle data
}
addSource(repo.tomorrowForecast) { // it: StatefulData<Forecast>
// handle stateful data
}
}
}
52. 52
mapToLiveData
class WeatherDasboardViewModel : ViewModel() {
private val vanillaWeather: LiveData<Weather> = repo.weather
.mapToLiveData(errorMapFunction = { // it : Throwable
return Weather()
})
}
fun <T> StatefulLiveData<T>.mapToLiveData(
errorMapFunction: (Throwable) -> T? = { _ -> null },
loadingMapFunction: (Any?) -> T? = { _ -> null },
fallbackMapFunction: () -> T? = { null }
): LiveData<T>
57. 57
class WeatherDasboardViewModel : ViewModel() {
private val mutableWeather = MutableStatefulLiveData<Weather>()
private fun setWeatherLoading() {
mutableWeather.putLoading() //loadingData = null
mutableWeather.putLoading(”Please wait...”) // loadingData: String
mutableWeather.putLoading(53) // loadingData: Int - show % progress
mutableWeather.putLoading(oldWeather) // loadingData: Weather
}
}
loadingData
58. 58
class WeatherDasboardViewModel : ViewModel() {
private val mutableWeather = MutableStatefulLiveData<Weather>()
private fun setWeatherLoading() {
mutableWeather.putLoading() //loadingData = null
mutableWeather.putLoading(”Please wait...”) // loadingData: String
mutableWeather.putLoading(53) // loadingData: Int - show % progress
mutableWeather.putLoading(oldWeather) // loadingData: Weather
}
}
loadingData
59. 59
class WeatherDasboardViewModel : ViewModel() {
private val mutableWeather = MutableStatefulLiveData<Weather>()
private fun setWeatherLoading() {
mutableWeather.putLoading() //loadingData = null
mutableWeather.putLoading(”Please wait...”) // loadingData: String
mutableWeather.putLoading(53) // loadingData: Int - show % progress
mutableWeather.putLoading(oldWeather) // loadingData: Weather
}
}
loadingData
60. 60
class WeatherDasboardViewModel : ViewModel() {
private val mutableWeather = MutableStatefulLiveData<Weather>()
private fun setWeatherLoading() {
mutableWeather.putLoading() //loadingData = null
mutableWeather.putLoading(”Please wait...”) // loadingData: String
mutableWeather.putLoading(53) // loadingData: Int - show % progress
mutableWeather.putLoading(oldWeather) // loadingData: Weather
}
}
loadingData
67. 67
class FirestoreRepo {
private val firestoreDb: FirebaseFirestore = … // init FirebaseFirestore
fun getUser(userId: String) : Task<User> {
val getUserTask = firestoreDb
.collection(“Users”)
.whereEqualTo(“userId”, userId)
return getUserTask
}
}
Task<T>.toStatefulLiveData
68. 68
class FirestoreRepo {
private val firestoreDb: FirebaseFirestore = … // init FirebaseFirestore
fun getUser(userId: String) : StatefulLiveData<User> {
val getUserTask = firestoreDb
.collection(“Users”)
.whereEqualTo(“userId”, userId)
return getUserTask.toStatefulLiveData()
}
}
Task<T>.toStatefulLiveData
69. 69
Task<T>.toStatefulLiveData
class FirestoreRepo {
private val firestoreDb: FirebaseFirestore = … // init FirebaseFirestore
fun getUser(userId: String) : StatefulLiveData<Account> {
val getUserTask = firestoreDb
.collection(“Users”)
.whereEqualTo(“userId”, userId)
return getUserTask.toStatefulLiveData { // User: task result
val userAccount: Account = … // map User -> Account
userAccount ^toStatefulLiveData
}
}
}
72. 72
class LoadingWithPercent(val percent: Int) : StatefulData<Int>()
class WeatherDashboardActivity : Activity() {
private fun observeWeather() {
viewModel.weather.observe(this, Observer { //it: StatefulData<Weather>
When (it) {
is ... -> {...}
is LoadingWithPercent -> {
showProgress(it.percent) // it.percent: Int
}
}
})
}
}
73. 73
class LoadingWithPercent(val percent: Int) : StatefulData<Int>()
fun <T> MutableStatefulLiveData<T>.putLoadingWithPercent(percent: Int) {...}
fun <T> StatefulLiveData<T>.observeLoadingWithPercent(owner: LifecycleOwner,
observer: Observer<in Any?>): StatefulLiveData<T> {...}
74. 74
Event bus Callback listener Rx StatefulLiveData
Life cycle aware X X X
Low learning
curve
X
Lean X X
Data retention X X
Memory leaks
free
X X X