I have this code to wrap a callback in Rx Java 1 and it compiles fine , but now that I have switched to RX Java 2 it does not compile...what is the equivalent in Rx Java 2?
return Observable.fromEmitter(new Action1<AsyncEmitter<Integer>>() {
#Override
public void call(AsyncEmitter<Integer> emitter) {
transObs.setTransferListener(new TransferListener() {
#Override
public void onStateChanged(int id, TransferState state) {
if (state == TransferState.COMPLETED)
emitter.onCompleted();
}
#Override
public void onProgressChanged(int id, long bytesCurrent, long bytesTotal) {
}
#Override
public void onError(int id, Exception ex) {
emitter.onError(ex);
}
});
emitter.setCancellation(new AsyncEmitter.Cancellable() {
#Override
public void cancel() throws Exception {
transObs.cleanTransferListener();
}
});
}
}, AsyncEmitter.BackpressureMode.BUFFER);
UPDATE:
I came up with this, but Do you have to deal with backpressure since its an oncreate call?
return Observable.create(new ObservableOnSubscribe<List<DigitsUser>>() {
#Override
public void subscribe(final ObservableEmitter<List<DigitsUser>> emitter) throws Exception {
mDigitFriends.findFriends((gotEm, users) -> {
emitter.onNext(users);
});
emitter.setCancellable(() -> {
emitter.onNext(null);
});
}
});
If you're worried about backpressure you should use the Flowable class. Here's a quote from the RxJava2 Wiki:
Practically, the 1.x fromEmitter (formerly fromAsync) has been renamed
to Flowable.create.
Here is your example using the Flowable class:
return Flowable.create(new FlowableEmitter<List<DigitsUser>>() {
#Override
public void subscribe(final FlowableEmitter<List<DigitsUser>> emitter) throws Exception {
mDigitFriends.findFriends((gotEm, users) -> {
emitter.onNext(users);
});
emitter.setCancellable(() -> {
emitter.onNext(null);
});
}
}, BackpressureStrategy.BUFFER);
Related
In RxJava2, here is my code:
public void myMethod() {
Flowable.create(e->{
// do sth.
}, BackpressureStrategy.BUFFER)
.subscribeOn(Schedulers.computation())
.observeOn(Schedulers.io())
.subscribe(new Subscriber<ContentsWithChannelVO>() {
#Override
public void onSubscribe(Subscription subscription) { // do sth. }
#Override
public void onNext(ContentsWithChannelVO contentAndChannel) { // do sth. }
#Override
public void onError(Throwable throwable) { // do sth. }
#Override
public void onComplete() { // do sth. }
});
doSomething();
}
Here is a synchronized problem.
I want to know how to let my method wait for the flowable onComplete, that is to say doSomething() will be called after the flowable onCompleted.
I have searched for it but it doesn't help.
You can use concat operator, but you need to wrap your method in another observable.
Concat waits to subscribe to each additional Observable that you pass
to it until the previous Observable completes.
You'd have to block the consumption of the flow for that (which is generally not recommended):
public void myMethod() {
Flowable.create(e -> {
// do sth.
}, BackpressureStrategy.BUFFER)
.subscribeOn(Schedulers.computation(), false)
.blockingSubscribe(new Subscriber<ContentsWithChannelVO>() {
#Override
public void onSubscribe(Subscription subscription) { // do sth. }
#Override
public void onNext(ContentsWithChannelVO contentAndChannel) { // do sth. }
#Override
public void onError(Throwable throwable) { // do sth. }
#Override
public void onComplete() { // do sth. }
});
doSomething();
}
In this case, having observeOn is pointless. Note also you have to use subscribeOn() with false otherwise the flow livelocks.
If you don't want to execute doSomething if the source fails, use blockingForEach:
public void myMethod() {
Flowable.create(e -> {
// do sth.
}, BackpressureStrategy.BUFFER)
.subscribeOn(Schedulers.computation(), false)
.blockingForEach(contentAndChannel -> { /* do sth. */ });
doSomething();
}
This is what I got to do
UploadCompleteListener is a custom interface that acts as a callback.
#Overrider
public Result doWork() {
mUpLoadDataService.uploadInspectionData(new UpLoadDataService.UploadCompleteListener() {
#Override
public void uploadComplete() {
return Result.success(); //this is what I want to do
}
#Override
public void uploadFailed(String reason) {
return Result.failure(); //this is what I want to do
}
});
return null;
}
Is it Possible?
If possible in any way please response soon. I can provide more details if you need it.
** This is what worked for me **
#NonNull
#Override
public Result doWork() {
final Result[] result = new Result[1];
mUpLoadDataService.uploadInspectionData(new UpLoadDataService.UploadCompleteListener() {
#Override
public void uploadComplete() {
result[0] = Result.success(); //this is what I want to do
}
#Override
public void uploadFailed(String reason) {
result[0] = Result.failure(); //this is what I want to do
}
});
return result[0];
}
public Result doWork(UpLoadDataService.UploadCompleteListener uploadListener) {
mUpLoadDataService.uploadInspectionData(uploadListener);
return null;
}
now pass the implementation from parent function. Lets say your parent function is named foobar
void foobar() {
someObject.doWork(new UpLoadDataService.UploadCompleteListener() {
#Override
public void uploadComplete() {
//write your logic here
return Result.success();
}
#Override
public void uploadFailed(String reason) {
//write your logic here
return Result.failure();
}
});
}
You have to realize that you are trying to get synchronously a result from an asynchronous call. This call is asynchronous for a reason, so the short answer is no, you can't.
Instead of returning a Result, you could return, for instance, a Future, which models the asynchronicity of the operation.
For android take a look at CallbackToFutureAdapter
https://developer.android.com/reference/kotlin/androidx/concurrent/futures/CallbackToFutureAdapter
https://developer.android.com/reference/java/util/concurrent/Future
You could use EventBus to notify Subscribed methods in every place you want like so:
public class MessageEvent {
public final String message;
public MessageEvent(String message) {
this.message = message;
}
}
public void doWork() {
mUpLoadDataService.uploadInspectionData(new UpLoadDataService.UploadCompleteListener() {
#Override
public void uploadComplete() {
EventBus.getDefault().post(new MessageEvent("success"));
}
#Override
public void uploadFailed(String reason) {
EventBus.getDefault().post(new MessageEvent("failed"));
}
});
}
#Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}
See this implementation guide.
You can get the callback of your task using the following way
doWork(paremter, new ServiceListener<String>() { //paremter if any
#Override
public void success(String obj) {
//get the response if success
}
#Override
public void fail(ServiceError error) {
//get the error response
}
});
do the work and send the call response from where it called
private void doWork(String param , ServiceListener<String> serviceListener) {
mUpLoadDataService.uploadInspectionData(new UpLoadDataService.UploadCompleteListener() {
#Override
public void uploadComplete() {
serviceListener.success("success");
}
#Override
public void uploadFailed(String reason) {
serviceListener.fail(new ServiceError("Can not Upload"));
}
});
}
ServiceListener interface will be defined as follow
public interface ServiceListener<T> {
void success(T obj);
void fail(ServiceError error);
}
public class ServiceError {
public Throwable errorObject;
public String message;
public ServiceError(){
message = "";
}
public ServiceError(String message){
this.message = message;
}
public ServiceError(String message, Throwable errorObject){
this.message = message;
this.errorObject = errorObject;
}
public Object getErrorObject() {
return errorObject;
}
public void setErrorObject(Throwable errorObject) {
this.errorObject = errorObject;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
In service I post my event :
RxBus.getSubject().onNext(eventAddNoteAndRealize) ;
This is my RxBus :
public final class RxBus {
private static final BehaviorSubject<Object> behaviorSubject
= BehaviorSubject.create();
public synchronized static BehaviorSubject<Object> getSubject() {
return behaviorSubject;
}
}
And In my Activity I have this :
DisposableObserver<Object> disposable = RxBus.getSubject().
subscribeWith(new DisposableObserver<Object>() {
#Override
public void onNext(Object o) {
if (o instanceof EventAddNoteAndRealize) {
Toast.makeText(NewMainActivity.this , "next", Toast.LENGTH_LONG).show();
EventAddNoteAndRealize event = new EventAddNoteAndRealize(((EventAddNoteAndRealize) o).getNoteAndRealizeDAOList());
eventAddNoteAndRealize = event;
getRealizeAndNote((EventAddNoteAndRealize)o);
}
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
And a method onNext execute two times this same objects. I do not have idea what I did wrong
Are you sure that you are not emitting the same object 2 times because as i test your code it works good
#Override
public void run(String... args) throws Exception {
DisposableObserver<Object> disposable = RxBus.getSubject().
subscribeWith(new DisposableObserver<Object>() {
#Override
public void onNext(Object o) {
System.out.println(o);
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
Observable.interval(100, TimeUnit.MILLISECONDS)
.map(aLong -> {
RxBus.getSubject().onNext(aLong);
return aLong;
}).subscribe();
}
}
final class RxBus {
private static final BehaviorSubject<Object> behaviorSubject
= BehaviorSubject.create();
public synchronized static BehaviorSubject<Object> getSubject() {
return behaviorSubject;
}
make sure that you are not emitting object to times my response is
0
1
2
3
4
5
6
.
.
.
I have an Observable<MoviesResponse>. My MovieResponse class contains a getResults() methods returning a List<Result>. This Result class has a getTitle() methods returning a String. I want to call the getTitle() methods of all my Result objects to get all the titles of my movies.
I achieved this with the code below using a foreach loop but I think there is a better way to do this by chaining RxJava operators, I just can't figure it out...
Subscription :
Observable<MoviesResponse> moviesResponseObservable = apiService.getTopRatedMoviesObservable(API_KEY);
subscription = moviesResponseObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<MoviesResponse>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(MoviesResponse moviesResponse) {
List<Result> results = moviesResponse.getResults();
for (Result r:results) {
Log.d(LOG_TAG,r.getTitle());
}
}
});
Interface :
public interface ApiService {
#GET("movie/top_rated")
Observable<MoviesResponse> getTopRatedMoviesObservable(#Query("api_key") String apiKey);
}
You can use a flatmap to transform your observable into an Observable<Result> and then use map to turn that into Observable<String>, which you can then subscribe to.
moviesReponseObservable
.subscribeOn(Schedulers.io())
.flatMapIterable(new Function<MoviesResponse, Iterable<Result>>() {
#Override
public Iterable<Result> apply(#NonNull MoviesResponse moviesResponse) throws Exception {
return moviesResponse.getResults();
}
})
.map(new Function<Result, String>() {
#Override
public String apply(#NonNull Result result) throws Exception {
return result.getTitle();
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<String>() {
#Override
public void onNext(String s) {
Log.d("TAG", s);
}
/* ... */
});
I got the following error with #zsmb13 answer :
new Function : map (rx.functions.Func1) in
Observable cannot be applied to (anonymous
java.util.function.Function)reason:
no instance(s) of type variable(s) R exist so that Function conforms to Func1
Anyway this answer was very helpul I just replaced Function with Func1 and used call method.
subscription = moviesResponseObservable
.subscribeOn(Schedulers.io())
.flatMapIterable(new Func1<MoviesResponse, Iterable<Result>>() {
#Override
public Iterable<Result> call(MoviesResponse moviesResponse) {
return moviesResponse.getResults();
}
})
.map(new Func1<Result, String>() {
#Override
public String call(Result result) {
return result.getTitle();
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<String>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(String s) {
Log.d(LOG_TAG, s);
}
});
Is it possible to resubscribe an Observable and get the error?
The Observable<T> retry() method resubscribes the observable but it consumes the error.
final PublishSubject<Integer> observable = PublishSubject.create();
observable
.flatMap(new Func1<Integer, Observable<Integer>>() {
#Override
public Observable<Integer> call(final Integer integer) {
if (integer % 2 == 0) {
return Observable.just(integer);
} else {
return Observable.error(new Exception("int: " + integer));
}
}
})
.retry()
.subscribe(new Action1<Integer>() {
#Override
public void call(final Integer integer) {
Timber.i("integer: %d", integer);
}
},
new Action1<Throwable>() {
#Override
public void call(final Throwable throwable) {
Timber.e(throwable, "throwable");
}
},
new Action0() {
#Override
public void call() {
Timber.w("onCompleted");
}
});
Observable
.range(0, 10)
.delay(2, TimeUnit.MILLISECONDS)
.subscribe(new Action1<Integer>() {
#Override
public void call(final Integer integer) {
observable.onNext(integer);
}
},
new Action1<Throwable>() {
#Override
public void call(final Throwable throwable) {
observable.onError(throwable);
}
},
new Action0() {
#Override
public void call() {
observable.onCompleted();
}
});
The onError part of observable is never called because .retry() consumes the error.
What you're looking for is retryWhen(). This allows you to pass a Func1 which provides you with the Throwable, that means you can place your onError logic there instead.
This is a good article.