fal
08/17/2019, 6:53 PMprivate val _navigateToSpeakerAction = MutableLiveData<Event<SpeakerId>>()
val navigateToSpeakerAction: LiveData<Event<SpeakerId>> = _navigateToSpeakerAction
it's so goddamn ugly.. i think i even prefer just a standalone getterMark Murphy
08/17/2019, 7:20 PMprivate val navAction = MutableLiveData<Event<SpeakerId>>()
val navigateToSpeakerAction: LiveData<Event<SpeakerId>> = navAction
There's also the custom getter option:
private val _navigateToSpeakerAction = MutableLiveData<Event<SpeakerId>>()
val navigateToSpeakerAction: LiveData<Event<SpeakerId>>
get() = _navigateToSpeakerAction
If you are not referencing the MutableLiveData
(as that type) from many places, you could go with a single property and a cast:
val navigateToSpeakerAction: LiveData<Event<SpeakerId>> = MutableLiveData<Event<SpeakerId>>()
// somewhere later
(navigateToSpeakerAction as MutableLiveData<Event<SpeakerId>>).value = TODO("something")
On one project that I worked on, a developer had us extend all our ViewModel
classes from a BaseViewModel
that had protected extension functions on LiveData
that would perform that casting for us:
protected fun <T> LiveData<T>.update(value: T?) {
(this as MutableLiveData).value = value
}
Then, the BaseViewModel
subclasses could just have:
navigateToSpeakerAction.update(TODO("something"))
So, there are a variety of options. None are the prettiest, but perhaps one looks better to you than the others...fal
08/17/2019, 7:25 PMPablichjenkov
08/17/2019, 7:27 PMMark Murphy
08/17/2019, 7:31 PMLiveData
syntactical overhead. It does require the common base class though, so the extension functions can only be used from the "owners" of the MutableLiveData
.
I keep thinking that there could be some sort of property delegate approach here, but I have never figured one out.fal
08/17/2019, 7:41 PMAdam Powell
08/17/2019, 8:03 PMliveData
builder in the ktx libraries. It's more or less a coroutine-based Observable.create
for LiveData.MutableLiveData
is basically a BehaviorSubject
, it has fairly limited use and the primary one is to bridge into streaming/observable code from outside. The pattern that started this thread is exceedingly rare outside of getting started cases.Mark Murphy
08/17/2019, 8:12 PMliveData {}
is in an alpha release right now, which means that it is unavailable to projects that avoid pre-release dependencies. I think MutableLiveData
is fairly popular, though some of those use cases will be able to be replaced with liveData {}
in time.Adam Powell
08/17/2019, 8:16 PMliveData {}
is certainly the direction in which we're headed. There are a few Flow interop extensions that just went in recently based on it too.Mark Murphy
08/17/2019, 8:17 PMAdam Powell
08/17/2019, 8:17 PMMark Murphy
08/17/2019, 8:19 PMAdam Powell
08/17/2019, 8:19 PMliveData {}
🙂 I imagine we're going to put it up for stable channel once some folks are back from summer vacationsPablichjenkov
08/17/2019, 8:38 PMcoworkers
😸
Anyways, what I found a bit verbose is the fact of mapping and re-publishing my Rx-Events to liveData events, specially when dealing with Exception -> Errors. Hopefully this liveData {...}
directive helps on that.
@fal, you said it right, when dealing with coroutines you can use a ConflatedBroadcastChannel or regular channel in the last mile. I wish some influencer makes a good standard sample but as of now we just have that.
There are many designs, one could be having a coroutineScope
living in your View, this one setup with a dispatcher on the UI thread. Then have a channel living in your View too and call channel.consumeEach()
under this coroutineScope. Assuming your ViewModel will be basically a coroutineScope then you can pass your channel as a SendChannel
and the ViewModel will put events into it.Adam Powell
08/17/2019, 8:44 PMConflatedBroadcastChannel
for the producer and .asFlow()
as the consumer that you get from [Mutable]LiveData. Flow makes a much better subscriber endpoint than raw channels do since you don't have to remember to close them.Pablichjenkov
08/17/2019, 8:51 PMfal
08/17/2019, 8:52 PMPablichjenkov
08/17/2019, 8:57 PMactor { ... }
builder a lot. It is obsolete now.
However, as Adam suggested Flow is cleaner for API designfal
08/17/2019, 8:59 PMPablichjenkov
08/17/2019, 9:03 PMAdam Powell
08/17/2019, 9:14 PMval disposable = launch {
try {
// implicit request(1) on initial collect
myFlow.collect {
onNext(it)
// implicit request(1) on return
// or throw CancellationException to cancel
}
onComplete()
} catch (ce: CancellationException) {
throw ce // pass cancellation through
} catch (t: Throwable) {
onError(t)
}
}
Pablichjenkov
08/17/2019, 9:19 PMFlow = FlowableNow I know where they picked the name from. Initially I thought they stole the term from AKKA-streams. Just in general, the Coroutine API is wider than RxJava, you can say that Flow is the Reactive-Stream side of it.
Adam Powell
08/17/2019, 9:35 PMjava.util.concurrent.Flow
🙂fal
08/17/2019, 9:45 PMPablichjenkov
08/17/2019, 9:46 PMAdam Powell
08/17/2019, 10:01 PMFlow<T>.asLiveData(): LiveData<T>
and LiveData<T>.asFlow(): Flow<T>
https://android-review.googlesource.com/c/platform/frameworks/support/+/1096457
https://android-review.googlesource.com/c/platform/frameworks/support/+/1098315rkeazor
08/18/2019, 10:52 AM