Rxjava static generic utility method with transformer - android

Recently I saw an RxJavaarticle explaining Transformers and highlighting how they could be used to reuse Schedulers. I tried to use this and inside the same class, this method works fine:
<T>Observable.Transformer<T, T> applySchedulers() {
return new Observable.Transformer<T, T>() {
#Override
public Observable<T> call(Observable<T> observable) {
return observable
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
};
}
I want to move this into a helper class with a static method so I can use it in my whole Androidapp. But when I try to use the method of this class
public class RxFunctions {
public static <T>Observable.Transformer<T, T> applySchedulers() {
return new Observable.Transformer<T, T>() {
#Override
public Observable<T> call(Observable<T> observable) {
return observable
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
};
}
}
inside another class
public void requestLoginState() {
restClient.requestLoginState()
.compose(RxFunctions.applySchedulers())
.subscribe(new TimberErrorSubscriber<LoginStateResponse>() {
#Override
public void onCompleted() {
}
...
it will no longer recognise my Subscriber, error: Cannot resolve method 'subscribe(anonymous com.example.util.rx.TimberErrorSubscriber<com.example.network.retrofit.response.login.LoginStateResponse>)'
I'm using Java8without Retrolambda.
Changing the compose line to
.compose(this.<LoginStateResponse> RxFunctions.applySchedulers())
results in an error for the LoginState type saying Reference parameters are not allowed here
I'm fairly new to RxJava and grateful for any advice.

Edit: now Android does support java 8
You say you are using Java8 on android, but android does not support Java8 without plugins like retrolambda, so I'll asume that you are actually compiling with Java6 (since Java7 only works for KitKat and above).
In that scenario, you might need to make explicit the parametric type of your applySchedulers. I believe you tried to do that when you wrote this.<LoginStateResponse>, but that would only work if your generic method is inside your current class.
In your case what you actually need is to specify the type on the static method call:
.compose(RxFunctions.<LoginStateResponse>applySchedulers())

Related

Using Rxjava to insert , delete from room db

i'm working on project where i have to insert and delete data from room db , so basically i was using the old approach which is to implement Asynctask for background operations but since it is no longer recommended , i decided to use Rxjava instead , i tried to implement it but i'm not getting any result so far , and this is a piece of code where it shows the insertion of data
Completable.fromAction(new Action() {
#SuppressLint("CheckResult")
#Override
public void run() throws Exception {
recordingDb.insertRecording(modelUidd);
}
}).subscribeOn(Schedulers.io());
}
And this is the deletion method
public void DeleteData(modelUidd modelUidd) {
Completable.fromAction(new Action() {
#Override
public void run() throws Exception {
recordingDb.delete(modelUidd);
}
}).subscribeOn(Schedulers.io());
}
So basically i tried to use completable with the operator fromaction , i'm not sure if what i implemented is correct or not , any help would appreciated guys , thank you
The problem is that you are actually not subscribing to the observables, so nothing is happening.
To subscribe to an observable, you have to call the .subscribe() method.
I suggest that your methods defined in your DAO classes (or you "repository" classes), such as DeleteData in your example, return the Observable. Then, you can call the method in the DAO to get the Observable and subscribe to it from (ideally) a ViewModel or, if not, directly from an Activity. The moment you call the subscribe you will trigger the actual insertion or deletion, and will get a response from the onSuccess or onError defined callbacks.
For example:
public class MyViewModel extends ViewModel {
private MyRepository myRepository;
private final CompositeDisposable disposables;
#Inject
public MyViewModel(MyRepository myRepository) {
...
this.myRepository = myRepository;
disposables = new CompositeDisposable();
...
}
public void callObservableInRepository() {
disposables.add(myRepository.myObservable()
.subscribe(onSuccess -> {...} , onError -> {...}));
}
#Override
protected void onCleared() {
disposables.clear();
}
}
You can also check these two other answers for more information:
About async operations in RxJava
Using CompositeDisposable in ViewModel

How to combine Espresso with IdlingResource with RxJava's Subject

I have a problem with combining Google's Espresso test automation library.
In production code the following transformer to apply Schedulers to Observable is used:
public SCProductionUiSchedulersTransformer() {
schedulersTransformer = new Observable.Transformer() {
#Override
public Object call(Object observable) {
return ((Observable) observable)
.subscribeOn(rxFactory.getIoScheduler())
.observeOn(rxFactory.getMainThreadScheduler());
}
};
}
In Espresso automation tests the following is used:
public <T> SCIdlingUiSchedulersTransformer(Context context, final CountingIdlingResource countingIdlingResource) {
schedulersTransformer = new Observable.Transformer<T, T>() {
#Override
public Observable<T> call(Observable<T> observable) {
return observable
.subscribeOn(rxFactory.getIoScheduler())
.observeOn(rxFactory.getMainThreadScheduler())
.doOnSubscribe(new Action0() {
#Override
public void call() {
countingIdlingResource.increment();
}
})
.doOnTerminate(new Action0() {
#Override
public void call() {
countingIdlingResource.decrement();
}
});
}
};
}
CountingIdlingResource is the regular Espresso one used to inform automation Thread that application is not idle and it has to wait for the operation to complete.
For regular Observable the following code works without problems.
The problem is with Observable of type Subject. Of course it does not work, because subscription to Subject is made on Activity start and causes the automation Thread to hang.
Is there any way to align the code to make it work for Subject? Maybe detecting that observable is of type Subject?

RxAndroid - java.lang.IllegalStateException: Another strategy was already registered

I am writing a unit test and need to mock an Observable (from retrofit)
The code in the tested component is as follows:
getApiRequestObservable()
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(...)
In the unit test (against the JVM so AndroidSchedulers are not available) I need to make it all synchronous so my tests will look like:
#Test
public void testSomething() {
doReturn(mockedResponse).when(presenter).getApiRequestObservable();
presenter.callApi();
verify(object,times(1)).someMethod();
}
To do this, I should register hooks in a setUp() method:
#Before
public void setUp() throws Exception {
// AndroidSchedulers.mainThread() is not available here so we fake it with this hook
RxAndroidPlugins.getInstance().registerSchedulersHook(new RxAndroidSchedulersHook() {
#Override
public Scheduler getMainThreadScheduler() {
return Schedulers.immediate();
}
});
// We want synchronous operations
RxJavaPlugins.getInstance().registerSchedulersHook(new RxJavaSchedulersHook(){
#Override
public Scheduler getIOScheduler() {
return Schedulers.immediate();
}
});
}
But this throws the above exception as I am apparently not allowed to register two hooks. Is there any way around that?
The problem is that you're not resetting test state - you can verify that by running single test. To fix your particular problem you need to reset rx plugins state like so:
#Before
public void setUp(){
RxJavaPlugins.getInstance().reset();
RxAndroidPlugins.getInstance().reset();
//continue setup
...
}
You can even wrap the reset into a reusable #Rule as described by Alexis Mas blog post:
public class RxJavaResetRule implements TestRule {
#Override
public Statement apply(Statement base, Description description) {
return new Statement() {
#Override
public void evaluate() throws Throwable {
//before: plugins reset, execution and schedulers hook defined
RxJavaPlugins.getInstance().reset();
RxAndroidPlugins.getInstance().reset();
// register custom schedulers
...
base.evaluate();
}
};
}
}

Does Retrofit support rx.Future/Task/Async/Single?

I've found nice example of usage RxJava at this article:
Subscription subscription = Single.create(new Single.OnSubscribe() {
#Override
public void call(SingleSubscriber singleSubscriber) {
String value = longRunningOperation();
singleSubscriber.onSuccess(value);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1() {
#Override
public void call(String value) {
// onSuccess
Snackbar.make(rootView, value, Snackbar.LENGTH_LONG).show();
}
}, new Action1() {
#Override
public void call(Throwable throwable) {
// handle onError
}
});
But since I am using Retrofit I would like to create RetrofitService and use Single class to combine the result of two requests to backend into one dataset, as described:
When subscribing to a Single, there is only an onSuccess Action and an
onError action. The Single class has a different set of operators than
Observable, with several operators that allow for a mechanism of
converting a Single to an Observable. For example, using the
Single.mergeWith() operator, two or more Singles of the same type can
be merged together to create an Observable, emitting the results of
each Single to one Observable.
Is it possible to achieve this (and how)?
Yes, see Retrofit Adapters
Only worked with Retrofit2.0

Verify interactions in rxjava subscribers

Picture the situation in an MVP pattern where your presenter subscribes to a service returning an observer:
public void gatherData(){
service.doSomeMagic()
.observeOn(Schedulers.io())
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(new TheSubscriber());
}
Now the class TheSubscriber calls onNext a method from the view, say:
#Override public void onNext(ReturnValue value) {
view.displayWhatever(value);
}
Now, in my unit test I would like to verify that when the method gatherData() is called on a non-erroneous situation, the view's method displayWhatever(value) is called.
The question:
Is there a clean way to do this?
Background:
I'm using mockito to verify the interactions and a lot more of course
Dagger is injecting the entire presenter except for TheSubscriber
What have I tried:
Inject the subscriber and mock it in the tests. Looks a bit dirty to me, because if I want to change the way the presenter interacts with the service (Say not Rx) then I need to change a lot of tests and code.
Mock the entire service. This was not so bad, but requires me to mock a lot of methods and I didn't quite reach what I wanted.
Looked up around the internet, but no one seems to have a clean straight way of doing this
Thanks for the help
Assuming that you are using interfaces for service and view in a similar manner:
class Presenter{
Service service;
View view;
Presenter(Service service){
this.service = service;
}
void bindView(View view){
this.view = view;
}
void gatherData(){
service.doSomeMagic()
.observeOn(Schedulers.io())
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(view::displayValue);
}
}
It is possible then to provide mock to control and verify behaviour:
#Test void assert_that_displayValue_is_called(){
Service service = mock(Service.class);
View view = mock(View.class);
when(service.doSomeMagic()).thenReturn(Observable.just("myvalue"));
Presenter presenter = new Presenter(service);
presenter.bindView(view);
presenter.gatherData();
verify(view).displayValue("myvalue");
}
I know its pretty late but may it helps someone, cause i searched pretty long for a solution to your question :D
For me it worked out to add a Observable.Transformer<T, T> as followed:
void gatherData() {
service.doSomeMagic()
.compose(getSchedulerTransformer())
.subscribe(view::displayValue);
}
private <T> Observable.Transformer<T, T> getSchedulerTransformer() {
if (mTransformer == null) {
mTransformer = (Observable.Transformer<T, T>) observable -> observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
return mTransformer;
}
void setSchedulerTransformer(Observable.Transformer<Observable<?>, Observable<?>> transformer) {
mTransformer = transformer;
}
And to set the Transformer just I just passed this:
setSchedulerTransformer(observable -> {
if (observable instanceof Observable) {
Observable observable1 = (Observable) observable;
return observable1.subscribeOn(Schedulers.immediate())
.observeOn(Schedulers.immediate());
}
return null;
});
So just add a #Before method in your test and call presenter.setSchedulerTransformer and it should be able to test this :)
hope this helps and is somehow understandable :D

Categories

Resources