Dec 6 | 7:25 PM |
Mark M. | has entered the room |
Mark M. | turned on guest access |
Dec 6 | 7:30 PM |
Aaron | has entered the room |
Dec 6 | 7:35 PM |
Aaron |
hey Mark.
|
Mark M. |
hello, Aaron!
|
Mark M. |
how can I help you today?
|
Aaron |
I've been switching over my networking to RxJava and I have numerous questions along those lines
|
Aaron |
this is going to be a wall of text, so sorry lol, but here goes:
|
Aaron |
View paste
(10 more lines)
|
Mark M. |
first, on the whole, my book's "Rx Basics" chapter is... not great
|
Aaron |
it was brief but helpful. I've used several other sources to complement it
|
Aaron |
(1) is really the only question that pertains to your book specifically
|
Mark M. |
at the same time, the RxJava documentation, IMHO, is... not great
|
Aaron |
that has been my impression so far
|
Mark M. |
so, with all that in mind, let's plow through your questions
|
Aaron |
OK
|
Mark M. |
ObservableOnSubscribe, despite the name, is not an Observable
|
Dec 6 | 7:40 PM |
Mark M. |
but you use one with things like Observable.create() to create an Observable
|
Mark M. |
the JavaDocs for Observable.create() reference cold observables: http://reactivex.io/RxJava/javadoc/io/reactivex...
|
Mark M. |
so, in the battle of the not-great written works, I'll stand by my not-great over their not-great :-)
|
Mark M. |
"so, the `ObservableEmitter` *is* the subscribed `Observer`, right?" -- not really
|
Mark M. |
ObservableEmitter is the way you deliver objects to observers
|
Mark M. |
there are some levels of indirection in here that is part of the great mystery that is the RxJava implementation
|
Aaron |
yeah...
|
Dec 6 | 7:45 PM |
Aaron |
so it's not really the Observer, but emitter.onNext() is still calling the Observer's onNext(), correct?
|
Mark M. |
I would phrase it more as "triggering" than calling, as I don't know what all is in between those two objects
|
Aaron |
OK
|
Mark M. |
"calling" to me implies that the emitter has a direct reference to the observer, and I don't know if that is the case or not
|
Aaron |
got it
|
Mark M. |
for #3, I think that you're basically correct, though your example (flatMap() to single-String Observables) isn't exactly a recommended practice
|
Mark M. |
the language that you cite for #4 is mostly drawing a comparison between map() and flatMap(), I suspect
|
Mark M. |
to Rx-ify an example from a LiveData presentation I was just watching: suppose that you have an Observable that emits user IDs, based on the user logging in and out of your app
|
Aaron |
in response to #3, slightly tangential, but I am curious what is recommended then, as that exact example was actually provided in more than one "intro" style article
|
Mark M. |
that ties into #4, so let me finish that, and I'll blend in my response to this as part of it
|
Aaron |
OK
|
Mark M. |
so you have this userIdObservable
|
Mark M. |
however, you really need a User object, which you need to load from the database
|
Mark M. |
you have a Room UserDao set up with a loadUser(String userId) method that returns Single<User>
|
Mark M. |
you also have a loadUserRightNowDammit(String userId) method that returns User
|
Dec 6 | 7:50 PM |
Mark M. |
to build an RxJava chain that starts with userIdObservable and emits a User to your subscriber, you have two main options
|
Mark M. |
A. you map() the userId to the User using loadUserRightNowDammit()
|
Mark M. |
B. you flatMap() the userId to the User using loadUser()
|
Mark M. |
so long as you have your threads set up properly with your subscribers, those will have the same net result in the end
|
Mark M. |
however, that's a case where *you* were in control over the API and basically gave yourself two options
|
Mark M. |
now imagine that you're using somebody else's user-management library
|
Mark M. |
you don't control the API -- you get what they give you
|
Mark M. |
if they give you a loadUserRightNowDammit()-style method, you would use map()
|
Mark M. |
if they give you a loadUser()-style method, you would use flatMap()
|
Mark M. |
the developers of the library might intentionally only expose reactive types (e.g., Single<User>), to make it less likely that consumers of the library would accidentally call loadUserRightNowDammit() on an inappropriate thread, such as Android's main application thread
|
Aaron |
OK, keep going but indicate when you are done, as I have a question
|
Mark M. |
so, rolling back to #3, artificially creating observables just to use flatMap() is pointless, as you will be better served with other operators, such as map()
|
Mark M. |
but, *if* you are using something that is exposing a reactive API, that is where flatMap() comes into play
|
Mark M. |
and with that, fire away with questions! when we're done with those, I can return to your list
|
Dec 6 | 7:55 PM |
Aaron |
OK, so, this is just a thought experiment, obviously it would be bad, but,
|
Aaron |
technically you could also have option (C), in which you map() the userId to the User with loadUser(), and then double-subscribe to the resulting Single<Single<User>> - right?
|
Mark M. |
um, sure, though I don't know of any use case for that
|
Aaron |
yeah, I doubt there is one, it's just a helpful way to verify I get what's happening in both cases
|
Aaron |
so that's good, I think I get it then
|
Aaron |
please continue!
|
Mark M. |
in terms of #5, near as I can tell, Reactive Streams is part of a standardization process
|
Mark M. |
RxJava provides an implementation of that, but its own capabilities greatly exceeded the Reactive Streams spec
|
Dec 6 | 8:00 PM |
Mark M. |
personally, outside of toFlowable() for use with LiveDataReactiveStreams, I have not used the Reactive Streams family of classes
|
Mark M. |
so, I can't really answer that one
|
Aaron |
OK. I figured that Subscriber might be too low-level to matter for me right now
|
Aaron |
that's a good enough answer for me
|
Mark M. |
for #6, you probably want to look at PublishSubject and BehaviorSubject
|
Mark M. |
off the top of my head, BehaviorSubject is probably the closest analogue to MutableLiveData
|
Mark M. |
however, if you don't want the "get only future emissions, not the last one", that's PublishSubject
|
Mark M. |
sorry, typo in that last one
|
Mark M. |
let's try that again
|
Mark M. |
however, if you want "get only future emissions, not the last one", that's PublishSubject
|
Aaron |
OK
|
Mark M. |
in terms of #7, the approach that you are describing is not itself lifecycle-aware
|
Mark M. |
you keep updating the MutableLiveData(?) until you unsubscribe
|
Mark M. |
the LiveData that you get from LiveDataReactiveStreams is lifecycle-aware and will unsubscribe for yout
|
Mark M. |
er, for you
|
Aaron |
ah that makes sense.
|
Dec 6 | 8:05 PM |
Mark M. |
but your one-liner is basically the guts of the implementation, more or less
|
Dec 6 | 8:05 PM |
Aaron |
got it
|
Mark M. |
in terms of your last one -- and bearing in mind the problems you were pointing out with the heap dumps not clearing garbage anymore -- I tend not to use heap dumps to try to track down that sort of thing
|
Mark M. |
I will focus on my types and obvious stuff from libraries
|
Mark M. |
in the end, a random String is not likely to be leaked in isolation
|
Aaron |
yep, ok, it seemed like a hopeless endeavor anyway
|
Mark M. |
something is holding onto something that is holding onto something that is holding onto something that is holding that String, and you are leaking the very first "something"
|
Mark M. |
and that "something" is more likely to be recognizable
|
Mark M. |
the only low-level types I'll worry about are bitmaps, 'cause they can get huge
|
Mark M. |
I think that covers your list
|
Aaron |
yep, that's great, thank you,
|
Aaron |
let me throw in a couple quick follow ups before going. I didn't have time to properly think this one through so it's going to be poorly formulated, but
|
Dec 6 | 8:10 PM |
Aaron |
I was trying to think about how RxJava switchMap() is analogous to LiveData switchMap(), but was having a tough time, as I think the similarity is fairly abstract
|
Aaron |
is it obvious to you how to formulate the analogy?
|
Mark M. |
I have to keep looking up on Google to understand the difference between flatMap() and switchMap() just within the RxJava world
|
Mark M. |
let alone comparing to LiveData's switchMap()
|
Aaron |
yeah, ok, don't worry about it then
|
Mark M. |
they are analogous insofar as they both map a simple thing to a reactive thing
|
Mark M. |
compared to map() which maps a simple thing to a simple thing
|
Aaron |
no worries :p
|
Aaron |
last thing, I can probably figure this out myself after the chat but just want to ask before I go, this was an example of the flatMap usage I was talking about
|
Aaron |
imagine this method returns search engine URL results based on the query:
|
Aaron |
View paste
|
Aaron |
the article is putting forth that flatMap() is necessary to accomplish that and print one URL at a time
|
Aaron |
how would you rewrite it to use map()?
|
Mark M. |
I wouldn't
|
Mark M. |
well, I suppose it depends
|
Dec 6 | 8:15 PM |
Mark M. |
query() is returning a Single<List<String>> or something?
|
Aaron |
yes
|
Aaron |
sorry
|
Aaron |
Observable<List<String>> as per the example but should be functionally the same I think
|
Mark M. |
and you are looking to consume each URL individually for one reason or another
|
Aaron |
what he says is that the subcriber's role is to consume, not to mutate, so you wouldn't want to break the List apart inside the subscriber
|
Aaron |
and yes, if you want to get each one individually that flatMap would be needed
|
Mark M. |
this may be an RxJava 1.x example, as there is no from() on Observable
|
Aaron |
yes, it is 1.x
|
Mark M. |
ah
|
Mark M. |
take anything related to RxJava 1.x with a grain of salt when it comes to RxJava 2.x
|
Mark M. |
the equivalent in RxJava 2 would be fromIterable()
|
Mark M. |
I think there is another operator that could flatten the List<String> to String events without the flatMap()
|
Dec 6 | 8:20 PM |
Mark M. |
though I confess that I am not seeing it
|
Aaron |
hm, OK I'll research it further and formulate a better question for next time if needed
|
Mark M. |
but I will agree that splitting an object into multiple downstream objects may require a flatMap() created using some simple Observable factory method, such as fromIterable()
|
Mark M. |
your question isn't bad, it's just that I think that there's a simpler solution out there
|
Aaron |
do you agree that map() would not suffice?
|
Mark M. |
map() is 1:1
|
Mark M. |
you basically want 1:N (1 list to N strings)
|
Mark M. |
and map() can't do that
|
Aaron |
yeah, OK, I think we are on the same page about it
|
Aaron |
mainly wnated to make sure about what you said earlier, "artificially creating observables just to use flatMap() is pointless, as you will be better served with other operators, such as map()", that this would not be a case in which it's pointless
|
Aaron |
although the chat history is scrolling off screen and that may have been in response to something else
|
Mark M. |
unless there is some other solution, other than flatMap(), for 1:N
|
Aaron |
OK, that is all I've got
|
Dec 6 | 8:25 PM |
Aaron |
roger that
|
Aaron |
alright thanks! that was really helpful
|
Mark M. |
you're welcome!
|
Aaron |
I might have 1-2 more during the next chat but that was most of it
|
Mark M. |
sounds good
|
Aaron |
thanks - have a good night
|
Mark M. |
you too!
|
Aaron | has left the room |
Dec 6 | 8:30 PM |
Mark M. | turned off guest access |