SlideShare ist ein Scribd-Unternehmen logo
1 von 76
Downloaden Sie, um offline zu lesen
1
LiveData
on Steroids
Taking LiveData to the next level
Giora Shevach &
Shahar Ben Moshe
LiveData<>
4
● Observable data holder
LiveData (vanilla)
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
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
7
8
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
}
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
}
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
}
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
}
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
}
LiveData - where it falls short
15
WeatherDashboardActivity
viewModel.weather.observe(...)
WeatherDashboardViewModel
val weather: LiveData<Weather> = weatherRepo.weather
WeatherRepo
val weather = MutableLiveData<Weather>()
fun fetchNewWeather() {
// do some network stuff…
weather.value = networkResponse.data
}
AppLifecycleObserver
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun onForeground() {
weatherRepo.fetchNewWeather()
}
val weatherError = MutableLiveData<Exception>()
val weatherError = weatherRepo.weatherError
viewModel.weatherError.observe(...)
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()
}
}
}
})
})
})
Stateful.Live.Data
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
❏ 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
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
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
StatefulLiveData
LiveData<StatefulData<T>>
typealias StatefulLiveData<T> = LiveData<StatefulData<T>>
typealias MutableStatefulLiveData<T> = MutableLiveData<StatefulData<T>>
typealias MediatorStatefulLiveData<T> = MediatorLiveData<StatefulData<T>>
WeatherDashboardActivity
viewModel.weather.observe(...)
WeatherDashboardViewModel
val weather: LiveData<Weather> = weatherRepo.weather
WeatherRepo
val weather = MutableLiveData<Weather>()
fun fetchNewWeather() {
// do some network stuff…
weather.value = networkResponse.data
}
AppLifecycleObserver
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun onForeground() {
weatherRepo.fetchNewWeather()
}
val weatherError= MutableLiveData<Exception>()
val weatherError= weatherRepo.weatherError
viewModel.weatherError.observe(...)
WeatherDashboardActivity
viewModel.weather.observe(...)
WeatherDashboardViewModel
val weather: StatefulLiveData<Weather> =
weatherRepo.weather
WeatherRepo
val weather = MutableStatefulLiveData<Weather>()
fun fetchNewWeather() {
// do some network stuff…
weather.value = networkResponse.data
}
AppLifecycleObserver
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun onForeground() {
weatherRepo.fetchNewWeather()
}
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)
}
}
})
}
}
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)
}
}
})
}
}
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)
}
}
})
}
}
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)
}
}
})
}
}
31
It’s
Alive!!
(Data)
Simple State Observation
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
}
}
})
}
}
34
observeSuccess
class WeatherDasboardActivity : Activity() {
private fun observeWeather() {
viewModel.weather.observeSuccess(this, Observer { //it: Weather
showTemperatureInBig(it.temperature)
})
}
}
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)
})
}
}
Flexible Observation
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
}
}
})
}
}
class WeatherDasboardActivity : Activity() {
private fun observeWeather() {
viewModel.weather.subscribe(this)
.onSuccess{ // it: Weather
showTemperatureInBig(it.temperature)
}
.onError{ // it: Throwable
showErrorMessage()
log(it)
}
.observe()
}
}
class WeatherDasboardActivity : Activity() {
private fun observeWeather() {
viewModel.weather.subscribe(this)
.onSuccess{ // it: Weather
showTemperatureInBig(it.temperature)
}
.onError{ // it: Throwable
showErrorMessage()
log(it)
}
.observe()
}
}
class WeatherDasboardActivity : Activity() {
private fun observeWeather() {
viewModel.weather.subscribe(this)
.onSuccess{ // it: Weather
showTemperatureInBig(it.temperature)
}
.onError{ // it: Throwable
showErrorMessage()
log(it)
}
.observe()
}
}
class WeatherDasboardActivity : Activity() {
private fun observeWeather() {
viewModel.weather.subscribe(this)
.onSuccess{ // it: Weather
showTemperatureInBig(it.temperature)
}
.onError{ // it: Throwable
showErrorMessage()
log(it)
}
.observe()
}
}
class WeatherDasboardActivity : Activity() {
private fun observeWeather() {
viewModel.weather.subscribe(this)
.onSuccess{ // it: Weather
showTemperatureInBig(it.temperature)
}
.onError{ // it: Throwable
showErrorMessage()
log(it)
}
.observe()
}
}
44
Transformations.Map
class MyViewModel() : ViewModel() {
val temperature: StatefulLiveData<Temperature> = repo.weather.map { // it : Weather
It.temperature ^map
}
}
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
class MyViewModel() : ViewModel() {
val temperature: StatefulLiveData<Temperature> =
repo.weather.map(myCoroutineScope) { // it : Weather
It.temperature ^map
}
}
Transformations.MapWithCoroutine
Stateful.Live.Data.Mediators
48
typealias MediatorStatefulLiveData<T> = MediatorLiveData<StatefulData<T>>
StatefulLiveDataMediator
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
}
}
}
51
class WeatherDashboardViewModel : ViewModel() {
private val vanillaWeather: LiveData<Weather> =
repo.weather.mapToLiveData()
}
mapToLiveData
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>
Put
54
Put
fun <T> MutableStatefulLiveData<T>.putData(data: T)
fun <T> MutableStatefulLiveData<T>.putLoading(loadingData: Any?)? = null)
fun <T> MutableStatefulLiveData<T>.putError(error: Throwable)
55
Put
fun <T> MutableStatefulLiveData<T>.putData(data: T)
fun <T> MutableStatefulLiveData<T>.putLoading(loadingFunction: (() -> Any?)? = null)
fun <T> MutableStatefulLiveData<T>.putError(error: Throwable)
loadingData
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
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
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
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
61
fun <T> LiveData<T>.observeOnce(
observer: Observer<T>,
retainForLoadingState: Boolean = true)
observeOnce
63
Stateful.Live.Data.Call.Adapter
fun createClient(baseUrl: String): Retrofit {
return Retrofit
.Builder()
.baseUrl(baseUrl)
.addCallAdapterFactory(StatefulLiveDataCallAdapterFactory.create())
.build()
}
64
Stateful.Live.Data.Call.Adapter
fun createClient(baseUrl: String): Retrofit {
return Retrofit
.Builder()
.baseUrl(baseUrl)
.addCallAdapterFactory(StatefulLiveDataCallAdapterFactory.create())
.build()
}
65
Stateful.Live.Data.Call.Adapter
fun createClient(baseUrl: String): Retrofit {
return Retrofit
.Builder()
.baseUrl(baseUrl)
.addCallAdapterFactory(StatefulLiveDataCallAdapterFactory.create())
.build()
}
interface MyRetrofitService {
@GET("weather/current")
fun getWeather(): Call<Weather>
}
66
Stateful.Live.Data.Call.Adapter
fun createClient(baseUrl: String): Retrofit {
return Retrofit
.Builder()
.baseUrl(baseUrl)
.addCallAdapterFactory(StatefulLiveDataCallAdapterFactory.create())
.build()
}
interface MyRetrofitService {
@GET("weather/current")
fun getWeather(): StatefulLiveData<Weather>
}
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
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
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
}
}
}
Extendibility
71
class LoadingWithPercent(val percent: Int) : StatefulData<Int>()
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
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
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
75
Use the right tool for the job
(or make one if it doesn’t exist)
76
Got get Stateful, It’s Live!
https://github.com/climacell/statefullivedata
Thank you!

