compositeDisposable += Observable.zip(
someObservable(),
someObservableTwo(), { t1, t2 ->
Pair(first, second)
}
).zipWith(Observable.interval(0, 10, TimeUnit.SECONDS), { t1, t2 ->
t1
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
//success
}, {
//error
})
The main objective of this code to execute every 10 seconds. Logic is not wrong but somehow I am missing something. Hope someone helps
What about following solution?
The source observable will emit an event ever 1_000ms. On each event both API endpoints will be called. When the interval source observable emits a new value, while the Observable#combineLatest is subscribed, it will be unsubscribed and called again (switchMap). If you do not want to cancel out the inner stream on each new emit, you woul use flatMap (NOTE: at max 128 concurrent streams are merged by flatMap by default)
interface Api {
Observable<Integer> call1();
Observable<String> call2();
}
static final class ApiImpl implements Api {
#Override
public Observable<Integer> call1() {
return Observable.just(42);
}
#Override
public Observable<String> call2() {
return Observable.just("42");
}
}
#Test
public void interval() {
TestScheduler testScheduler = new TestScheduler();
Api api = new ApiImpl();
TestObserver<Integer> test = Observable.interval(0, 1_000, TimeUnit.MILLISECONDS, testScheduler)
.switchMap(aLong -> Observable.combineLatest(api.call1(), api.call2(), (integer, s) -> 42))
.test();
testScheduler.advanceTimeBy(1, TimeUnit.MILLISECONDS);
test.assertNotComplete().assertValueCount(1)
.assertValues(42);
testScheduler.advanceTimeBy(2_000, TimeUnit.MILLISECONDS);
test.assertNotComplete().assertValueCount(3)
.assertValues(42, 42, 42);
}
Related
There are cases when I need to chain RxJava calls.
The simplest one:
ViewModel:
fun onResetPassword(email: String) {
...
val subscription = mTokenRepository.resetPassword(email)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(
//UI update calls
)
...
}
My Repository:
fun resetPassword(email: String): Single<ResetPassword> {
return Single.create { emitter ->
val subscription = mSomeApiInterface.resetPassword(email)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe({
emitter.onSuccess(...)
}, { throwable ->
emitter.onError(throwable)
})
...
}
}
My Question
Do I need to Add:
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
for both calls to avoid any app freeze? or the second one for API call is enough?
No, you don't need to add
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
for the repo and the viewmodel.
.observeOn usually should be called right before handling the ui rendering. So usually, you'll need it in the ViewModel right before updating the ui or emitting the LiveData values.
Also, you properly don't need to subscribe to mSomeApiInterface in your repo, I think it would be better off to just return in as it's from your method up the chain, somthing like this:
fun resetPassword(email: String): Single<ResetPassword> {
return mSomeApiInterface.resetPassword(email);
}
and if you have any mapping needed you can chain it normally
fun resetPassword(email: String): Single<ResetPassword> {
return mSomeApiInterface.resetPassword(email)
.map{it -> }
}
This way you can write your ViewModel code as follow
fun onResetPassword(email: String) {
...
// note the switcing between subscribeOn and observeOn
// the switching is in short: subscribeOn affects the upstream,
// while observeOn affects the downstream.
// So we want to do the work on IO thread, then deliver results
// back to the mainThread.
val subscription = mTokenRepository.resetPassword(email)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
//UI update calls
)
...
}
This will run the API request on the io thread, will returning the result on the mainThread, which is probably what you want. :)
This artical has some good examples and explanations for subscribeOn and observeOn, I strongly recommend checking it.
Observable<RequestFriendModel> folderAllCall = service.getUserRequestslist(urls.toString());
folderAllCall.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map(result -> result.getRequested())
.subscribe(this::handleResults, this::handleError);
private void handleResults(List<Requested> folderList) {
if (folderList != null && folderList.size() != 0) {
usersList.addAll(folderList);
}
adapter.notifyDataSetChanged();
}
}
private void handleError(Throwable t) {
Toast.makeText(getContext(),t.getMessage(),Toast.LENGTH_LONG).show();
}
in interface:
#Headers({ "Content-Type: application/json;charset=UTF-8"})
#GET
Observable<RequestFriendModel> getUserRequestslist(#Url String url);
POJO model :
public class RequestFriendModel {
#SerializedName("requested")
#Expose
private List<Requested> requested = null;
public List<Requested> getRequested() {
return requested;
}
public void setRequested(List<Requested> requested) {
this.requested = requested;
}
}
Is there RxJava(Rxandroid) equivalent of Handler.runWithScissors(final Runnable r, long timeout)?
I have a problem that getting the long-running result in workerThread asynchronously. (mApplication.startApp())
At the same time, notify the progress in uiThread, my solution is below:
/**
* #return Observable<String> that subscribe the progress.
*/
public Observable<String> startApp() {
Subject<String> mAppState = BehaviorSubject.create();
Observable.just("Initialize...")
.observeOn(mSchedulerProvider.single())
.doOnNext((state) -> {
mAppState.onNext(state);
mApplication.startApp();
})
.doOnError((error) -> mAppState.onError(error))
.map((state) -> "Initialization Complete.")
.doOnNext((state) -> {
TimeUnit.MILLISECONDS.sleep(1000);
mAppState.onNext(state);
mAppState.onComplete();
})
.compose(bindToLifecycle())
.subscribe();
return mAppState.compose(bindToLifecycle())
.observeOn(mSchedulerProvider.ui());
}
Somebody has better solution? Thanks.
I dont think that your solution is great below you have a simple solution
when you do your job in computation or io and you update ui every 1 second
Observable.just(1)
.observeOn(Schedulers.computation())
.map(integer -> {
// do your long job or computation
return integer;
})
.skip(1, TimeUnit.SECONDS)
.observeOn(AndroidScheduler.mainThread())
.map(o -> {
//UI update
return o;
}).subscribe();
I need to do:
Request 2 lists of news from different websites
Combine results from requests
Sort items by date
Get 10 newest news
Save them
Show complete message
For example, I have this two observables:
Observable<RegionalNews> regionalNews;
Observable<NationalNews> nationalNews;
public interface NewsNationalService {
#GET("news/national")
Observable<News> getNationalNews();
}
public interface NewsRegionalService {
#GET("news/regional")
Observable<News> getRegionalNews();
}
You can use zip operator to call 2 requests async and save or process their data on response.
For example.
Below are two Observable
Observable<ResponseOne> responseOneObservable = getRetrofitClient().getDataOne()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
Observable<ResponseTwo> responseTwoObservable = getRetrofitClient().getDataTwo()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
Using zip operator on above two Observable as below.
Observable<ArrayList<TestData>> testDataObservable = Observable.zip(responseOneObservable, responseTwoObservable, new Func2<ResponseOne, ResponseTwo, ArrayList<TestData>>() {
#Override
public ArrayList<TestData> call(ResponseOne responseOne, ResponseTwo responseTwo) {
ArrayList<TestData> testDataList = new ArrayList();
// process data from response responseOne & responseTwo
return testDataList;
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<ArrayList<TestData>>() {
#Override
public void onNext(ArrayList<TestData> testDataList) {
}
#Override
public void onCompleted() {
Log.d(TAG, "onCompleted" );
// you can show alert here or do something when completed
}
#Override
public void onError(Throwable t) {
Log.d(TAG, "onError Throwable: " + t.toString() );
}
});
If you don't want to do something specific with the combined results, then merge() is enough:
Observable<RegionalNews> regionalNews = ...;
Observable<NationalNews> nationalNews = ...;
Observable
.merge(regionalNews, nationalNews)
.ignoreElements()
.observeOn(AndroidSchedulers.mainThread())
.doOnComplete(() -> { /* show alert */ })
.subscribe()
Well it depends, as always. Do you need to process the returned values down the chain, or just save it?
In this implementation I use Single and Completable. You subscribe to the completable and you will get notified when both Singles finished.
#Test
public void name() throws Exception {
TestScheduler testScheduler = new TestScheduler();
Single<Long> request1 = Single.timer(1000, TimeUnit.MILLISECONDS, testScheduler)
.doOnSuccess(aLong -> {
System.out.println("save to db.");
});
Single<Long> request2 = Single.timer(500, TimeUnit.MILLISECONDS, testScheduler)
.doOnSuccess(aLong -> {
System.out.println("save to db.");
});
Completable completable = Single.zip(request1, request2, (aLong, aLong2) -> aLong).toCompletable();
TestObserver<Void> test = completable.test();
testScheduler.advanceTimeBy(1010, TimeUnit.MILLISECONDS);
test.assertComplete();
}
You also can use flatMapCompletable instead of doOnSuccess
#Test
public void name() throws Exception {
TestScheduler testScheduler = new TestScheduler();
Completable request1 = Single.timer(1000, TimeUnit.MILLISECONDS, testScheduler)
.flatMapCompletable(this::saveToDb);
Completable request2 = Single.timer(500, TimeUnit.MILLISECONDS, testScheduler)
.flatMapCompletable(this::saveToDb);
// need to cheat here, becuase completeable does not provide zip
Completable completable = Single.zip(request1.toSingle(() -> 1), request1.toSingle(() -> 1), (aLong, aLong2) -> aLong)
.toCompletable();
TestObserver<Void> test = completable.test();
testScheduler.advanceTimeBy(1010, TimeUnit.MILLISECONDS);
test.assertComplete();
}
private Completable saveToDb(long value) {
return Completable.complete();
}
zip is the way to combine observables. Combining their results is just a consequence.
If you want to wait for both observables to finish (complete), the easiest way is to use zip. You just don't have to use the results of your requests in the combining function. Just use this function as a way to emit something different when both of those calls finish. When this function emits an item:
[...] do something when all requests completed (show alert for example)
For example like this (executing someOtherCall when both of those requests finish):
Observable<Integer> obs1 = ...;
Observable<Long> obs2 = ...;
Observable.zip(obs1, obs2, new Func2<Integer, Long, String>() {
#Override
public String call(Integer integer, Long aLong) {
return "something completely different";
}
}).flatMap(new Func1<String, Observable<Float>>() {
#Override
public Observable<Float> call(String s) {
return performSomeOtherCall();
}
}).subscribe(...);
I have a scenario about using RxJava with interval operator. In fact, I can set interval for Job A. Like that
Observable
.interval(0, 10, TimeUnit.SECONDS)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.map(new Func1<Long, List<Bean>>() {
#Override
public List<Bean> call(Long pLong) {
return null;
}
})
.subscribe(new Action1<List<Bean>>() {
#Override
public void call(List<Bean> pO) {
if (pO.size() > 0) setData(pO);
}
});
But I tried to set interval for Job A, Job B, Job C but they have different interval between them.
Job A, Job B, Job C (30 seconds)
Job A -> 5 seconds -> Job B -> 10 seconds -> Job C
Observable
.interval(0, 30, TimeUnit.SECONDS)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.flatMap(new Func1<Long, Observable<Long>>() {
#Override
public Observable<Long> call(Long pLong) {
//Job A
return Observable.interval(0, 5, TimeUnit.SECONDS);
}
})
.flatMap(new Func1<Long, Observable<Long>>() {
#Override
public Observable<Long> call(Long pLong) {
//Job B
return Observable.interval(0, 10, TimeUnit.SECONDS);
}
})
.subscribe(new Action1<Long>>() {
#Override
public void call(Long pO) {
//Job C
if (pO.size() > 0) setData(pO);
}
});
I tried to use something like this but no luck. I am new to Rx, appreciate any comment about it.
.interval() generates sequence of events, try using .timer() for triggering another job, or .interval().take(1). You will start job B (10 seconds) every 5 seconds (overlapping intervals?)
I've got an EditText view and TextWatcher for it, in onTextChanged method I have to requst server for result with query from EditText field.
In my presenter I use rx for that, but i need to delay search until user's input ends. At this moment i've got this:
service.getData(query)
.delaySubscription(REQUEST_DELAY_FROM_SERVER, TimeUnit.MILLISECONDS, Schedulers.io())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
data-> {
getViewState().showData(data);
},
error -> {
Log.e(this.getClass().getSimpleName(), error.getMessage(), error);
}
);
But delaySubscription does not work as desired. It collects all call and after delay sends every of them. I have to do same as if I had used handler.postDelayed(), when only once request will be send.
Edit 2:
The saple of a presenter in RxJava2
class Presenter {
private PublishSubject<String> queryPublishSubject = PublishSubject.create();
public Presenter() {
queryPublishSubject
.debounce(1000, TimeUnit.MILLISECONDS)
// You might want to skip empty strings
.filter(new Predicate<CharSequence>() {
#Override
public boolean test(CharSequence charSequence) {
return charSequence.length() > 0;
}
})
// Switch to IO thread for network call and flatMap text input to API request
.observeOn(Schedulers.io())
.flatMap(new Function<CharSequence, Observable<...>() {
#Override
public Observable<...> apply(final CharSequence charSequence) {
return ...; // Call API
}
})
// Receive and process response on Main thread (if you need to update UI)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(...);
}
public void onSearchTextChanged(String query) {
queryPublishSubject.onNext(query);
}
}
Edit 1:
The same code in RxJava 1:
class Presenter {
private PublishSubject<String> queryPublishSubject = PublishSubject.crate();
public Presenter() {
queryPublishSubject
.debounce(1000, TimeUnit.MILLISECONDS)
// You might want to skip empty strings
.filter(new Func1<CharSequence, Boolean>() {
#Override
public Boolean call(CharSequence charSequence) {
return charSequence.length() > 0;
}
})
// Switch to IO thread for network call and flatMap text input to API request
.observeOn(Schedulers.io())
.flatMap(new Func1<CharSequence, Observable<...>() {
#Override
public Observable<...> call(final CharSequence charSequence) {
return ... // Call API
}
})
// Receive and process response on Main thread (if you need to update UI)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(...);
}
public void onSearchTextChanged(String query) {
queryPublishSubject.onNext(query);
}
}
Initial answer (with RxBinding and RxJava 1)
The correct answer is to use Debounce, but besides that there are some other tricks you might find useful
textChangeListener = RxTextView
.textChanges(queryEditText)
// as far as I know, subscription to textChanges is allowed from Main thread only
.subscribeOn(AndroidSchedulers.mainThread())
// On subscription Observable emits current text field value. You might not need that
.skip(1)
.debounce(1000, TimeUnit.MILLISECONDS)
// You might want to skip empty strings
.filter(new Func1<CharSequence, Boolean>() {
#Override
public Boolean call(CharSequence charSequence) {
return charSequence.length() > 0;
}
})
// Switch to IO thread for network call and flatMap text input to API request
.observeOn(Schedulers.io())
.flatMap(new Func1<CharSequence, Observable<...>() {
#Override
public Observable<...> call(final CharSequence charSequence) {
return ... // Call API
}
})
// Receive and process response on Main thread (if you need to update UI)
.observeOn(AndroidSchedulers.mainThread())
I have something similar for an address research combining with RxAndroid could give something like that :
RxTextView.textChanges(searchEditText)
.debounce(100, TimeUnit.MILLISECONDS)
.subscribe(....);
The debounce operator will wait in this case that the observable stop to emit for 100ms before emitting the next value.
Try using debounce instead. For eg. code below look for changes in a TextView and do something when there is a change but with a debounce of 100 ms
RxTextView
.textChanges(queryEditText)
.debounce(100, TimeUnit.MILLISECONDS)
.doOnNext(new Action1<CharSequence>() {
#Override
public void call(CharSequence charSequence) {
}
})
.subscribe();