I have a list of files that I'd like to upload to the backend from an Android device. Due to memory constraints, I'd like to make the second API call only after the first finished, the third after the second finished, and so on.
I wrote something like
private Observable<Integer> uploadFiles(List<File> files) {
return Observable.create(subscriber -> {
for (int i = 0, size = files.size(); i < size; i++) {
UploadModel uploadModel = new UploadModel(files.get(0));
int uploadResult = retrofitApi.uploadSynchronously(uploadModel);
subscriber.onNext(uploadResult);
}
subscriber.onCompleted();
}).subscribeOn(Schedulers.newThread());
}
But I feel like this might be going against the spirit of Rx, and the saying is if you're using Observable.create, you're probably doing it wrong...
Is this a reasonable approach? Is there a better way to achieve this with Retrofit's RxJava integration?
Naively, I would do that (it does not work, though, see below):
return Observable.from(files).concatMap(file -> retrofitApi.upload(uploadModel));
Now the issue is that there is no way to tell retrofit to use only one thread for those calls.
reduce, however, passes the result of one function call to the next, along with the next emitted value from the original observable. That would work, but the function passed to reduce needs to be synchronous. Not good.
Another approach would be to modify the observable recursively:
void getNextFile(int i) {
return retrofit.upload(i).
onNext(result -> getNextFile(i + 1));
}
roughly. But I am not sure how to clean it to make it more readable.
The cleanest I would think would be something like:
Observable.from(files).map(file -> retrofitApi.uploadSynchronously(new UploadModel(file)));
The natives of RxJava would emit all items in Observable.from(...) as if in parallel. That's the best way to think of it as parallel emission. However some cases require real consequent execution of the whole chain. I've come to the following solution, probably not the best one but working.
import rx.Observable;
import rx.Subscriber;
import java.util.Iterator;
import java.util.function.Function;
public class Rx {
public static void ignore(Object arg) {
}
public static <E, R> Observable<Void> sequential(Iterator<E> iterator, Function<E, Observable<R>> action) {
return Observable.create(collectorSubscriber ->
Observable.<Void>create(producerSubscriber ->
producerSubscriber.setProducer(ignoredCount -> {
if (!iterator.hasNext()) {
producerSubscriber.onCompleted();
return;
}
E model = iterator.next();
action.apply(model)
.subscribe(
Rx::ignore,
producerSubscriber::onError,
() -> producerSubscriber.onNext(null));
}))
.subscribe(new Subscriber<Void>() {
#Override
public void onStart() {
request(1);
}
#Override
public void onCompleted() {
collectorSubscriber.onNext(null);
collectorSubscriber.onCompleted();
}
#Override
public void onError(Throwable e) {
collectorSubscriber.onError(e);
}
#Override
public void onNext(Void aVoid) {
request(1);
}
}));
}
}
Example usage would be:
Iterator<? extends Model> iterator = models.iterator();
Rx.sequential(iterator, model -> someFunctionReturnsObservable(model))
.subscribe(...);
This method guarantees chained executions of
Observable<Dummy> someFunctionReturnsObservable(Model model)
Currently the prefered way of creating observables is with fromAsync:
Observable.fromAsync(new Action1<AsyncEmitter<Object>>()
{
#Override
public void call(final AsyncEmitter<Object> emitter)
{
emitter.onNext(object);
emitter.onCompleted();
emitter.setCancellation(new AsyncEmitter.Cancellable()
{
#Override
public void cancel() throws Exception
{
// on unSubscribe() callback
}
});
}
}, AsyncEmitter.BackpressureMode.BUFFER);
Related
I'm new to Android development and am currently trying to make a simple MVC app that works with Rest API.
API calls are made without using Retrofit, although this is not so important. The main catch is that using Observable with debounce and SwitchMap I still get too many API calls (and the extra ones should be discarded). The function is called when text is entered (EditText Listener with TextWatcher). And when administered continuously without delay word, every symbol processed by the server and should only be administered when not within 600 milliseconds. Please help me.
public Observable<String> getObservable(final String s){
return Observable
.create(new ObservableOnSubscribe<String>() {
#Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
emitter.onNext(model.translateText(s));
}
});
}
public Observer<String> observer = new Observer<String>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(String s) {
mainView.hideProgress();
mainView.showResult(s);
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
};
public void onEditTextChange(String textForTranslate){
mainView.showProgress();
getObservable(textForTranslate)
.debounce(600,TimeUnit.MILLISECONDS)
.switchMap(new Function<String, ObservableSource<String>>() {
#Override
public ObservableSource<String> apply(String s) throws Exception {
return Observable.just(s);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
}
You are creating a new observable every time a character is typed. So multiple observers are created with each having separate debounce (time throttle) and switch but they are not reused. Instead you create a new observable whenever text changes and start rx chain on it.
You need to create a single PublishSubject
private final PublishSubject<String> querySubject = PublishSubject.create();
that emits entered text/query whenever text is changed. Use it in your callback:
public void onEditTextChange(String textForTranslate) {
querySubject.onNext(textForTranslate);
}
And in your main function, subscribe to observable:
querySubject
.debounce(600, TimeUnit.MILLISECONDS)
.distinctUntilChanged()
.switchMap(new Function<String, ObservableSource<String>>() {
#Override
public ObservableSource<String> apply(String s) throws Exception {
// perform api call or any other operation here
return Observable.just(s);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
Debounce operator emits single item only after given time (600 ms) has passed. It ignores items if current item is being processed and given time has not passed.
distinctUntilChanged helps in reducing processing of same query.
I'm new to RxJava, I know flatmaps are for mapping an emitted item to observable. I also know that based on the documentation the emitted observables all get combined (flatten) to a single observable stream.
I was wondering what happens if any of those inner observables get completed?
for example: I have an observable that emits a item data key. I have to make another async http call to get the item data from the server, so I call it by using another observable. I use a flat map to connect these two and create one main observable.
When does the run() method of following "SomeMethodThatWantsItems" get called?
public void someMethodThatWantsItems(MyHttpCaller httpCaller, SomeSearchEngine searchEngine)
{
Consumer<Item> onNextConsumer =
Observable<Item> searchObservable = getSearchResult(httpCaller, searchEngine, "The Search Word");
searchObservable
.subscribeOn(Schedulers.newThread())
.subscribe(new Consumer<Item>(){
#Override
public void accept(#NonNull Item item) throws Exception {
//Do stuff with the item
}
}
, new Consumer<Exception>() { //some implementation of onErrorConsumer
}
//OnComplete
, new Action(){
#Override
public void run() throws Exception {
//When does this get called??? after the search complete or when the first http call is successful?
}
});
}
private Observable<String> getSearchResultKeys(SomeSearchEngine searchEngine, String someSearchWord)
{
return Observable.create(new ObservableOnSubscribe<String>() {
#Override
public void subscribe(#NonNull final ObservableEmitter<String> emitter) throws Exception {
//Assume that our search engine call onFind everytime it finds something
searchEngine.addSearchListener(new searchEngineResultListener(){
#Override
public void onFind(String foundItemKey){
emitter.onNext(foundItemKey);
}
#Override
public void onFinishedFindingResults(){
emitter.onComplete();
}
});
}
});
}
private Observable<Item> getItemByKey(MyHttpCaller httpCaller, String key)
{
return Observable.create(new ObservableOnSubscribe<Item>() {
#Override
public void subscribe(#NonNull final ObservableEmitter<Item> emitter) throws Exception {
//Call the server to get the item
httpCaller.call(key, new onCompleteListener(){
#Override
public void onCompletedCall(Item result)
{
emitter.onNext(result);
//The result is complete! end the stream
emitter.onComplete();
}
});
}
});
}
public Observable<Item> getSearchResult(MyHttpCaller httpCaller, SomeSearchEngine searchEngine, String someSearchWord){
//Where everything comes together
Observable<String> searchResultObservable = getSearchResultKeys(searchEngine, someSearchWord);
retuern searchResultObservable
.observeOn(Schedulers.newThread())
.flatMap(new Function<String, Observable<Item>>(){
#Override
public Observable<Item> apply(String key){
return getItemByKey(httpCaller, key);
}
});
}
The onComplete() always get call once and then the streams stops. (this is part of the Observable Contract).
That means that in your case, your onComplete() at SomeMethodThatWantsItems will be called after all items were retrieved.
In case of flatMap(), completion of each inner Observable, simply will signal the source Observable to stop flatting item from the inner Observable to the source Observable, flatMap() merges items from the inner Observable as long as this stream sends items, so it's basically consume the entire inner Observable stream into the source stream, the entire stream is until termination event3 like onComplete(), so in case where inner Observable can emit more than 1 item, that means that it will make more than 1 emission on the source stream.
I'm trying to create a really simple callback using some Strings. The IDE's code sense is is moaning about an unchecked call to exceptionally. Can anyone give me a fix for this? The idea in the end is to wrap a network call so that the promised result is returned and I can tack on additional functions as needed.
import java.util.concurrent.*;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Supplier;
public class FuturesTest {
public static void main(String[] args) throws Exception {
new FuturesTest().go();
}
private void go() throws ExecutionException, InterruptedException {
CompletableFuture.supplyAsync(new MakesANetworkCall())
.whenComplete(new BiConsumer<String, String>() {
#Override
public void accept(String result, String s) {
System.out.println(result.toString());
}
})
.exceptionally(new Function<Exception, Exception>() {
#Override
public Exception apply(Exception e) {
e.printStackTrace();
return e;
}
}
).thenApplyAsync(new Function<String, String>() {
#Override
public String apply(String o) {
System.out.println("Last action of all!");
return null;
}
});
System.out.println("Main thread will sleep");
Thread.sleep(2500);
System.out.println("Program over");
}
class MakesANetworkCall implements Supplier {
#Override
public String get() {
try {
System.out.println("Ground control to Major Tom");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// int i = 2/0;
return new String("Major Tom reporting!");
}
}
}
First of all, your class MakesANetworkCall implements the raw type Supplier instead of Supplier<String>. This will effectively disable the type checking and hide all errors you made, so it’s not the single warning you should worry about, as that’s not the only thing wrong in your code:
The BiConsumer passed to whenComplete should be able to consume a Throwable as its second argument.
The Function passed to exceptionally should consume a Throwable and return an alternative result.
Further, you are invoking a static method using the expression new CompletableFuture<String>() as its target and you have an obsolete string creation expression as new String("Major Tom reporting!") where the simple constant "Major Tom reporting!" will do. Generally, you seem to try to always use an inappropriate method, i.e. one designed to consume things you don’t use or one for supplying a value where you don’t have one. Consider this:
CompletableFuture.supplyAsync(new MakesANetworkCall())
.thenAccept(result -> System.out.println(result))
.exceptionally(e -> { e.printStackTrace(); return null;})
.thenRun(()->System.out.println("Last action of all!"));
This does what seems to be your intention. If you ensure that your MakesANetworkCall correctly implements Supplier<String>, this should compile without any warnings.
Your core issue is with class MakesANetworkCall implements Supplier {. This is using raw types and therefore hides further problems. Fix that to class MakesANetworkCall implements Supplier<String> { and fix all ensuing issues and you get:
CompletableFuture.supplyAsync(new MakesANetworkCall())
// Not <String, String>
.whenComplete(new BiConsumer<String, Throwable>() {
#Override
public void accept(String result, Throwable t) {
System.out.println(result);
}
})
// Not <Exception,Exception>
.exceptionally(new Function<Throwable, String>() {
#Override
public String apply(Throwable t) {
t.printStackTrace();
// Must return a Streing
return t.getMessage();
}
}
).thenApplyAsync(new Function<String, String>() {
#Override
public String apply(String o) {
System.out.println("Last action of all!");
return null;
}
});
CompletableFuture.exceptionally is declared like so:
public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn);
You're passing it a Function<Exception, Exception>, whereas you should be passing it a Function<Throwable, String>.
I'm using RxJava and nesting Observables as below. I want to call one observable inside another observable and have the outer observable emit the result of the inner observable onNext. It appears to work, but I'm not certain this is the correct implementation since I wasn't able to find any documentation to confirm.
public Observable<User> updateUser(final String id) {
return Observable.create(new Observable.OnSubscribe<User>() {
#Override
public void call(final Subscriber<? super User> observer) {
try {
if (!observer.isUnsubscribed()) {
getUser(id).subscribe(new Action1<User>() {
#Override
public void call(User user) {
observer.onNext(user);
observer.onCompleted();
}
});
}
} catch (Exception e) {
observer.onError(e);
}
}
});
}
public Observable<User> getUser(final String id) {
...
}
Avoid calls to subscribe when you are just doing Observable transformations as you have to be mindful of all the problems mentioned by #akarnokd and #zsxwing in the comments.
I would also avoid using Observable.create because creating OnSubscribe implementations involves considering backpressure and consequent tricky business with concurrency. Prefer Observable.just,Observable.from,Observable.range,Observable.defer, Observable.using (there are more, check the wiki) and for more advanced purposes implement SyncOnSubscribe.
This code probably covers your use case:
public Observable<User> updateUser(final String id) {
return getUser(id).doOnNext(user -> updateUser(user));
}
public void updateUser(User user) {
//whatever you want here
}
public Observable<User> getUser(final String id) {
...
}
I'm using Retrofit and RxJava to perform some background tasks. Code looks like this:
public class MyLoader{
public Observable<MyData> getMyData(){
return setupHelper().flatMap(new Func1<MyHelper, Observable<MyData>>() {
#Override
public Observable<MyData> call(MyHelper myHelper) {
return queryData(myHelper);
}
});
}
private Observable<MyData> queryData(MyHelper myHelper){
...
}
private Observable<MyHelper> setupHelper(){
return Observable.create(new Observable.OnSubscribe<MyHelper>() {
#Override
public void call(final Subscriber<? super MyHelper> subscriber) {
try{
MyHelper helper = makeRetrofitCall();//Using Retrofit blocking call to get some data
subscriber.onNext(helper);
subscriber.onCompleted();
}catch(RetrofitError e){
subscriber.onError(e)
}
}
}
}
}
This fails with RetrofitError, due to NetworkOnMainThread Exception at this line:
MyHelper helper = makeRetrofitCall();//Using Retrofit blocking call to get some data
Subscribing to my Observable:
myLoader.getMyData()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<MyData>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(MyData inventory) {
}
});
According to Rx documentation flatMap doesn't operate on any background thread. My question is how do I ensure that the whole getMyData() method runs in background.
I just add observeOn(Schedulers.newThread()) before flatMap and it works!
This moves just one step in the pipeline to the background thread:
Observable<Integer> vals = Observable.range(1,10);
vals.flatMap(val -> Observable.just(val)
.subscribeOn(Schedulers.computation())
.map(i -> intenseCalculation(i))
).subscribe(val -> System.out.println(val));
Originally answered here:
https://stackoverflow.com/a/35429084/2908525
There is a good chance when you create the MyLoader object in the main thread the Observable.create be executed as well (or maybe somewhere else before in your code (?) ). If it's so, the .subscribeOn(Schedulers.io()) will have no effect on changing the thread.
You can try wrap the .create() with a .defer() to make sure the Observable is created only when it's subscribed.
e.g. defer(() -> create(....))