Weitere ähnliche Inhalte

Was ist angesagt?

Was ist angesagt? (19)

Rxjs marble-testing
Rxjs marble-testingRxjs marble-testing
Rxjs marble-testing
 
The Ring programming language version 1.6 book - Part 70 of 189
The Ring programming language version 1.6 book - Part 70 of 189The Ring programming language version 1.6 book - Part 70 of 189
The Ring programming language version 1.6 book - Part 70 of 189
 
Drools 6.0 (CamelOne 2013)
Drools 6.0 (CamelOne 2013)Drools 6.0 (CamelOne 2013)
Drools 6.0 (CamelOne 2013)
 
The Ring programming language version 1.5.1 book - Part 64 of 180
The Ring programming language version 1.5.1 book - Part 64 of 180The Ring programming language version 1.5.1 book - Part 64 of 180
The Ring programming language version 1.5.1 book - Part 64 of 180
 
Latest java
Latest javaLatest java
Latest java
 
Swift Montevideo Meetup - iPhone, una herramienta medica
Swift Montevideo Meetup - iPhone, una herramienta medicaSwift Montevideo Meetup - iPhone, una herramienta medica
Swift Montevideo Meetup - iPhone, una herramienta medica
 
Analytics with Spark
Analytics with SparkAnalytics with Spark
Analytics with Spark
 
