So I have following Disposable which doesn't work. I am using Room to get all rows from a table as a list, map each of them to something and create a list and then it doesn't continue from there.
storedSuggestionDao
.getSuggestionsOrderByType() //Flowable
.doOnNext(storedSuggestions -> Timber.e("storedSuggestions: " + storedSuggestions)) //this work
.flatMapIterable(storedSuggestions -> storedSuggestions)
.map(Selection::create) ))
.doOnNext(selection -> Timber.e("Selection: " + selection)) // works
.toList()
.toObservable() // nothing works after this...
.doOnNext(selections -> Timber.d("selections: " + selections))
.map(SuggestionUiModel::create)
.doOnNext(suggestionUiModel -> Timber.d("suggestionUiModel: " + suggestionUiModel))
.subscribe();
These types of data sources from 3rd parties are usually infinite sources but toList() requires a finite source. I guess you wanted to process that collection of storedSuggestions and keep it together. You can achieve this via an inner transformation:
storedSuggestionDao
.getSuggestionsOrderByType() //Flowable
.doOnNext(storedSuggestions -> Timber.e("storedSuggestions: " + storedSuggestions)) //this work
// -------------------------------------
.flatMapSingle(storedSuggestions ->
Flowable.fromIterable(storedSuggestions)
.map(Selection::create)
.doOnNext(selection -> Timber.e("Selection: " + selection))
.toList()
)
// -------------------------------------
.doOnNext(selections -> Timber.d("selections: " + selections))
.map(SuggestionUiModel::create)
.doOnNext(suggestionUiModel -> Timber.d("suggestionUiModel: " + suggestionUiModel))
.subscribe();
I think in your case you don't need to call .toObserable()
It should be like this
storedSuggestionDao
.getSuggestionsOrderByType() //Flowable
.doOnNext(storedSuggestions -> Timber.e("storedSuggestions: " + storedSuggestions)) //this work
.flatMapIterable(storedSuggestions -> storedSuggestions)
.map(Selection::create) ))
.doOnNext(selection -> Timber.e("Selection: " + selection)) // works
.toList() // you don't have to call .toObserable()
.map(SuggestionUiModel::create)
.subscribe();
The problem is
storedSuggestionDao.getSuggestionsOrderByType() //Flowable
is a hot stream.
toList still waits for upstream to complete
Related
I keep getting a null pointer exception i want to see if the response exists and if so println it out.
if (responsePlace.result.opening_hours.weekday_text.isNotEmpty() ){
println("The response for place time " + responsePlace.result.opening_hours.weekday_text[0].toString())}
you can try this :
responsePlace.let
{
when(it.isSuccessful) {
true -> println(""The response for place time " + it.result.opening_hours.weekday_text[0].toString()")
false -> println("something went wrong!")
}
Use the handy isNullOrEmpty() method in kotlin.
So your method will look like
if (!responsePlace.result.opening_hours.weekday_text.isNullOrEmpty()){
println("The response for place time " + responsePlace.result.opening_hours.weekday_text[0].toString())}
take care of the '!' negation at the start of the condition
You must check which object is null or better to post log trace.
you may use Kotlin safe call which will prevent NPE.
if(responsePlace?.result?.opening_hours.weekday_text.isNullOrEmpty())
println("The response for place time " + responsePlace.result.opening_hours.weekday_text[0].toString())
I have the following case
someThing.forEach{
someWidget.setOnClickListener{
//it is an View
//I need foreach it of someObject
}
}
I read this answer but it does not work
kotlin how to refer outer-scope this in multi-layer apply functions
The problem is that you are not dealing with this here.
forEach has a parameter and for simplicity you can leave it away and just use it instead. Not using it is the same as using _ -> instead... you just discard it.
So your example written with named lambda parameters instead:
someThing.forEach{ some -> // 'it' was available here too, but will not be accessible from within the next setOnClickListener...
someWidget.setOnClickListener{
// some contains one of the someThings now and 'it' is still your View
}
}
You can name the variable in the forEach.
things.forEach { thing ->
someWidget.setOnClickListener {
thing.doSomething()
}
}
I think you mean something like this:
someThing.forEach{ x->
someWidget.setOnClickListener{
//use x
//I need foreach it of someObject
}
}
just use another name like x, you don't have to use it.
Here is an example:
val a = mutableListOf<Int>(1, 3)
val b = mutableListOf<Int>(2, 4)
a.forEach { x ->
b.forEach {
println("" + x + " " + it)
}
}
here x is each item from list a
and it is each item from list b
I am trying to see if I can spawn 1 million Observables on io() and computation() Schedulers.
public static void observableLimit() {
sum = 0;
long lowerBound = 0;
long higherBound = 1000;
Flowable.fromCallable(() -> {
Flowable.rangeLong(lowerBound, higherBound + 1)
.subscribe(integer -> Observable.just(integer)
.subscribeOn(Schedulers.io())
.subscribe(j -> {
printNum(j);
sum = sum + j;
}));
return true;
}).blockingSubscribe(aBoolean -> {
long actualSum = (higherBound * (higherBound + 1)) / 2;
System.out.println("");
System.out.println("SUM: " + sum);
Assert.assertEquals(actualSum, sum);
});
}
For higherBound = 100 it works most of the time, for 1000 it works sometimes and fails most of the time and for 10000 it almost fails everytime, it works if I tell it to run it on newThread() and if I don't use subscribeOn() at all.
How can I fix this behaviour?
The problem you're facing is not about of some limitations of Observables, but a problem with your code. You're blockingSubscribe to a Flowable that have no relation with the Flowable that span all other threads. for small values of higherBound you'll see that the code works while for large values doesn't and that because the outer Flowable may be as fast as the inner Flowable for small higherBound but collapse faster for high values of higherBound.
What I'm trying to say is that in order to see the right result you need to syncronize with the Flowable that span all the other threads instead of the outer one. I also would replace long sum by a thread-safe implementation LongAdder sum, you can achieve this using flatMap operator.
Flowable.rangeLong(lowerBound, higherBound + 1)
.flatMap(t -> Flowable.just(t)
.subscribeOn(Schedulers.io())
)
.doOnNext(sum::add)
.doOnComplete(() -> {
long actualSum = (higherBound * (higherBound + 1)) / 2;
log("SUM: " + sum.longValue() + ", ACTUAL: " + actualSum);
log("Equals: " + (actualSum == sum.longValue()));
})
.blockingSubscribe();
How can I fix this behaviour?
Don't use that pattern. Why do you want to do that in the first place?
io and newThread create OS threads and are fundamentally limited by your OS' capabilities and available memory.
computation has a fixed set of threads and can handle much larger number of Flowables because they get assigned to one of the existing worker threads.
I am using Room (1.0.0.rc1) with RX, my Dao is defined is this way:
#Dao
interface AccountDao {
#Query("SELECT * FROM Account ORDER BY name")
fun all(): Flowable<List<Account>>
}
I am subscribing this way:
dao
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { accounts = it }
I have more than one place in the code who subscribes to the flowable. The first to subscribe gets the data, the other ones don't.
How can I make an observable that will emit the actual content every time someones subscribes and will also notify every subscriber when the data changes?
You can use replay to emit lastest value every time someone subscribes. And use distinctUntilChanged to notify only when data changes.
Here is the sample:
import io.reactivex.Observable;
import io.reactivex.subjects.BehaviorSubject;
public class Q47000608 {
public static void main(String[] args) {
BehaviorSubject<Integer> bs = BehaviorSubject.createDefault(1);
Observable<Integer> o = bs.replay(1).autoConnect().distinctUntilChanged();
o.subscribe(i -> System.out.println("s1 accept " + i));
bs.onNext(2);
o.subscribe(i -> System.out.println("s2 accept " + i));
o.subscribe(i -> System.out.println("s3 accept " + i));
bs.onNext(3);
o.subscribe(i -> System.out.println("s4 accept " + i));
bs.onNext(4);
}
}
And output:
s1 accept 1
s1 accept 2
s2 accept 2
s3 accept 2
s1 accept 3
s2 accept 3
s3 accept 3
s4 accept 3
s1 accept 4
s2 accept 4
s3 accept 4
s4 accept 4
I have some doubts regarding the working on the subscribeOn operator. I read some article regarding this.
The observeOn is quite easy to understand, it changes only the downstram, and change affects to all the downstream.
But as told in the article subscribeOn can be put in any place in the stream because it affects only the time of subscription.:
To understand this , I did a samlpe and tried logging the thread at each point of time.
Observable.just("Hello")
.map(s -> {
Log.d(TAG, s + " in " + Thread.currentThread());
return 1;
})
.subscribeOn(Schedulers.newThread())
.map(integer -> {
Log.d(TAG, integer + " in " + Thread.currentThread());
return true;
})
.map(aBoolean -> {
Log.d(TAG, aBoolean + " in " + Thread.currentThread());
return 11.0;
})
.subscribeOn(Schedulers.computation())
.subscribe(aDouble -> {
Log.d(TAG, "accept in " + Thread.currentThread());
Log.d(TAG, "accept: " + aDouble);
});
The result is
Hello in Thread[RxNewThreadScheduler-1,5,main]
1 in Thread[RxNewThreadScheduler-1,5,main]
true in Thread[RxNewThreadScheduler-1,5,main]
accept in Thread[RxNewThreadScheduler-1,5,main]
accept: 11.0
Here twice I'm applying subscribeOn, but everytime the first added one seem to be applied throughout the stream.
Can anyone please explain in simple words how does it actually work, since I'm a beginner and hard to digest this!
Thanks in advance
subscribeOn: If you have multiple subscribeOn then the first one takes effect. If you want to change the Scheduler on the stream after making a subscribeOn, then take a look at observeOn
observeOn: It changes the Scheduler going downstream.
For example:
just("Some String") // Computation
.subscribeOn(Schedulers.computation()) // it changes scheduler to computation beginning from source to observer.
.map(str -> str.length()) // Computation
.observeOn(Schedulers.io) //change the scheduler from here till the observer
.map(length -> 2 * length) // io
.subscribe(number -> Log.d("", "Number " + number));// io