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.
Related
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.
I'm using Mockito to test my views but my tests are failing because of a method that is supposed to be called after a retrofit call is complete. How can I mock a view who's method is called by presenter after completion of a retrofit call? I'd like to verify that unBlockUI() below has been called. My tests show blockUI() is called but unblockUI() is not being called.
I get a fail message
Wanted but not invoked:
view.unBlockUI();
In my presenter I have the method
public void fetchResults(){
view.blockUI();
ResultsDataService resultsDataService = new ResultsDataService()
resultsDataService.getAllResults(new Callback<Catalog>() {
#Override
public void onResponse(Call<Catalog> call, Response<Catalog> response) {
view.unBlockUI();
}
#Override
public void onFailure(Call<Catalog> call, Throwable t) {
view.unBlockUI();
t.printStackTrace();
}
})
}
Results data service.
public class ResultsDataService {
private final RestApi restApi;
public CatalogDataService() {
//here I have a class that builds the REST Service
restApi = RestServiceBuilder.createService(RestApi.class);
}
public void getAllResults() {
Call<Catalog> call = restApi.getAllResults();
call.enqueue(callback);
}
}
my test method
#Test
public void shouldFetchAllResults_allOK() {
presenter.fetchResults();`
verify(view).blockUI();//This is called
verify(view).unBlockUI();//this is not called
}
I think one possible solution is to mock ResultsDataService to call the onResponse method of any callback every time getAllResults is called.
Unfortunately, the way you're creating your ResultsDataService inside fetchResults makes it really hard to do this. This is what we call tight coupling. You have a method that depends strictly on ResultsDataService with no chance to change it. Therefore you cannot control the presenter from the outside. As a rule of thumb, every time you see the new operator that's a sign of tight coupling.
Usually we use dependency injection to solve this. One way you can do it in your code is simply change the fetchResults method to receive the service as an argument:
public void fetchResults(#NonNull ResultsDataService service) {
// ...
}
It might not seem much, but now in the test you can pass in a configured mock and in your app you just pass in the real service.
Say now in your test you'd configure a mock like so:
ResultDataService service = mock(ResultDataService.class);
doAnswer(new Answer() {
#Override
public Object answer(InvocationOnMock invocation) throws Throwable {
Call call = (Call) invocation.getArgument(0);
call.onResponse(call, <some response here>);
return null;
}
}).when(service.getAllResults(any(Call.class)));
You can now use this to pass it to your presenter fetchResults.
What does the mock above do? It will call the onResponse method of the passed in argument. So basically it will call right away the onResponse callback when you call fetchResults. In your case this will in turn call unBlockUI.
Notice you can do something similar to test the onFailure. You should also make ResultsDataService an interface, so your presenter doesn't depend on concrete implementations, but just interfaces. This is much more flexible.
Hope this helps. Remember, this is one way of doing this and not the single way.
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.
Imagine you need to load and save some Data in one of your Fragments.
I want to use RX Java. How do you deal with multiple subscriptions on one Fragment ? AndroidObservable.bindFragment does the Job. But how can i use it when i need more subscriptions ?
public class MyFragment extends SomeFragment implements Observer<List<Item>>
{
private Subscription mReadSubscription;
private Subscription mWriteSubscription;
private JSONLoader mLoader;
#Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
final File theFile = new File(getActivity().getFilesDir(), FILE_NAME);
mLoader = new JSONLoader(theFile);
mReadSubscription = mLoader.getReadSubscription(this);
mWriteSubscription = mLoader.createWriteSubscription(new WriteObserver(), Collections.EMPTY_LIST);
The idea behind this is to save and load items using loader.load() loader.save(), each of this will result in an observer being used (mReadSubscription,mWriteSubscription).
The WriteObserver is just a simple Bean implementing the Observer again, but there is the part i do not understand: The #bindFragment Method checks for instances of Fragment. As WriteObserver is not an Fragment i cause an Exception. But i cant register a second observer because of Generics.
Im pretty sure i know to less about RX, anyone can point me in in a right direction to solve this ?
[Update 1]
There is my WriteObserver:
private final class WriteObserver implements Observer<Void> {
#Override
public void onCompleted() {
Toast.makeText(getActivity(), "Save Successful", Toast.LENGTH_SHORT).show();
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Void aVoid) {
}
}
That design isn't working as WriteObserver is not a Fragment, cause an Exception when you doing:
java.lang.IllegalArgumentException: Target fragment is neither a native nor support library Fragment
AndroidObservable.bindFragment(observer, source)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
So, how can i get around that limitation ?
As I understand, mLoader#getReadSubscription() and mLoader#createWriteSubscription() creates steams and subscribe to it.
I don't think that the right way to do it, as if mLoader subscribe to streams, it should deal how to unsubscribe to it too.
As you want to use AndroidObservable.bindFragment, mLoader#getReadSubscription() and mLoader#createWriteSubscription() should return Observable and not Subscription.
So, in your fragment, you'll be able to write :
#Override
public void onCreate(final Bundle savedInstanceState) {
// [...]
Observable<?> readObs = mLoader.getReadSubscription();
mReadSubscription = AndroidObservable.bindFragment(this, readObs).subscribe(this);
Observable<?> writeObs = mLoader.createWriteSubscription(Collections.emptyList());
mWriteSubscription = AndroidObservable.bindFragment(this, writeObs).subscribe(new WriteObserver());
}
So, how can i get around that limitation ?
It's not a limitation.
public static <T> Observable<T> bindFragment(Object fragment, Observable<T> source) {
// ...
}
The first argument should be your fragment, not your Observer. That why you should write your code as :
yourSubscription = AndroidObservable.bindFragment(yourFraglebt, yourObservable).subscribe(yourObserver);
so in your case :
AndroidObservable.bindFragment(this, source) // as bindFragment observeOn mainThread, you can remove the observeOn method call
.subscribeOn(Schedulers.io())
.subscribe(observer);
and not
AndroidObservable.bindFragment(observer, source)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
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()