Managing parallelism using coroutines
Managing parallelism using coroutinesManaging parallelism using coroutines
Managing parallelism using coroutines
 
Solid principles in practice the clean architecture - Droidcon Italy
Solid principles in practice the clean architecture - Droidcon ItalySolid principles in practice the clean architecture - Droidcon Italy
Solid principles in practice the clean architecture - Droidcon Italy
 
Test
TestTest
Test
 
The Ring programming language version 1.8 book - Part 74 of 202
The Ring programming language version 1.8 book - Part 74 of 202The Ring programming language version 1.8 book - Part 74 of 202
The Ring programming language version 1.8 book - Part 74 of 202
 
droidparts
droidpartsdroidparts
droidparts
 
JS Fest 2019. Anjana Vakil. Serverless Bebop
JS Fest 2019. Anjana Vakil. Serverless BebopJS Fest 2019. Anjana Vakil. Serverless Bebop
JS Fest 2019. Anjana Vakil. Serverless Bebop
 
java assignment
java assignmentjava assignment
java assignment
 
The Ring programming language version 1.7 book - Part 72 of 196
The Ring programming language version 1.7 book - Part 72 of 196The Ring programming language version 1.7 book - Part 72 of 196
The Ring programming language version 1.7 book - Part 72 of 196
 
Android Architecture - Khoa Tran
Android Architecture -  Khoa TranAndroid Architecture -  Khoa Tran
Android Architecture - Khoa Tran
 
The Ring programming language version 1.9 book - Part 72 of 210
The Ring programming language version 1.9 book - Part 72 of 210The Ring programming language version 1.9 book - Part 72 of 210
The Ring programming language version 1.9 book - Part 72 of 210
 
Practical RxJava for Android
Practical RxJava for AndroidPractical RxJava for Android
Practical RxJava for Android
 
Compose Async with RxJS
Compose Async with RxJSCompose Async with RxJS
Compose Async with RxJS
 

Ähnlich wie LiveData on Steroids - Giora Shevach + Shahar Ben Moshe, Climacell

Ähnlich wie LiveData on Steroids - Giora Shevach + Shahar Ben Moshe, Climacell (20)

MVI - Managing State The Kotlin Way
MVI - Managing State The Kotlin WayMVI - Managing State The Kotlin Way
MVI - Managing State The Kotlin Way
 
DWR, Hibernate and Dojo.E - A Tutorial
DWR, Hibernate and Dojo.E - A TutorialDWR, Hibernate and Dojo.E - A Tutorial
DWR, Hibernate and Dojo.E - A Tutorial
 
Effective Android Data Binding
Effective Android Data BindingEffective Android Data Binding
Effective Android Data Binding
 
Presentation Android Architecture Components
Presentation Android Architecture ComponentsPresentation Android Architecture Components
Presentation Android Architecture Components
 
Android architecture components - how they fit in good old architectural patt...
Android architecture components - how they fit in good old architectural patt...Android architecture components - how they fit in good old architectural patt...
Android architecture components - how they fit in good old architectural patt...
 
Architecture components - IT Talk
Architecture components - IT TalkArchitecture components - IT Talk
Architecture components - IT Talk
 
Survive the lifecycle
Survive the lifecycleSurvive the lifecycle
Survive the lifecycle
 
Working effectively with ViewModels and TDD - UA Mobile 2019
Working effectively with ViewModels and TDD - UA Mobile 2019Working effectively with ViewModels and TDD - UA Mobile 2019
Working effectively with ViewModels and TDD - UA Mobile 2019
 
package org dev
package org devpackage org dev
package org dev
 
Package org dev
Package org devPackage org dev
Package org dev
 
iOS Talks 6: Unit Testing
iOS Talks 6: Unit TestingiOS Talks 6: Unit Testing
iOS Talks 6: Unit Testing
 
Android Architecture Components
Android Architecture ComponentsAndroid Architecture Components
Android Architecture Components
 
