Am I the only soul on earth that finds the whole L...
# android
f
Am I the only soul on earth that finds the whole LiveData encapsulation atrocious to the eyes? I obviously understand why we only let 'LiveData' vars accessible and not mutable ones, but it honestly kills me inside to have repeated fields where one has prefixed '_', e.g.:
Copy code
private val _navigateToSpeakerAction = MutableLiveData<Event<SpeakerId>>()
val navigateToSpeakerAction: LiveData<Event<SpeakerId>> = _navigateToSpeakerAction
it's so goddamn ugly.. i think i even prefer just a standalone getter
m
The underscore-and-duplicate-name convention is a convention. If you can keep track of stuff, you are not obligated to follow that convention:
Copy code
private val navAction = MutableLiveData<Event<SpeakerId>>()
val navigateToSpeakerAction: LiveData<Event<SpeakerId>> = navAction
There's also the custom getter option:
Copy code
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:
Copy code
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:
Copy code
protected fun <T> LiveData<T>.update(value: T?) {
    (this as MutableLiveData).value = value
}
Then, the
BaseViewModel
subclasses could just have:
Copy code
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...
πŸ‘ 3
f
I realize it's a convention, just not a very pretty one to me πŸ˜› but thanks for different alternatives! I actually find the last one you provided interesting
πŸ‘ 1
p
Don't feel alone. On my personal opinion we don't need LiveData. It just add ceremony to an RxStream or a Coroutine cluster.
m
That last one worked reasonably well and has the least per-
LiveData
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.
πŸ‘ 1
f
@Pablichjenkov on that note, I would love to have a full coroutine example app to mess around with. I'm very used to Rx, and while I realize we could replace LiveData with ConflatedBroadcastChannel & regular channels, I'm so new to it that I don't know how to wire them up... Still searching for that example app..
πŸ‘ 1
a
If you're used to Rx but still want to use LiveData as a last-mile for UI, check out the
liveData
builder in the ktx libraries. It's more or less a coroutine-based
Observable.create
for LiveData.
πŸ‘ 1
If you come from more of an Rx background,
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.
m
liveData {}
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.
a
Sure, but
liveData {}
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.
βœ… 1
m
Yep, and I'm looking forward to them! "The future is here, it's just not evenly distributed", and all that.
a
(also, hey @Mark Murphy, how've you been? 😁)
m
(doing fine -- hope life is treating you well!)
a
It is, thanks!
I mean, if someone says the MutableLiveData scoping dance is ugly I'm not going to argue, it's absolutely true and the official answer to that is
liveData {}
πŸ™‚ I imagine we're going to put it up for stable channel once some folks are back from summer vacations
πŸ‘ 1
βž• 2
p
You guys sound
coworkers
😸 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.
πŸ‘ 1
a
You'll get more or less equivalent patterns from
ConflatedBroadcastChannel
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.
πŸ‘ 2
p
Definitely Flow for better and cleaner.
f
@Pablichjenkov yep! I'm going to investigate channels further soon enough
@Adam Powell Thanks! I realize that livedata is similar to BehaviorSubject... Where I work I still use MVP with Rx (it works for our purposes and it's an old big project so refactoring is hard). For my projects, however, I want to explore the latest trends. Ideally I'd like to have a full coroutine/flow project but it's still not very clear to me how it all works and how it wires up together (hence the need for a complete example). Most examples out there are not very complete so it's hard for me to visualize what is the functional equivalent of the Rx way in certain scenarios on architecture
p
I have some code with that design I talked to you. Mainly influenced by my trauma with Actors. Unfortunately I used
actor { ... }
builder a lot. It is obsolete now. However, as Adam suggested Flow is cleaner for API design
f
@Pablichjenkov does flow eliminate the need to use channels in most cases?
p
It depends on your use case. They are both structured concurrency primitives. In the reactive theory Flow is a cold source while channel is more of a hot source. Some Flow Operators(Source -> Sink) uses channel internally to buffer, merge and many other good stuff you can do with it.
For a more comprehensive explanation about suspend functions, CoroutineScope/CoroutineContext, Channels and Flow I advice you follow Roman Elizarov on medium. Start reading his articles sorted by date. Of course staying tuned to this channel is golden too.
a
yeah Roman's stuff is great
if you already know Rx, Flow = Flowable, more or less
basically, here's your equivalents:
Copy code
val 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)
  }
}
p
Flow = Flowable
Now 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.
a
also
java.util.concurrent.Flow
πŸ™‚
f
Thanks a lot @Pablichjenkov @Adam Powell! I'll be diving into it very soon and this is all very helpful already πŸ‘
πŸ‘ 1
p
that’s right πŸ€“. The future sounds like it’s gonna be Flow party. Hopefully all frameworks implement pretty much the same concept and not miss-use the term to represent something totally different. at Francisco no problem at all
a
fwiw, recently merged into androidx libraries:
Flow<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/+/1098315
πŸ‘ 6
r
Remember there is Transformations.switchMap ,Transformations.map , and MedatatorLiveData that all return LiveDatat object..