I have the code:
return channelRepository.getRssFeedContent(channel.getSourceLink())
.toObservable()
.map(this::parseItem)
.flatMapIterable(xmlItemRawObjects -> xmlItemRawObjects)
.compose(/* question */)
.subscribeOn(Schedulers.from(threadExecutor))
.observeOn(postExecutionThread.getScheduler());
Using xmlItemRawObjects I need to make a query to the database, check if the record exists. If no record exists, return the same xmlItemRawObjects from compose() to continue working with it.
If the record exists in the database, then make sure that compose() skips the element for the stream.
I tried to create a function:
.compose(new ObservableTransformer<XmlItemRawObject, XmlItemRawObject>() {
#Override
public ObservableSource<XmlItemRawObject> apply(Observable<XmlItemRawObject> upstream) {
Boolean isExists = false;
Observable<Item> test = Observable.create(emitter -> {
upstream
.flatMap(xmlItemRawObject -> channelRepository.getItemByUniqueId(xmlItemRawObject.getGuid())
.subscribe(item -> isExists = true));
});
}
})
but it works just fine. Thank you for your advice.
P.S. No examples from compose() at all.
I've tried to solve the problem through flatmap:
.flatMap(new Function<XmlItemRawObject, ObservableSource<XmlItemRawObject>>() {
#Override
public ObservableSource<XmlItemRawObject> apply(XmlItemRawObject xmlItemRawObject) throws Exception {
Observable<XmlItemRawObject> test = Observable.create(emitter -> {
channelRepository.getItemByUniqueId(xmlItemRawObject.getGuid())
.subscribe(
item -> {
emitter.onComplete();
}, throwable -> {}, () -> Observable.just(xmlItemRawObject));
});
return test.defaultIfEmpty(xmlItemRawObject);
}})
Can't check an empty "subquery" result, make an if-else construct to pass on the xmlItemRawObject along the chain
Related
I'm working on a trading app. When the user select some products I need to show for each product if the market is open and its latest price. The user can select for example 2 products and what I have to do is to show the data only when I have all the info for the 2 products. The data can change at any time (i.e. the market for one of the products got closed). This is my code:
data class ProductInfo(
val productCode: String,
val isMarketOpen: Boolean,
val latestPrice: BigDecimal,
)
// This observable could emit at any time due to user interaction with the UI
private fun productsCodeObservable(): Observable<List<String>> = Observable.just(listOf("ProductA", "ProductB"))
// Markets have different working hours
private fun isMarketOpenObservable(productCode: String): Observable<Boolean> {
return Observable.interval(2, TimeUnit.SECONDS)
.map {
// TODO: Use API to determine if the market is open for productCode
it.toInt() % 2 == 0
}
}
// The product price fluctuates so this emits every X seconds
private fun latestPriceObservable(productCode: String): Observable<BigDecimal> {
return Observable.interval(2, TimeUnit.SECONDS)
.map { productPrice -> productPrice.toBigDecimal() }
}
#Test
fun `test`() {
val countDownLatch = CountDownLatch(1)
productsCodeObservable()
.switchMap { Observable.fromIterable(it) }
.flatMap { productCode ->
Observable.combineLatest(
isMarketOpenObservable(productCode),
latestPriceObservable(productCode)
) { isMarketOpen, latestPrice ->
ProductInfo(productCode, isMarketOpen, latestPrice)
}
}
.toList()
.doOnSuccess { productsInfo ->
println(productsInfo)
}
.subscribe()
countDownLatch.await()
}
I don't know what the problem is because the test method never prints anything. I don't know much about RxJava but my understanding is that toList is not working because the source observables never complete. Any idea about how I can collect the data for the product codes and emit a list when any of the data changes? :)
If you want to receive new product info list every time any of these products has changed:
productsCodeObservable()
.switchMap { list ->
val productInfoObservables = list.map { productCode ->
Observable.combineLatest(
isMarketOpenObservable(productCode),
latestPriceObservable(productCode)
) { isMarketOpen, latestPrice ->
ProductInfo(productCode, isMarketOpen, latestPrice)
}
}
Observable.combineLatest(productInfoObservables) { it.toList() as List<ProductInfo> }
}
.doOnNext { productsInfoList ->
println(productsInfoList)
}
.subscribe()
Use the RxJava’s Emitter interface and implement its methods:
public interface Emitter<T> {
void onNext(T value);
void onError(Throwable error);
void onComplete();
}
I think you need an ObservableEmitter, Please take a look at the following page:
https://www.raywenderlich.com/2071847-reactive-programming-with-rxandroid-in-kotlin-an-introduction
I have this code :
getLocationObservable() // ---> async operation that fetches the location.
// Once location is found(or failed to find) it sends it to this filter :
.filter(location -> { // ---> I want to use this location in the the onNext in the end
after finishing some calculation here, I either return 'true' and continue
to the next observable which is a Retrofit server call, or simply
return 'false' and quit.
})
.flatMap(location -> getRetrofitServerCallObservable( location )
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
new Observer<MyCustomResponse>() {
#Override
public void onSubscribe(Disposable d) {
_disposable = d;
}
#Override
public void onNext(MyCustomResponse response) {
// I want to be able to use the `location` object here
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
I want to be able to use the location object from line 3(first observable), in the "onNext" that is trigerred by the second observable.
I can't manage to work it out.. any help would be much appreciated.
Instead of
getRetrofitServerCallObservable( location )
you could map the result to be a Pair (from your favourite library) of the response and the location:
getRetrofitServerCallObservable( location ).map(response -> Pair.create(location, response))
Then, in your onNext, you'd be receiving Pair<Location,MyCustomResponse> instances.
If you don't want to use a Pair class, you could use Object[], but if you do, please don't tell me about it :P
i would like to implement a Pollingservice which calls a REST Api every nDelay Seconds and notify all subscribers if the data has been changed. Now i have a little problem with my code since it always returns a value to my Consumer, even if the data has not been changed.
private Observable<List<HueLight>> pollingLightsObservable = null;
public Observable<List<HueLight>> getPollingLightsObservable() {
if (pollingLightsObservable == null) {
pollingLightsObservable = Observable.fromCallable(
() -> LightManager
.getInstance(context)
.getLights()
.blockingSingle())
// .distinctUntilChanged( (l1, l1) -> !l1.equals(l2) )
.repeatWhen(o -> o.concatMap(v -> Observable.timer(1, TimeUnit.SECONDS)));
}
return pollingLightsObservable;
}
Enabling or using the distinctUntilChanged dont change anything. Doesnt matter if i put it before or after my repeatWhen.
Since my RetroFit Call returns an Observable, i have to use blockingSingle(). Using the Observable directly it leads into a return of "4, 8, 12, 16, .." items with this sample:
LightManager.getInstance(context).getLights()
.repeatWhen(o -> o.concatMap(v -> Observable.timer(1, TimeUnit.SECONDS)))
Currently i subscribe from different classes/activites with
this.lightChangeSubscriber = PollingManager
.getInstance(getContext())
.getPollingLightsObservable()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(hueLights -> {
{
Log.d(TAG, "Lights received successfully! Size=" + hueLights.size());
}
});
I would lovely avoid using interfaces and timer to create the polling. What would you recommend ?
what about using some custom filter?
public class FilterDuplicateHueConfig implements Predicate<HueConfig> {
private HueConfig lastVal;
#Override
public boolean test(HueConfig newVal) {
if(lastVal == null) {
lastVal = newVal;
return true;
}
... compare here the two values and return true/false appropriately...
}
}
I want to perform long calculations on background thread using RXJava in android. After calculation I am trying to present the result in Recylerview. I am using following piece of code:
Observable.just("true")
.subscribeOn(Schedulers.io())
.map(new Func1<String, String>() {
#Override
public String call(String s) {
feedlist.clear();
if (eventFeedItems != null && !eventFeedItems.isEmpty()) {
for (int i = 0; i < eventFeedItems.size(); i++) {
if (eventFeedItems != null && eventFeedItems.get(i) != null
&& ((eventFeedItems.get(i).getType() != null && eventFeedItems.get(i).getType().equalsIgnoreCase("EVENT"))
|| (eventFeedItems.get(i).getActivityRequestType() != null && eventFeedItems.get(i).getActivityRequestType().equalsIgnoreCase(EventConstants.TRENDING_ACTIVITY)))) {
if (eventFeedItems.get(i).getActivityRequestType() != null && !eventFeedItems.get(i).getActivityRequestType().equalsIgnoreCase("")) {
feedlist.add(new FeedsListModel(eventFeedItems.get(i), eventFeedItems.get(i).getActivityRequestType(), null));
} else if (eventFeedItems.get(i).getRequestType() != null && !eventFeedItems.get(i).getRequestType().equalsIgnoreCase("")) {
feedlist.add(new FeedsListModel(eventFeedItems.get(i), eventFeedItems.get(i).getRequestType(), null));
} else
feedlist.add(new FeedsListModel(eventFeedItems.get(i), EventConstants.ATTENDEE_POST, null));
}
}
}
Log.d("calculations","Completed");
return "";
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<String>() {
#Override
public void call(String s) {
// feed_list.setLayoutManager(mLayoutManager);
// feedListAdapter.notifyDataSetChanged();
Log.d("Adapter", "Set");
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
Log.d("Exception", "oh! fish...");
throwable.printStackTrace();
}
});
With the above piece of code I am facing UI Hindring as the ArrayList eventFeedItems size is of about more then 300 items.I am new to RXJava. Please help me out.
you will not achieve concurrency using map-Operator.
The first subscribeOn will move all emitions to IO-scheduler. No concurrency happening here.
.subscribeOn(Schedulers.io())
The map-operator will be called synchronously from previous thread. In your case it would be some thread from IO-threadpool.
.map(new Func1<String, String>() {
After the map-Operator has been executed, you will move the value from the IO-thread to the Android-UI-event-loop with
.observeOn(AndroidSchedulers.mainThread())
After the value has been translated from IO-thread to the UI-thread the next value from the initial observable will be processed.
Observable.just("true")
In your example there will be no more values because you only produce one value.
In order to achieve concurrency you should use flatMap instead of map. And use subscribeOn() in the flatMap to create each stream on another thread.
Please consider this example, to see how concurrency is happening. Every observable will be subscribed at once, so the max. time for the teset would be something around 5 seconds. If now concurrency would happen it would take 1+2+3+4+5 seconds plus execution time.
#Test
public void name1() throws Exception {
Observable<Integer> value = Observable.just(1_000, 2_000, 3_000, 4_000, 5_000)
.flatMap(i -> Observable.fromCallable(() -> doWork(i)).subscribeOn(Schedulers.io())
).doOnNext(integer -> System.out.println("value"));
value.test().awaitTerminalEvent();
}
private int doWork(int sleepMilli) {
try {
Thread.sleep(sleepMilli);
} catch (InterruptedException e) {
e.printStackTrace();
}
return -1;
}
If you want to know more about how concurrency is happening with flatMap please consider reading http://tomstechnicalblog.blogspot.de/2015/11/rxjava-achieving-parallelization.html
In regard to your code I would suggest:
Move the anonymouse implementation of interface to a private inner class implementation and use an instance of it. You will get a more readable observable
Don't use side-effects to global variables from operators within. You
will get race-condition if concurrency is involved.
List<FeedsListModel> eventFeedItems = Arrays.asList(new FeedsListModel(), new FeedsListModel());
Observable<FeedsListModel> feedsListModelObservable = Observable.fromIterable(eventFeedItems)
.flatMap(feedsListModel -> Observable.fromCallable(() -> calculation(feedsListModel))
.subscribeOn(Schedulers.computation())
);
feedsListModelObservable
.toList()
.observeOn(Schedulers.io())
.subscribe(feedsListModels -> {
// do UI stuff
});
Helping-method:
private FeedsListModel calculation(FeedsListModel model) {
// do calculation here
return new FeedsListModel();
}
I am messing around some with the google awareness api and now my understanding of RxJava is limiting me.
What I want to achieve in the end:
I want to get a Weather and a Location from the Api, and merge them into one object that I can pass on to my view for update.
However, I'm not sure how I achieve the returning of an Observable from the api callback here since it has void return type, and how to achieve merging of the weather and location object from api.getWeather and api.getLocation
public void requestUserCurrentInfo() {
Subscription userInfo = getWeatherLocation().subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).subscribe(userinfo ->
Log.d(TAG,userinfo.something()));
}
public Observable<UserInfo> getWeatherLocation () {
try {
Awareness.SnapshotApi.getWeather(client)
.setResultCallback(weather -> {
if (!weather.getStatus().isSuccess()) {
Log.d(TAG, "Could not get weather");
return;
}
//How do I do here?
return weather.getWeather();
});
Awareness.SnapshotApi.getLocation(mGoogleApiClient)
.setResultCallback(retrievedLocation -> {
if(!retrievedLocation.getStatus().isSuccess()) return;
Log.d("FRAG", retrievedLocation.getLocation().getLatitude() + "");
});
} catch (SecurityException exception) {
throw new SecurityException("No permission " + exception);
}
}
For my other things in my Project, I get some stuff through a REST api following the repository pattern, then I can get it like this because every step returns a Observable< SmhiResponse >
getWeatherSubscription = getWeatherUsecase.execute().subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).subscribe(
smhiResponseModel -> {Log.d(TAG,"Retrieved weather"); locationView.hideLoading();},
err -> {Log.d(TAG,"Error fetching weather"); locationView.hideLoading();}
);
You don't return an observable from the callback but wrap your callbacks into observables to make them combinable (untested):
Observable<WeatherResult> weatherObservable = Observable.create(subscriber -> {
Awareness.SnapshotApi.getWeather(client)
.setResultCallback(weather -> {
if (!weather.getStatus().isSuccess()) {
subscriber.onError(new Exception("Could not get weather."));
Log.d(TAG, "Could not get weather");
} else {
//How do I do here?
subscriber.onNext(weather);
subscriber.onCompleted();
}
});
});
Observable<LocationResult> locationObservable = Observable.create(subscriber -> {
Awareness.SnapshotApi.getLocation(mGoogleApiClient)
.setResultCallback(retrievedLocation -> {
if(!retrievedLocation.getStatus().isSuccess()) {
subscriber.onError(new Exception("Could not get location."));
} else {
Log.d("FRAG", retrievedLocation.getLocation().getLatitude() + "");
subscriber.onNext(retrievedLocation);
subscriber.onCompleted();
}
});
});
now combine them via .combineLatest() or .zip():
Observable<CombinedResult> combinedResults = Observable.zip(weatherObservable, locationObservable,
(weather, location) -> {
/* somehow combine weather and location then return as type "CombinedResult" */
});
don't forget to subscribe, otherwise none of them gets executed:
combinedResults.subscribe(combinedResult -> {/*do something with that stuff...*/});
Observable.combineLatest(getWeather (), getLocation(), new Func2<List<Object_A>, List<Object_B>, Object>() {
#Override
public Object call(Object o, Object o2) {
combine both results and return the combine result to observer
}
})
getweather() and getlocation() return observables