Slightly Advanced Android Wear ;)
Slightly Advanced Android Wear ;)Slightly Advanced Android Wear ;)
Slightly Advanced Android Wear ;)
 
Async JavaScript in ES7
Async JavaScript in ES7Async JavaScript in ES7
Async JavaScript in ES7
 
Introduction to CQRS and Event Sourcing
Introduction to CQRS and Event SourcingIntroduction to CQRS and Event Sourcing
Introduction to CQRS and Event Sourcing
 
Saving lives with rx java
Saving lives with rx javaSaving lives with rx java
Saving lives with rx java
 
Android Jetpack: ViewModel and Testing
Android Jetpack: ViewModel and TestingAndroid Jetpack: ViewModel and Testing
Android Jetpack: ViewModel and Testing
 
Data in Motion: Streaming Static Data Efficiently
Data in Motion: Streaming Static Data EfficientlyData in Motion: Streaming Static Data Efficiently
Data in Motion: Streaming Static Data Efficiently
 
From mvc to redux: 停看聽
From mvc to redux: 停看聽From mvc to redux: 停看聽
From mvc to redux: 停看聽
 
How to become an Android dev starting from iOS (and vice versa)
How to become an Android dev starting from iOS (and vice versa)How to become an Android dev starting from iOS (and vice versa)
How to become an Android dev starting from iOS (and vice versa)
 

Mehr von DroidConTLV

Mehr von DroidConTLV (20)

Mobile Development in the Information Age - Yossi Elkrief, Nike
Mobile Development in the Information Age - Yossi Elkrief, NikeMobile Development in the Information Age - Yossi Elkrief, Nike
Mobile Development in the Information Age - Yossi Elkrief, Nike
 
Doing work in the background - Darryn Campbell, Zebra Technologies
Doing work in the background - Darryn Campbell, Zebra TechnologiesDoing work in the background - Darryn Campbell, Zebra Technologies
Doing work in the background - Darryn Campbell, Zebra Technologies
 
No more video loss - Alex Rivkin, Motorola Solutions
No more video loss - Alex Rivkin, Motorola SolutionsNo more video loss - Alex Rivkin, Motorola Solutions
No more video loss - Alex Rivkin, Motorola Solutions
 
Mobile at Scale: from startup to a big company - Dor Samet, Booking.com
Mobile at Scale: from startup to a big company - Dor Samet, Booking.comMobile at Scale: from startup to a big company - Dor Samet, Booking.com
Mobile at Scale: from startup to a big company - Dor Samet, Booking.com
 
MVVM In real life - Lea Cohen Tannoudji, Lightricks
MVVM In real life - Lea Cohen Tannoudji, LightricksMVVM In real life - Lea Cohen Tannoudji, Lightricks
MVVM In real life - Lea Cohen Tannoudji, Lightricks
 
Best Practices for Using Mobile SDKs - Lilach Wagner, SafeDK (AppLovin)
Best Practices for Using Mobile SDKs - Lilach Wagner, SafeDK (AppLovin)Best Practices for Using Mobile SDKs - Lilach Wagner, SafeDK (AppLovin)
Best Practices for Using Mobile SDKs - Lilach Wagner, SafeDK (AppLovin)
 
Building Apps with Flutter - Hillel Coren, Invoice Ninja
Building Apps with Flutter - Hillel Coren, Invoice NinjaBuilding Apps with Flutter - Hillel Coren, Invoice Ninja
Building Apps with Flutter - Hillel Coren, Invoice Ninja
 
New Android Project: The Most Important Decisions - Vasiliy Zukanov
New Android Project: The Most Important Decisions - Vasiliy ZukanovNew Android Project: The Most Important Decisions - Vasiliy Zukanov
New Android Project: The Most Important Decisions - Vasiliy Zukanov
 
Designing a Design System - Shai Mishali, Gett
Designing a Design System - Shai Mishali, GettDesigning a Design System - Shai Mishali, Gett
Designing a Design System - Shai Mishali, Gett
 
The Mighty Power of the Accessibility Service - Guy Griv, Pepper
The Mighty Power of the Accessibility Service - Guy Griv, PepperThe Mighty Power of the Accessibility Service - Guy Griv, Pepper
The Mighty Power of the Accessibility Service - Guy Griv, Pepper
 
