How to create an Observable from OnClick Event Android? - android

I'm new in reactive programming. So I have problem when create a stream from an Event, like onClick, ontouch...
Can anyone help me solve this problem.
Thanks.

You would do something like this:
Observable<View> clickEventObservable = Observable.create(new Observable.OnSubscribe<View>() {
#Override
public void call(final Subscriber<? super View> subscriber) {
viewIWantToMonitorForClickEvents.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (subscriber.isUnsubscribed()) return;
subscriber.onNext(v);
}
});
}
});
// You can then apply all sorts of operation here
Subscription subscription = clickEventObservable.flatMap(/* */);
// Unsubscribe when you're done with it
subscription.unsubscribe();
Since you're using Android then you may already include the contrib rxjava-android dependency now known as ioreactivex:rxandroid.
They already have a class to facilitate this. The method is ViewObservable.clicks. You can use it like so.
Observable<View> buttonObservable = ViewObservable.clicks(initiateButton, false);
buttonObservable.subscribe(new Action1<View>() {
#Override
public void call(View button) {
// do what you need here
}
});
Edit: Since version 1.x, ViewObservable and many helper classes are removed from RxAndroid. You will need RxBinding library instead.
Observable<Void> buttonObservable = RxView.clicks(initiateButton);
buttonObservable.subscribe(new Action1<Void>() {
#Override
public void call(Void x) {
// do what you need here
}
});

For Android development, have a look at Jake Wharton's RxBindings. For example, it allows you to create an observable and subscribe to click events with:
RxView.clicks(myButton)
.subscribe(new Action1<Void>() {
#Override
public void call(Void aVoid) {
/* do something */
}
});
or, even better, with lambda expressions (using either Kotlin, Java 8 or Retrolambda):
RxView.clicks(myButton)
.subscribe(aVoid -> /* do something */);
If you're using Kotlin, it's worth noting that RxBindings also provides Kotlin extension functions that allow you to apply each binding function directly on the target type.

You could use a Subject.
A Subject is a sort of bridge or proxy that acts both as an Subscriber and as an Observable. Because it is a Subscriber, it can subscribe to one or more Observables, and because it is an Observable, it can pass through the items it observes by reemitting them, and it can also emit new items.
public class Events {
public static PublishSubject <Object> myEvent = PublishSubject.create ();
}
When you want to publish something
Events.myEvent.onNext(myObject);
When you want to receive an event
Events.myEvent.subscribe (...);
Edit
**Using Architecture Components LiveData is better because it handles the lifecycle of and activity or fragment and you don't have to worried about unsubscribe from events because it observe the ui components lifecycle.
MutableLiveData<Object> event = new MutableLiveData<>();
when you want to publish something
event.postValue(myObject);
When you want to receive and event
event.observe(lifeCycleOwner, (myObject)->...);

Using RxJava 2:
return Observable.create(new ObservableOnSubscribe<View>() {
#Override
public void subscribe(ObservableEmitter<View> e) throws Exception {
clickView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
e.setCancellable(new Cancellable() {
#Override
public void cancel() throws Exception {
view.setOnClickListener(null);
}
});
e.onNext(view); // Or whatever type you're observing
}
});
}
});
Looks much nicer using lambdas:
return Observable.create(new ObservableOnSubscribe<View>() {
#Override
public void subscribe(ObservableEmitter<View> e) throws Exception {
keypad.setOnClickListener(view -> {
e.setCancellable(() -> view.setOnClickListener(null));
e.onNext(view);
});
}
});
Or just use RxBinding as stated by others.
My solution above is a pretty general pattern for wrapping listeners into an Observable though.

You can do this easily with Kotlin using extension functions. For example you write an extension function like this:
fun View.observableClickListener(): Observable<View> {
val publishSubject: PublishSubject<View> = PublishSubject.create()
this.setOnClickListener { v -> publishSubject.onNext(v) }
return publishSubject
}
And you would use this extension like this:
viewIWantToObserveClicks.observableClickListener().subscribe()

