iex
02/25/2020, 3:33 PMprivate val dealerAction: Single<DealerAction> = carDealersRepo.selectedCarDealer()
.map {
it.toDealerAction()
}
val dealerButtonTitle: LiveData<String> = dealerAction
.map { it.toButtonTitle() }
.toObservable()
.toLiveData()
When I navigate to a new screen, I change carDealersRepo's underlaying data. When I navigate back, I want that this view model re-fetches selectedCarDealer
Zach Klippenstein (he/him) [MOD]
02/25/2020, 3:40 PMselectedCarDealer()
method won’t be executed again, but the single it returns should be subscribed to again.iex
02/25/2020, 3:40 PMiex
02/25/2020, 3:41 PMoverride fun selectedCarDealer(): Single<Optional<CarDealer>> =
(preferences.getObject(SELECTED_CAR_DEALER, CarDealer::class.java)?.let {
Optional.Some(it)
} ?: Optional.None).let{ Single.just(it) }
Zach Klippenstein (he/him) [MOD]
02/25/2020, 3:41 PMdealerAction = Single.defer { carDealersRepo.selectedCarDealer() }
iex
02/25/2020, 3:41 PMZach Klippenstein (he/him) [MOD]
02/25/2020, 3:41 PMSingle.just
.Zach Klippenstein (he/him) [MOD]
02/25/2020, 3:41 PMselectedCarDealer
method doesn’t magically get executed againiex
02/25/2020, 3:43 PMZach Klippenstein (he/him) [MOD]
02/25/2020, 3:44 PMselectedCarDealer
though, to get the behavior you’re expecting. Something like:
override fun selectedCarDealer(): Maybe<CarDealer> =
Maybe.fromCallable {
preferences.getObject(…)
}
iex
02/25/2020, 3:44 PMZach Klippenstein (he/him) [MOD]
02/25/2020, 3:45 PMjust
is usually a smell, since you usually want the computation performed on subscription.iex
02/25/2020, 3:45 PMjust
. If it was create
, it would work, I assumeZach Klippenstein (he/him) [MOD]
02/25/2020, 3:46 PMMaybe<Foo>
is semantically equivalent to Single<Optional<Foo>>
with less typing)iex
02/25/2020, 3:46 PMiex
02/25/2020, 3:48 PMnull
? in map
I get only the success valueiex
02/25/2020, 3:49 PMiex
02/25/2020, 3:51 PMMaybe
isn't made for that 😕iex
02/25/2020, 3:55 PMselectedCarDealer()
to this:
override fun selectedCarDealer(): Single<Optional<CarDealer>> =
fromCallable {
Optional.from(preferences.getObject(SELECTED_CAR_DEALER, CarDealer::class.java))
}
iex
02/25/2020, 3:56 PMselectedCarDealer()
🤔Zach Klippenstein (he/him) [MOD]
02/25/2020, 4:00 PMand how do I get notified about whether the result is a value orAh, then? innull
I get only the success valuemap
Single<Optional>>
is the way to goZach Klippenstein (he/him) [MOD]
02/25/2020, 4:06 PMfrom an architecture perspective this seems bad though. I shouldn’t have to depend on the implementation of selectedCarDealer()It’s pretty common for methods that return `Single`s that perform some computation to do that computation on subscription, because
Single
typically represents asynchronous computation. The fact that this particular method implementation doesn’t actually do anything asynchronous and always returns immediately is also an implementation detail, but it’s the more anomalous one.iex
02/25/2020, 4:09 PMZach Klippenstein (he/him) [MOD]
02/25/2020, 4:09 PMSingle
as the return type here implies that this function needs to do some asynchronous work (that’s the whole point of Single
). The way for Single
to do asynchronous work is by kicking it off on subscription, so it’s natural to expect that any work it does will be performed on subscription.Zach Klippenstein (he/him) [MOD]
02/25/2020, 4:13 PMI should not have to think about how an interface is implementedI agree. The point I’m trying (badly) to communicate, is the fact that your interface uses
Single
at all implies that work is being done on subscription, and not just when your method is called. That’s basically the contract of Single
.
That said, if you really don’t trust your implementations to follow this convention, you can just wrap it in a Single.defer
. But it would be better to make your implementation behave in a less surprising way to begin with, since this might trip up other callers in the future as well.iex
02/25/2020, 4:14 PM.just
Zach Klippenstein (he/him) [MOD]
02/25/2020, 4:14 PMjust
in tests all the time, it’s great for that. But in that case you’re hardcoding a value, not computing it from some real world resource.iex
02/25/2020, 4:15 PMiex
02/25/2020, 4:15 PMZach Klippenstein (he/him) [MOD]
02/25/2020, 4:15 PMjust
is fine.iex
02/25/2020, 4:16 PMZach Klippenstein (he/him) [MOD]
02/25/2020, 4:16 PMSingle
instead of Observable
?iex
02/25/2020, 4:16 PMiex
02/25/2020, 4:17 PMZach Klippenstein (he/him) [MOD]
02/25/2020, 4:17 PMwell, I get the reasoning of what you’re saying but it still doesn’t feel 100% clean from an architectural point of view. “Don’t make assumptions” etc. etc.Like Jake said, this is the default behavior for
Single
. Retrofit does this too: if you have a method that returns a Single
, the network request will be performed every time you subscribe to the single, not every time you call the method.iex
02/25/2020, 4:18 PMiex
02/25/2020, 4:19 PMiex
02/25/2020, 4:20 PMiex
02/25/2020, 4:20 PMiex
02/25/2020, 4:20 PMZach Klippenstein (he/him) [MOD]
02/25/2020, 4:24 PMthat’s not what I had in mind for the overall architectureThis is obviously off topic from your original question, but again… why not? Rx is short for “reactive”. Reactive means that code reacts to changes and external events – things like preferences changing. The advantage of reactive UI programming is that your UI will automatically update to reflect state changes. That said, maybe you’re not trying to build a reactive UI and only use RxJava as a concurrency library – and that’s fine too.
iex
02/25/2020, 4:25 PMiex
02/25/2020, 4:25 PMiex
02/25/2020, 4:26 PMiex
02/25/2020, 4:28 PM