I am trying to learn RXJava with MVVM pattern.
Here is scenario I am trying to implement:
On some search event I am calling SearchViewModel.handleSearchTopic() which is emitting list but somehow it is not getting caught in observer's onNext event. Subscription is also happening successfully. I think I am doing some simple mistake, please point that. Also, is there any better way of implementing this use case?
SearchViewModel.java
private final BehaviorSubject<List<Topic>> topicList = BehaviorSubject.create();
public void handleSearchTopic() {
List<Topic> list = //getsomehow;
topicList.onNext(list);
}
public Observable<List<Topic>> getTopicListObservable() {
return topicList.asObservable();
}
Fragment.java
#NonNull
private CompositeSubscription subscription;
#NonNull
private SearchViewModel searchViewModel;
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
searchViewModel = new SearchViewModel();
bind();
}
#Override
public void onDestroy() {
unBind();
super.onDestroy();
}
private void bind() {
subscription = new CompositeSubscription();
subscription.add(searchViewModel.getTopicListObservable()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<Topic>>() {
#Override
public void onCompleted() {
//do something
}
#Override
public void onError(Throwable e) {
//do something
}
#Override
public void onNext(List<Topic> topics) {
//ideally this should be called when event is emitted but not getting called
}
}));
}
private void unBind() {
subscription.unsubscribe();
}
It was a stupid mistake. I was using different instances of SearchViewModel in fragment and search event.
Related
I am new to RxJava and if I understand correctly the Observer is passed the Disposable on the onSubscribe so it can manually stop the processing if the dispose() has already been called.
I created the following code:
#NonNull Observable<Long> src = Observable.interval(1, TimeUnit.SECONDS);
src.subscribe(new Observer<Long>() {
private Disposable d;
#Override
public void onSubscribe(#NonNull Disposable d) {
this.d = d;
}
#Override
public void onNext(#NonNull Long aLong) {
if(!d.isDisposed()) {
System.out.println("Number onNext = " + aLong);
}
}
#Override
public void onError(#NonNull Throwable e) {
}
#Override
public void onComplete() {
System.out.println("completed");
}
});
but I can't figure out how to call dispose() for that subscription. subscribe with passing Observer as an argument returns void and subscribeWith does not accept my Observer without compile errors.
How is this supposed to work? What am I misunderstanding here?
The JavaDocs of Observable has a straightforward example:
Disposable d = Observable.just("Hello world!")
.delay(1, TimeUnit.SECONDS)
.subscribeWith(new DisposableObserver<String>() {
#Override public void onStart() {
System.out.println("Start!");
}
#Override public void onNext(String t) {
System.out.println(t);
}
#Override public void onError(Throwable t) {
t.printStackTrace();
}
#Override public void onComplete() {
System.out.println("Done!");
}
});
Thread.sleep(500);
// the sequence can now be disposed via dispose()
d.dispose();
Edit
The following examples are ways to get the Disposable out of the onSubscribe method but are generally not recommended:
// field in the owner class
Disposable disposable;
public void doReactive() {
Observable<Long> src = Observable.interval(1, TimeUnit.SECONDS);
src.subscribe(new Observer<Long>() {
#Override
public void onSubscribe(#NonNull Disposable d) {
disposable = d;
}
// ...
});
}
public void cleanup() {
if (disposable != null) {
disposable.dispose();
disposable = null;
}
}
or
SerialDisposable sd = new SerialDisposable();
Observable<Long> src = Observable.interval(1, TimeUnit.SECONDS);
src.subscribe(new Observer<Long>() {
#Override
public void onSubscribe(#NonNull Disposable d) {
sd.set(d);
}
// ...
});
// ...
sd.dispose();
You can use the DisposableObserver which can be easily disposed when you are done observing.
#NonNull Observable<Long> src = Observable.interval(1, TimeUnit.SECONDS);
src.subscribe(new DisposableObserver<Long>() {
#Override
public void onNext(#NotNull Long aLong) {
//Do anything you want to do..
dispose();
}
#Override
public void onError(#NotNull Throwable e) {
//Handle the errors here..
dispose();
}
#Override
public void onComplete() {
dispose();
}
});
You can also use CompositeDisposable to dispose many observers at one time, For more details check this out.
https://www.tutorialspoint.com/rxjava/rxjava_compositedisposable.htm
When I switch from running the observeOn on the Main thread to a newThread, the onNext only runs once.
That only way I can get it to work is to keep it on the Main thread, but then I get that it does too much work on the Main Thread.
Without the setErrorHandler it just crashes (Also, can I use doOnError instead of RxJavaPlugins?)
PS. It works on the emulator fine, but it's on the physical device that the issue comes up.
public void updatePie() {
RxJavaPlugins.setErrorHandler(Functions.<Throwable>emptyConsumer());
Observable<Long> intervalObservable = Observable
.interval(1, TimeUnit.SECONDS)
//.doOnError(Functions.<Throwable>emptyConsumer())
.subscribeOn(Schedulers.io())
.takeWhile(new Predicate<Long>() {
#Override
public boolean test(Long aLong) throws Exception {
if (isMyServiceRunning(MyService.class) == false) {
RxB = false;
}
return RxB;
}
})
.observeOn(Schedulers.newThread());
intervalObservable.subscribe(new io.reactivex.Observer<Long>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(Long aLong) {
triple = mService.Time;
entries.set(0, new PieEntry(mService.Time, "kronk"));
entries.set(1, new PieEntry(mService.Time2, "notre dame"));
pie_chart.notifyDataSetChanged();
pie_chart.invalidate();
}
#Override
public void onError(Throwable e) {
Log.d(TAG, "Pie Update " + e.toString());
}
#Override
public void onComplete() {
Log.d(TAG, "Pie Update completed");
}
});
}
I have an ArrayList of type Object that I'm listening to using an Observable pattern:
//Instances
private List<Store> storeList = new ArrayList<>();
private static PublishSubject<List<Store>> publishStoreListSubject = PublishSubject.create();
private Observer<List<Store>> storeListObserver = new Observer<List<Store>>() {
#Override
public void onSubscribe(Disposable d) { }
#Override
public void onNext(List<Store> storeList) {
//Do work ....
}
#Override
public void onError(Throwable e) { }
#Override
public void onComplete() { }
};
in my onCreate method:
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
publishStoreListSubject.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).subscribe(storeListObserver);
}
In my client side server method, I have the following:
onRetrieveStores() {
publishStoreListSubject.onNext(storeList);
..
..
..
}
PROBLEM: The onNext is being triggered for each item instead of for the whole storeList as one unit. For example: if I have 4 stores, the onNext is being triggered 4 times.
Btw, I do not have any mechanisms that is looping through in my onRetrieveStores
If you want to do some action on when last item has been emitted then you should implement onComplete(). onNext() is called for each new item emitted.
I am using Retrofit for the network call in my android app. Now if the response if something wrong (maybe wrong data), I do not want the onComplete to be executed. Please see the code snippet,
restClient.getService().getProjectDetail(projectId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableObserver<List<Project>>() {
#Override
public void onNext(List<Project> value) {
/*Something wrong in the data and I would like to execute onError*/
}
#Override
public void onError(Throwable e) {
handleError(e, 0, "");
hideProgressDialog();
}
#Override
public void onComplete() {
hideProgressDialog();
}
});
Thanks in advance.
Since your end-consumer can crash, the straightforward way is to catch that exception and delegate to onError:
.subscribeWith(new DisposableObserver<List<Project>>() {
#Override
public void onNext(List<Project> value) {
try {
// something that can crash
} catch (Throwable ex) {
// tell the upstream we can't accept any more data
dispose();
// do the error handling
onError(ex);
}
}
#Override
public void onError(Throwable e) {
handleError(e, 0, "");
hideProgressDialog();
}
#Override
public void onComplete() {
hideProgressDialog();
}
});
On a side note, RxJava does pretty much this in its own operators when dealing with potentially failing user functions: try-catch, cancel the source and signal through onError.
You can use flatMap. ex:
restClient.getService().getProjectDetail(projectId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap(new Function<Integer, Observable<List<Project>>>() {
#Override
public Observable<List<Project>> apply(List<Project> x) {
if(validate(x)){
return Observable.error(new Exception("Response is invalid"));
}else {
return Observable.just(x);
}
}
public boolean validate(List<Project> x){
return x.size()==0;
}
})
.subscribeWith(new DisposableObserver<List<Project>>() {
#Override
public void onNext(List<Project> value) {
/*Something wrong in the data and I would like to execute onError*/
}
#Override
public void onError(Throwable e) {
handleError(e, 0, "");
hideProgressDialog();
}
#Override
public void onComplete() {
hideProgressDialog();
}
});
example other:
Observable.just(1)
.flatMap(new Function<Integer, Observable<Integer>>() {
#Override
public Observable<Integer> apply(Integer x) {
if(validate(x)){
return Observable.error(new Exception("Response is invalid"));
}else {
return Observable.just(x);
}
}
public boolean validate(Integer x){
return x==1;
}
})
.subscribe(new Observer<Integer>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onComplete() {
}
#Override
public void onError(Throwable e) {
Log.e("ERROR", e.getMessage());
}
#Override
public void onNext(Integer integer) {
Log.d("flatMap", "onNext: " + integer.toString());
}
});
You are questioning the basic behaviour of rx-java. if you want call hideProgressDialog only once. you should delete that from onError. the sequence must destroy after problem.
but if you want to get other items in onNext and avoid onError you can use this method on your observable chain:onErrorResumeNext
restClient.getService().getProjectDetail(projectId).onErrorResumeNext(/*Func1*/)
consider this method will emit List<Project> in onNext instead of onError. and onComplete would not call
as long as you use retrofit the onNext only will invoke one times. so the better solution is the first one
Is there a way to listen for events from Picasso when using the builder like:
Picasso.with(getContext()).load(url).into(imageView);
I'm trying to call requestLayout() and invalidate() on the parent GridView so it'll resize properly but I don't know how to set a listener or callback.
I see that Picasso has error event reporting, but is there a success event?
You can use a Callback to get onSuccess and onError events. Just add a new Callback to your request like so:
Picasso.with(getContext())
.load(url)
.into(imageView, new com.squareup.picasso.Callback() {
#Override
public void onSuccess() {
}
#Override
public void onError() {
}
});
Then you can perform any alterations and modifications in the onSuccess callback.
If you need to access the bitmap before it is loaded to the view, try using :
private Target target = new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
}
#Override
public void onBitmapFailed() {
}
}
In the calling method:
Picasso.with(this).load("url").into(target);
Ideally you'd implement Target on a view or view holder object directly.
Hope this helps
Answering #Jas follow up question as a comment to MrEngineer13's answer (since I don't have enough reputation to comment in any answer), you should use the error() method prior to registering the Callback at the into() method, for example:
Picasso.with(getContext())
.load(url)
.error(R.drawable.error_placeholder_image)
.into(imageView, new com.squareup.picasso.Callback() {
#Override
public void onSuccess() {
//Success image already loaded into the view
}
#Override
public void onError() {
//Error placeholder image already loaded into the view, do further handling of this situation here
}
}
);
Square lately has updated Target class and now there are more methods to override (onPrepareLoad and onBitmapFailed):
Target target = new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
};
And you still have to use:
Picasso.with(context).load(url).into(target);
private final Callback mImageCallback = new Callback() {
#Override
public void onSuccess() {
startPostponedEnterTransition();
}
#Override
public void onError() {
startPostponedEnterTransition();
}
};
RequestCreator creator = Picasso.with(getActivity()).load(list.get(position).getId());
creator.into(imageView, mImageCallback);
Try This
Picasso.with(context)
.load(services.get(position).getImageInactive())
.into(holder.icon, new Callback() {
#Override
public void onSuccess() {
holder.imageLoad.setVisibility(View.GONE);
}
#Override
public void onError() {
holder.icon.setImageResource(R.drawable.ic_error_image_load);
}
});
As a complement to other answers, in case you don't know where to use original image view, e.g. ImageView myIV:
Original:
Picasso.with(activity).load(url).into(myIV);
New (inside onBitmapLoaded() of new Target()):
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
myIV.setImageBitmap(bitmap);
}