Related

Refresh data with RxJava and Retrofit

I'm using Retrofit with RxJava2 to obtain some data from a Rest API. I want to use a SwipeRefreshLayout to update the view and I'm using a ViewModel to handle the API call, so I want to implement a method in there to refresh the data programmatically.
I want to obtain something like this https://stackoverflow.com/a/34276564/6787552 but instead of having a periodic trigger, I want to do that programmatically when the user pull to refresh.
That's the ViewModel:
public class DashboardViewModel extends ViewModel {
public final Single<Dashboard> dashboard;
public DashboardViewModel() {
dashboard = Api.getDashboard();
refresh();
}
public void refresh() {
// Refresh data
}
}
And in the DashboardFragment:
#Override
public View onCreateView(...) {
...
viewModel.dashboard
.observeOn(AndroidSchedulers.mainThread())
.subscribe(dashboard -> {
binding.setDashboard(dashboard);
binding.swipeRefreshLayout.setRefreshing(false);
});
binding.swipeRefreshLayout.setOnRefreshListener(() -> viewModel.refresh());
...
}
Thank you in advance!
EDIT:
That's what I ended up doing:
public class DashboardViewModel extends ViewModel {
private final BehaviorSubject<Dashboard> dashboard;
public DashboardViewModel() {
dashboard = BehaviorSubject.createDefault(Api.getDashboard());
}
public void refresh() {
// I use a Object because null values are not supported
dashboard.onNext(Api.getDashboard());
}
public Observable<Dashboard> getDashboard(){
return dashboard;
}
}
And then in the DashboardFragment just subscribe to viewModel.getDashbaord()
I'm not 100% sure that I understood what you want to do but if I got the question right, you can do something like this:
put a subject inside the model (probably a BehaviorSubject?)
expose it as an observable to the
view and subscribe to it (instead of subscribing to the single)
in the model, when you
receive a new call to refresh() from the ui, do something like
subject.onNext(Api.getDashboard())
in this way, each call to refresh will cause the emission of a new dashboard, and that will be properly bound by the subscription in the view.

LiveData in Background Threads or non-UI components

