https://kotlinlang.org logo
#android
Title
# android
f

fal

08/17/2019, 6:53 PM
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

Mark Murphy

08/17/2019, 7:20 PM
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

fal

08/17/2019, 7:25 PM
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

Pablichjenkov

08/17/2019, 7:27 PM
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

Mark Murphy

08/17/2019, 7:31 PM
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

fal

08/17/2019, 7:41 PM
@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

Adam Powell

08/17/2019, 8:03 PM
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

Mark Murphy

08/17/2019, 8:12 PM
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

Adam Powell

08/17/2019, 8:16 PM
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

Mark Murphy

08/17/2019, 8:17 PM
Yep, and I'm looking forward to them! "The future is here, it's just not evenly distributed", and all that.
a

Adam Powell

08/17/2019, 8:17 PM
(also, hey @Mark Murphy, how've you been? 😁)
m

Mark Murphy

08/17/2019, 8:19 PM
(doing fine -- hope life is treating you well!)
a

Adam Powell

08/17/2019, 8:19 PM
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

Pablichjenkov

08/17/2019, 8:38 PM
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

Adam Powell

08/17/2019, 8:44 PM
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

Pablichjenkov

08/17/2019, 8:51 PM
Definitely Flow for better and cleaner.
f

fal

08/17/2019, 8:52 PM
@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

Pablichjenkov

08/17/2019, 8:57 PM
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

fal

08/17/2019, 8:59 PM
@Pablichjenkov does flow eliminate the need to use channels in most cases?
p

Pablichjenkov

08/17/2019, 9:03 PM
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

Adam Powell

08/17/2019, 9:14 PM
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

Pablichjenkov

08/17/2019, 9:19 PM
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

Adam Powell

08/17/2019, 9:35 PM
also
java.util.concurrent.Flow
🙂
f

fal

08/17/2019, 9:45 PM
Thanks a lot @Pablichjenkov @Adam Powell! I'll be diving into it very soon and this is all very helpful already 👍
👍 1
p

Pablichjenkov

08/17/2019, 9:46 PM
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

Adam Powell

08/17/2019, 10:01 PM
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

rkeazor

08/18/2019, 10:52 AM
Remember there is Transformations.switchMap ,Transformations.map , and MedatatorLiveData that all return LiveDatat object..