I'm confused. I not found any articles about that.
Check my example, in this case longOperation will work on Schedulers.io()
private Completable longOperation() throws InterruptedException {
Thread.sleep(5000);
return Completable.complete();
}
private void doSomething() throws InterruptedException {
CompositeDisposable compositeDisposable = new CompositeDisposable();
compositeDisposable.add(Completable.defer(() -> longOperation())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableCompletableObserver() {
#Override
public void onComplete() {
customToast("long operation done");
}
#Override
public void onError(Throwable e) {
}
}));
}
but if I will remove Completable.defer(() -> longOperation()
private void doSomething() throws InterruptedException {
CompositeDisposable compositeDisposable = new CompositeDisposable();
compositeDisposable.add(longOperation()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableCompletableObserver() {
#Override
public void onComplete() {
customToast("long operation done");
}
#Override
public void onError(Throwable e) {
}
}));
}
I don't know where.
On which thread my method 'longOperation' will work ? I ask because my UI is freezed after invoke doSomething without Completable.defer.
Related
Let's say I have two classes A and B.
public class myClassA {
private void asyncMethodA(String url){
Observable.fromCallable((Callable<Void>) () -> null //common part of the code
).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<Void>() {
#Override
public void onSubscribe(#NonNull Disposable d) {
}
#Override
public void onNext(#NonNull Void aVoid) {
someACode();
//This part is different for each class
}
#Override
public void onError(#NonNull Throwable e) {
}
#Override
public void onComplete() {
}
});
}
}
public class myClassB {
private void asyncMethodB(String url){
Observable.fromCallable((Callable<Void>) () -> null //common part of the code
).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<Void>() {
#Override
public void onSubscribe(#NonNull Disposable d) {
}
#Override
public void onNext(#NonNull Void aVoid) {
someBCode();
//This part is different for each class
}
#Override
public void onError(#NonNull Throwable e) {
}
#Override
public void onComplete() {
}
});
}
}
Is it possible to have some sort of code that will help me not repeat the common part for each individual class? If so how do I implement it? It is necessary that the code will be asynchronous, so it won't lock the UI.
I have a subscriber that times out in 10 seconds. Where do I pass what happens once the timeout has occurred?
Service.registerUser(registerUserRequest)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.timeout(10, TimeUnit.SECONDS)
.subscribe(new SingleObserver<RegisterUserResponse>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onSuccess(RegisterUserResponse registerUserResponse) {
}
}
#Override
public void onError(Throwable e) {
Log.e(LogTags.API, "Error occurred while registering new user.");
e.printStackTrace();
}
});
If timeout occurs the onError would be invko TimeoutException so you can check that in onError method with this if:
if( e instanceof TimeoutException)
check this link out : http://reactivex.io/documentation/operators/timeout.html
I am facing with the problem. As far as I know zip method from RxJava waits for all observables to complete.
But am I getting another behaviour.
Here is my code snippet
private PublishSubject<Void> firstSubject;
private PublishSubject<Void> secondSubject;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
loadData();
mDrawerHeaderView.postDelayed(new Runnable() {
#Override
public void run() {
// getSecondSubject().onNext(null);
}
}, 1000);
mDrawerHeaderView.postDelayed(new Runnable() {
#Override
public void run() {
getFirstSubject().onCompleted();
}
}, 1000);
}
protected PublishSubject<Void> createFirstSubject() {
firstSubject = PublishSubject.create();
return firstSubject;
}
protected PublishSubject<Void> createSecondSubject() {
secondSubject = PublishSubject.create();
return secondSubject;
}
protected PublishSubject<Void> getFirstSubject() {
return firstSubject;
}
protected PublishSubject<Void> getSecondSubject() {
return secondSubject;
}
private void loadData() {
Observable<Void> firstSubject = createFirstSubject();
Observable<Void> secondSubject = createSecondSubject();
Observable<Boolean> allDataTask = Observable.zip(firstSubject, secondSubject, new Func2<Void, Void, Boolean>() {
#Override
public Boolean call(Void aVoid, Void aVoid2) {
return true;
}
});
allDataTask
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Boolean>() {
#Override
public void onCompleted() {
Notifications.showSuccessMessage(getApplicationContext(), "COMPLETE");
}
#Override
public void onError(Throwable e) {
Notifications.showErrorMessage(getApplicationContext(), "ERROR");
}
#Override
public void onNext(Boolean aBoolean) {
Notifications.showSuccessMessage(getApplicationContext(), "NEXT");
}
});
}
In this case I got COMPLETE message, but I was expecting to get nothing because the second subject is not completed.
What I am doing wrong ?
Please help me to get desired behaviour.
Yes, it works as expected. It makes perfect sense to receive the onCompleted() here, because if one stream is done, as long as all the elements it emitted are "zipped", there's no way to "zip" anything more, so it's "completed". You can also play with the sequence here.
Say I have a method like the code below, in which a List is flatMapped to individual strings, each of which has some expensive operation applied to them. Is there any way to parallelise the expensive operations, in the same way that I'd use parallelStream() in Java 8?
final List<String> names = new ArrayList<String>() {{
add("Ringo");
add("John");
add("Paul");
add("George");
}};
Observable.just(names).subscribeOn(Schedulers.io())
.flatMap(new Func1<List<String>, Observable<String>>() {
#Override
public Observable<String> call(final List<String> names) {
return Observable.create(new Observable.OnSubscribe<String>() {
#Override
public void call(Subscriber<? super String> subscriber) {
for (String name : names) {
subscriber.onNext(name);
}
}
});
}
})
.map(new Func1<String, String>() {
#Override
public String call(String s) {
//Simulate expensive operation
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return s.toUpperCase();
}
}).subscribe(new Subscriber<String>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(String s) {
Log.v("RXExample", s + " on " + Thread.currentThread().getName());
}
});
For completion, applying the change recommended in the answer looks like the following and works nicely!
final List<String> names = new ArrayList<String>() {{
add("Ringo");
add("John");
add("Paul");
add("George");
}};
Observable.just(names).subscribeOn(Schedulers.io())
.flatMap(new Func1<List<String>, Observable<String>>() {
#Override
public Observable<String> call(final List<String> names) {
return Observable.create(new Observable.OnSubscribe<String>() {
#Override
public void call(final Subscriber<? super String> subscriber) {
for (final String name : names) {
Observable
.just(name)
.subscribeOn(Schedulers.from(Executors.newFixedThreadPool(5)))
.map(new Func1<String, String>() {
#Override
public String call(String s) {
//Simulate expensive operation
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return s.toUpperCase();
}
}).subscribe(new Observer<String>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(String s) {
subscriber.onNext(name);
}
});
}
}
});
}
})
.subscribe(new Subscriber<String>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(String s) {
Log.v("RXExample", s + " on " + Thread.currentThread().getName());
}
});
you can parallelise work with flatMap as in the following example. I am using RxJava2 for testing.
For further explanation please read the flatMap usage from here: http://tomstechnicalblog.blogspot.de/2015/11/rxjava-achieving-parallelization.html
#Test
public void name() throws Exception {
final List<String> names = new ArrayList<String>() {{
add("Ringo");
add("John");
add("Paul");
add("George");
}};
Observable<String> stringObservable = Observable.fromIterable(names)
.flatMap(s -> {
return longWork(s).doOnNext(s1 -> {
printCurrentThread(s1);
}).subscribeOn(Schedulers.newThread());
});
TestObserver<String> test = stringObservable.test();
test.awaitDone(2_000, TimeUnit.MILLISECONDS).assertValueCount(4);
}
private Observable<String> longWork(String s) throws InterruptedException {
return Observable.fromCallable(() -> {
Thread.sleep(1_000);
return s;
});
}
private void printCurrentThread(String additional) {
System.out.println(additional + "_" + Thread.currentThread());
}
i'm making unit test for my application
my unit test class has this method
#Before
public void initialize() {
mContext = InstrumentationRegistry.getTargetContext();
RealmConfiguration realmConfiguration = new RealmConfiguration.Builder(mContext).name("realmTest").inMemory().build();
Realm.setDefaultConfiguration(realmConfiguration);
mWorkoutsModel = new WorkoutsModel(mContext);
mRealm = Realm.getInstance(realmConfiguration);
mWorkoutsModel.registerListener(this);
}
#Test
public void getWorkouts() throws Exception {
mWorkoutsModel.onStart();
mLock.await();
mWorkoutsModel.onStop();
}
#After
public void deInitialize() {
mWorkoutsModel.unRegisterListener();
mRealm.close();
}
and my model
#Override
public void onStart() {
mRealm = Realm.getDefaultInstance();
getDataFromApi();
}
private boolean getDataFromApi() {
Constants.AllAPIs.ALLWorkouts allWorkouts = new Constants.AllAPIs.ALLWorkouts();
if (Permissions.isInternetConnectionExist(mContext)) {
mApiHandler.downLoadDataFromApi(AllWorkouts.class, allWorkouts.getBaseUrl(),
new APIHandler.StringResponseHandler<AllWorkouts>() {
#Override
public void onResponse(AllWorkouts response) {
insertWorkouts(response.getWorkouts());
},
new APIHandler.ErrorResponseHandler() {
#Override
public void onErrorResponse(VolleyError error) {
}
}, TAG);
return true;
} else {
return false;
}
}
private void insertWorkouts(final List<Workout> workouts) {
mCurrentInsertTransaction = mRealm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm bgRealm) {
bgRealm.copyToRealmOrUpdate(workouts);
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
}
});
}
my problem that the unittest calls onStart which create realm object in the model in test thread but volley force onResponse to run on UIThread which makes realm throw exception Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created.
the code runs perfect in normal, but in test it fails
does anyone faced same problem or can solve it ?
i solved my problem by run the test in handler
new Handler(mContext.getMainLooper()).post(new Runnable() {
#Override
public void run() {
try {
mWorkoutsModel.onStart();
mLock.await();
mWorkoutsModel.onStop();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
hope that help somebody