Dears. I used to develop Android apps using MVP pattern and now I'm trying the MVVM with Architecture components like DataBind and LiveData.
I wrote my Repository class that provides a LiveData:
LiveData<MyEntity> getById(long id);
For Activity/Fragments I observe the LiveData exposed by ViewModel (that uses my Repository) and everything works fine.
The problem is I have to schedule an Alarm to display a Notification with a text related to MyEntity, so I created an Intent containing my MyEntityId as an Extra.
When the AlarmManager calls my BroadcastReceiver, I need to use the Repository to get MyEntity. The point is how to "Observe" the LiveData inside a non-UI component.
Also, I can start an IntentService (background thread) to avoid accessing the Repository in the Main Thread, and use something like "blockingNext" from RxJava, but I still could not figure a way to wait for LiveData.
What is the correct way of doing this? Note that my Repository may not be implemented using Room, due to legacy issues.
Thanks
The only solution I figured so far was have methods like this in the Repository:
LiveData<MyEntity> getByIdLive(long id);
MyEntity getById(long id);
But this does not look good for me.
So I'd like to ask how is the correct way of implement this.
Best Regards
It's better to avoid such things, but if you really need it and really know what you're doing you can try the following code:
public static <T> void observeOnce(final LiveData<T> liveData, final Observer<T> observer) {
liveData.observeForever(new Observer<T>() {
#Override
public void onChanged(T t) {
liveData.removeObserver(this);
observer.onChanged(t);
}
});
}
#WorkerThread
public static <T> T waitSync(final LiveData<T> liveData) {
final Object lock = new Object();
final Object[] result = new Object[1];
final boolean[] resultReady = new boolean[] {false};
(new Handler(Looper.getMainLooper())).post(new Runnable() {
#Override
public void run() {
observeOnce(liveData, new Observer<T>() {
#Override
public void onChanged(T t) {
synchronized (lock) {
result[0] = t;
resultReady[0] = true;
lock.notify();
}
}
});
}
});
synchronized (lock) {
try {
while (!resultReady[0]) {
lock.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
return null;
}
}
return (T) result[0];
}
You can only observe liveData in UI components like Activity/Fragment.
For your scenario, you can create an observer class which can be observed in non-UI classes as well or you can use EventBus.
to read about observer: https://developer.android.com/reference/java/util/Observer
about EventBus: https://github.com/greenrobot/EventBus

RxJava PublishProcessor does not receive onNext

I'am fairly new to RxJava and try to build up the Model View Intent Pattern in Android. In my View (Activity) i create a PublishProcessor as follows:
private PublishProcessor<MviResultIntent> mPublishProcessor;
mPublishProcessor = PublishProcessor.create();
After the creation I'am calling a method of my presenter with the Processor as a Parameter:
mResultPresenter.bindIntents(mPublishProcessor);
What happens inside the called method:
Disposable processIntents = mPublishProcessor
.subscribeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableSubscriber<MviResultIntent>() {
#Override
public void onNext(MviResultIntent mviResultIntent) {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
//may be ignored
}
});
mCompositeDisposable.add(processIntents);
and in my View Class i call afterwards:
mPublishProcessor.onNext(new MviResultIntent.ProductsIntent());
The PublishProcessor inside my Presenter does not get the onNext Event I'am trying to trigger. Am i missing something? I dont receive onComplete or onError neither.
Any help is appreciated! If you need any further Information feel free to ask. Thanks in advance for your help!
I figured out that i can't subscribe inside a non Android class on the Main Thread. When i change the
.subscribeOn(AndroidSchedulers.mainThread())
to
.subscribeOn(Schedulers.io())
it works.

How to write unit test for RxJava CompositeSubscription

#Override public void onBarcodeReceived(final String barcode) {
view.showProgress();
if (!textUtil.isEmpty(barcode)) {
subscriptions.add(
interactor.getSearchResultByBarcode(barcode).subscribe(subscriberForSearchResults(true)));
}
}
private Subscriber<PriceAndStockActivityViewModel> subscriberForSearchResults(
boolean fromBarcode) {
return new BaseSubscriber<PriceAndStockActivityViewModel>() {
#Override public void onNext(PriceAndStockActivityViewModel priceAndStockActivityViewModel) {
super.onNext(priceAndStockActivityViewModel);
view.updateView(priceAndStockActivityViewModel);
}
#Override public void onError(Throwable e) {
super.onError(e);
view.hideProgress();
view.organizeScreenComponentsByVisibility(true);
view.onError(e);
}
};
}
I've wanted to test method called onBarcodeReceived like below
#Test public void should_updateViewByViewModel_when_AnyBarcodeReceived() {
String barcode = "123123123";
PriceAndStockActivityViewModel viewModel = getPriceAndStockActivityViewModel(barcode);
when(textUtil.isEmpty(barcode)).thenReturn(false);
when(interactor.getSearchResultByBarcode(anyString())).thenReturn(Observable.just(viewModel));
presenter.onBarcodeReceived(barcode);
verify(view).showProgress();
verify(interactor).getSearchResultByBarcode(anyString());
verify(view).updateView(any(PriceAndStockActivityViewModel.class));
}
Since onNext runs in a different thread its normal not to reach view.updateView. It looks simple but I couldn't find how to solve it. Is there any way to verify updateView?
I presume getSearchResultByBarcode() works on a background thread. So I wonder how you're able to change your UI from this background thread?
I'd change the execution of your subscriber to Android's main thread, so that you can safely manipulate the view, regardless if the thread of getSearchResultByBarcode() changes in the future. However will not hardcode the Scheduler directly, rather lets inject it in the presenter class, for example via the constructor. Of course when you're creating the "real" presenter, you'd pass in AndroidSchedulers.mainThread():
public MyPresenter(, Scheduler observeScheduler) {
...
this.observeScheduler = observeScheduler;
}
....
#Override
public void onBarcodeReceived(final String barcode) {
view.showProgress();
if (!textUtil.isEmpty(barcode)) {
subscriptions.add(interactor.getSearchResultByBarcode(barcode)
.observeOn(observeScheduler)
.subscribe(subscriberForSearchResults(true)));
}
}
Then in your test, when constructing the Presenter you'd use Schedulers.immediate() (if you're using RxJava 1.x or Schedulers.trampoline() if you're using RxJava 2.x version. That should work without using any timeout()s in your Unit tests with Mockito ... after all you want them to run as fast as possible.
And one unrelated thing - you can use org.apache.commons.lang3.StringUtils as a substitution of android.text.TextUtils - it has roughly the same functionality but you won't need to mock it in your unit tests.
In order to wait for another thread to complete you can use this Mockito feature: verify with timeout.
verify(view, timeout(100)).updateView(any(PriceAndStockActivityViewModel.class));
Or use some means of thread synchronization like CountDownLatch. See example for Mockito here.