Kotlin Multiplatform in Action - Alexandr Pogrebnyak - IceRockDev
Kotlin Multiplatform in Action - Alexandr Pogrebnyak - IceRockDevKotlin Multiplatform in Action - Alexandr Pogrebnyak - IceRockDev
Kotlin Multiplatform in Action - Alexandr Pogrebnyak - IceRockDev
 
Flutter State Management - Moti Bartov, Tikal
Flutter State Management - Moti Bartov, TikalFlutter State Management - Moti Bartov, Tikal
Flutter State Management - Moti Bartov, Tikal
 
Reactive UI in android - Gil Goldzweig Goldbaum, 10bis
Reactive UI in android - Gil Goldzweig Goldbaum, 10bisReactive UI in android - Gil Goldzweig Goldbaum, 10bis
Reactive UI in android - Gil Goldzweig Goldbaum, 10bis
 
Fun with flutter animations - Divyanshu Bhargava, GoHighLevel
Fun with flutter animations - Divyanshu Bhargava, GoHighLevelFun with flutter animations - Divyanshu Bhargava, GoHighLevel
Fun with flutter animations - Divyanshu Bhargava, GoHighLevel
 
DroidconTLV 2019
DroidconTLV 2019DroidconTLV 2019
DroidconTLV 2019
 
Ok google, it's time to bot! - Hadar Franco, Albert + Stav Levi, Monday
Ok google, it's time to bot! - Hadar Franco, Albert + Stav Levi, MondayOk google, it's time to bot! - Hadar Franco, Albert + Stav Levi, Monday
Ok google, it's time to bot! - Hadar Franco, Albert + Stav Levi, Monday
 
Introduction to React Native - Lev Vidrak, Wix
Introduction to React Native - Lev Vidrak, WixIntroduction to React Native - Lev Vidrak, Wix
Introduction to React Native - Lev Vidrak, Wix
 
Bang-Bang, you have been hacked - Yonatan Levin, KolGene
Bang-Bang, you have been hacked - Yonatan Levin, KolGeneBang-Bang, you have been hacked - Yonatan Levin, KolGene
Bang-Bang, you have been hacked - Yonatan Levin, KolGene
 
Educating your app – adding ML edge to your apps - Maoz Tamir
Educating your app – adding ML edge to your apps - Maoz TamirEducating your app – adding ML edge to your apps - Maoz Tamir
Educating your app – adding ML edge to your apps - Maoz Tamir
 
Constraint-ly motion - making your app dance - John Hoford, Google
Constraint-ly motion - making your app dance - John Hoford, GoogleConstraint-ly motion - making your app dance - John Hoford, Google
Constraint-ly motion - making your app dance - John Hoford, Google
 

Kürzlich hochgeladen

Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
vu2urc
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
?#DUbAI#??##{{(☎️+971_581248768%)**%*]'#abortion pills for sale in dubai@
 

Kürzlich hochgeladen (20)

A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
HTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation StrategiesHTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation Strategies
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Advantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessAdvantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your Business
 

LiveData on Steroids - Giora Shevach + Shahar Ben Moshe, Climacell

  • 1. 1 LiveData on Steroids Taking LiveData to the next level Giora Shevach & Shahar Ben Moshe
  • 2.
  • 4. 4 ● Observable data holder LiveData (vanilla)
  • 5. 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
  • 7. 7
  • 8. 8
  • 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 }
  • 14. LiveData - where it falls short
  • 15. 15 WeatherDashboardActivity viewModel.weather.observe(...) WeatherDashboardViewModel val weather: LiveData<Weather> = weatherRepo.weather WeatherRepo val weather = MutableLiveData<Weather>() fun fetchNewWeather() { // do some network stuff… weather.value = networkResponse.data } AppLifecycleObserver @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) fun onForeground() { weatherRepo.fetchNewWeather() } val weatherError = MutableLiveData<Exception>() val weatherError = weatherRepo.weatherError viewModel.weatherError.observe(...)
  • 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() } } } }) }) })
  • 18.
  • 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
  • 23. StatefulLiveData LiveData<StatefulData<T>> typealias StatefulLiveData<T> = LiveData<StatefulData<T>> typealias MutableStatefulLiveData<T> = MutableLiveData<StatefulData<T>> typealias MediatorStatefulLiveData<T> = MediatorLiveData<StatefulData<T>>
  • 24.
  • 25. WeatherDashboardActivity viewModel.weather.observe(...) WeatherDashboardViewModel val weather: LiveData<Weather> = weatherRepo.weather WeatherRepo val weather = MutableLiveData<Weather>() fun fetchNewWeather() { // do some network stuff… weather.value = networkResponse.data } AppLifecycleObserver @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) fun onForeground() { weatherRepo.fetchNewWeather() } val weatherError= MutableLiveData<Exception>() val weatherError= weatherRepo.weatherError viewModel.weatherError.observe(...)
  • 26. WeatherDashboardActivity viewModel.weather.observe(...) WeatherDashboardViewModel val weather: StatefulLiveData<Weather> = weatherRepo.weather WeatherRepo val weather = MutableStatefulLiveData<Weather>() fun fetchNewWeather() { // do some network stuff… weather.value = networkResponse.data } AppLifecycleObserver @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) fun onForeground() { weatherRepo.fetchNewWeather() }
  • 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 } } }) } }
  • 34. 34 observeSuccess class WeatherDasboardActivity : Activity() { private fun observeWeather() { viewModel.weather.observeSuccess(this, Observer { //it: Weather showTemperatureInBig(it.temperature) }) } }
  • 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() } }
  • 43.
  • 44. 44 Transformations.Map class MyViewModel() : ViewModel() { val temperature: StatefulLiveData<Temperature> = repo.weather.map { // it : Weather It.temperature ^map } }
  • 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
  • 48. 48 typealias MediatorStatefulLiveData<T> = MediatorLiveData<StatefulData<T>> StatefulLiveDataMediator
  • 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 } } }
  • 50.
  • 51. 51 class WeatherDashboardViewModel : ViewModel() { private val vanillaWeather: LiveData<Weather> = repo.weather.mapToLiveData() } mapToLiveData
  • 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>
  • 53. Put
  • 54. 54 Put fun <T> MutableStatefulLiveData<T>.putData(data: T) fun <T> MutableStatefulLiveData<T>.putLoading(loadingData: Any?)? = null) fun <T> MutableStatefulLiveData<T>.putError(error: Throwable)
  • 55. 55 Put fun <T> MutableStatefulLiveData<T>.putData(data: T) fun <T> MutableStatefulLiveData<T>.putLoading(loadingFunction: (() -> Any?)? = null) fun <T> MutableStatefulLiveData<T>.putError(error: Throwable)
  • 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
  • 61. 61 fun <T> LiveData<T>.observeOnce( observer: Observer<T>, retainForLoadingState: Boolean = true) observeOnce
  • 62.
  • 63. 63 Stateful.Live.Data.Call.Adapter fun createClient(baseUrl: String): Retrofit { return Retrofit .Builder() .baseUrl(baseUrl) .addCallAdapterFactory(StatefulLiveDataCallAdapterFactory.create()) .build() }
  • 64. 64 Stateful.Live.Data.Call.Adapter fun createClient(baseUrl: String): Retrofit { return Retrofit .Builder() .baseUrl(baseUrl) .addCallAdapterFactory(StatefulLiveDataCallAdapterFactory.create()) .build() }
  • 65. 65 Stateful.Live.Data.Call.Adapter fun createClient(baseUrl: String): Retrofit { return Retrofit .Builder() .baseUrl(baseUrl) .addCallAdapterFactory(StatefulLiveDataCallAdapterFactory.create()) .build() } interface MyRetrofitService { @GET("weather/current") fun getWeather(): Call<Weather> }
  • 66. 66 Stateful.Live.Data.Call.Adapter fun createClient(baseUrl: String): Retrofit { return Retrofit .Builder() .baseUrl(baseUrl) .addCallAdapterFactory(StatefulLiveDataCallAdapterFactory.create()) .build() } interface MyRetrofitService { @GET("weather/current") fun getWeather(): StatefulLiveData<Weather> }
  • 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 } } }
  • 71. 71 class LoadingWithPercent(val percent: Int) : StatefulData<Int>()
  • 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
  • 75. 75 Use the right tool for the job (or make one if it doesn’t exist)
  • 76. 76 Got get Stateful, It’s Live! https://github.com/climacell/statefullivedata Thank you!