How to create an Observable in Android?

What I want to do is to create a simple in-memory cache just to try Observables out. However I got stuck because I don't understand how to create an observable. This is the code I have gotten so far:
public class MovieCache {
MovieWrapper movieWrapper;
public Observable<MovieWrapper> getMovies() {
//How to create and return an Observable<MovieWrapper> here?
}
public void setCache(MovieWrapper wrapper) {
movieWrapper = wrapper;
}
public void clearCache() {
movieWrapper = null;
}
}
In the getMovies() method I want to create an Observable and return my local field movieWrapper to the subscriber. How can I do this? I tried with using new Observable.just(movieWrapper) but it results in a null exception.
Take a look at this tutorial as it does exactly what you are looking for. Basically you use defer() to make sure you always get the latest version of your cached object:
public class MovieCache {
MovieWrapper movieWrapper;
public Observable<MovieWrapper> getMovies() {
return Observable.defer(new Func0<Observable<MovieWrapper>>() {
#Override
public Observable<MovieWrapper> call() {
return Observable.just(movieWrapper);
}
});
}
public void setCache(MovieWrapper wrapper) {
movieWrapper = wrapper;
}
public void clearCache() {
movieWrapper = null;
}
}
defer() makes sure that you will get the object upon subscription to the Observable not on creation.
Note however that, according to the author of the post:
The only downside to defer() is that it creates a new Observable each
time you get a subscriber. create() can use the same function for each
subscriber, so it's more efficient. As always, measure performance and
optimize if necessary.
As already said, accepted answer has downside
it creates a new Observable each time you get a subscriber
But it is not the only one.
Consumer won't receive any value if he calls getMovies().subscribe(...) before setCache(...) is called.
Consumer should resubscribe if he want to receive any updates (let's say setCache() can be called multiple times.
Of course all of them can be irrelevant in your scenario. I just want to show you another way (I'm sure there are many more).
You can use BehaviorSubject in order to eliminate all these disadvantages.
public class MovieCache {
private BehaviorSubject<MovieWrapper> mMovieCache = BehaviorSubject.create();
public void setCache(MovieWrapper wrapper) {
mMovieCache.onNext(wrapper);
}
public Observable<MovieWrapper> getMovieObservable() {
//use this if consumer want to receive all updates
return mMovieCache.asObservable();
}
public MovieWrapper getMovie() {
//use this if consumer want to get only current value
//and not interested in updates
return mMovieCache.getValue();
}
public void clearCache() {
//CAUTION consumer should be ready to receive null value
mMovieCache.onNext(null);
//another way is to call mMovieCache.onCompleted();
//in this case consumer should be ready to resubcribe
}
public static class MovieWrapper {}
}
Take a look at BehaviorSubject marble diagram.

Categories

Resources