Hi I just start learning Reactive programming using RxJava2.
How do I create a task that runs in the background thread and then complete on main thread using RxJava2.
Example in Android we use AsyncTask just like example below
private class MyTask extends AsyncTask<String, Integer, Boolean>
{
#Override
protected Boolean doInBackground(String... paths)
{
for (int index = 0; index < paths.length; index++)
{
boolean result = copyFileToExternal(paths[index]);
if (result == true)
{
// update UI
publishProgress(index);
}
else
{
// stop the background process
return false;
}
}
return true;
}
#Override
protected void onProgressUpdate(Integer... values)
{
super.onProgressUpdate(values);
int count = values[0];
// this will update my textview to show the number of files copied
myTextView.setText("Total files: " + count);
}
#Override
protected void onPostExecute(Boolean result)
{
super.onPostExecute(result);
if (result)
{
// display a success dialog
ShowSuccessAlertDialog();
}
else
{
// display a fail dialog
ShowFailAlertDialog();
}
}
}
For this example I want to pass in a Array / ArrayList of Strings and it is use to execute some method in the background thread. Then every success result will update my TextView (UI thread). If one of the process fail, I want it to stop directly. Lastly I want to update my Views when the process has completed.
I only manage to get this far
Observable.just(paths).subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<ArrayList<String>>()
{
private boolean result;
#Override
public void onSubscribe(Disposable d)
{
}
#Override
public void onNext(ArrayList<String> paths)
{
for (int index = 0; index < paths.size(); index++)
{
result = copyFileToExternal(paths[index]);
if (result == true)
{
// cant update UI because is in background thread
myTextView.setText("Total files: " + index);
}
else
{
// end the loop
break;
}
}
}
#Override
public void onError(Throwable e)
{
}
#Override
public void onComplete()
{
if (result)
{
// cant display because it is still in background thread
ShowSuccessAlertDialog();
}
else
{
// cant display because it is still in background thread
ShowFailAlertDialog();
}
}
});
I looked at a few tutorials but can't seem to find the answer.
Thanks in advance for the help
I would do something like this:
Observable.fromArray(getPaths())
.map(path -> copyFileToExternal(path))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(aInteger -> Log.i("test", "update UI"),
throwable -> ShowFailAlertDialog),
() -> ShowSuccessAlertDialog());
A good idea is usually to have a "handler" for controlling the subscription to your observer. So that, when you need to stop your background task (for example because the user left the Activity), you can use it. For this purpose you can use subscribeWith instead of subscribe, that receive as input a ResourceObserver: in this way you get a Disposable.
Disposable subscription = Observable.fromArray(getPaths())
.map(path -> copyFileToExternal(path))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new ResourceObserver<Integer>() {
#Override
public void onNext(#NonNull Integer index) {
Log.i("test", "update UI");
}
#Override
public void onError(#NonNull Throwable e) {
ShowFailAlertDialog();
}
#Override
public void onComplete() {
ShowSuccessAlertDialog();
}
});
When you need to stop the task you can just call:
subscription.dispose();
I'm new at this, but I got a working example..
//Observable
Observable.just("input_parameter")
.subscribeOn(Schedulers.io())//creation of secondary thread
.map(new Function<String, String>() {//<input obj,return obj>
#Override
public String apply(String cad){//input obj
Log.d(TAG,"thread :"+Thread.currentThread().getName());
//runs in a secondary thread
return "result text: "+doLongNetworkOperation();
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(MyObserver);//now this runs in main thread
And MyObserver:
//Observer
Observer MyObserver = new Observer() {
#Override
public void onSubscribe(Disposable d) {
Log.d(TAG,"onSubscribe thread:"+Thread.currentThread().getName());
}
#Override
public void onNext(Object value) {
Log.d(TAG,"on next, valor:<<"+value.toString()+">> \n nombre hilo:"+Thread.currentThread().getName());
}
#Override
public void onError(Throwable e) {
Log.d(TAG,"error "+e.toString());
}
#Override
public void onComplete() {
Log.d(TAG,"onCompleted thread:"+Thread.currentThread().getName());
}
};
Plz, let me know if this works for you.
Related
I'm struggling with RxJava2. I want to perform a function on each item of a list. This function :
public void function(final Result result) {
FirebaseFirestore.getInstance().collection(COLLECTION_NAME).document(result.getId()).get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
// do some operation
}
});
}
This function is async and use FirebaseFirestore.
So I tried to use RxJava2 on my list to call the function for every item:
Observable.fromIterable(resultList)
.concatMap(result -> Observable.fromCallable(new Callable<String>() {
#Override
public String call() throws Exception {
function(result);
return "ok";
}
}))
.subscribe(r -> {
// do some operation when all firebase async tasks are done
});
The concatMap works and the function is called for every item of the list. The problem is that I need a callback when all firebase async tasks are done.
Any help would be much appreciated.
I'll try to draw a possible solution:
public class Callback implements OnSuccessListener<DocumentSnapshot> {
private final ObservableEmitter<DocumentSnapshot> emitter;
private final boolean last;
public Callback(boolean lastvalue, ObservableEmitter<DocumentSnapshot> e) {
this.last = lastvalue;
this.emitter = e;
}
#Override
public void onSuccess(DocumentSnapshot value) {
emitter.onNext(value);
if (last) {
emitter.onComplete();
}
}
}
Observable<DocumentSnapshot> observable = Observable.create(new ObservableOnSubscribe<DocumentSnapshot>() {
#Override
public void subscribe(ObservableEmitter<DocumentSnapshot> e) throws Exception {
int i = 1;
for (Result result : resultList) {
/* callback object now knows which is the last request so it can emit the onComplete */
Callback callbackInstance = new Callback(resultList.size() == i, e);
i++;
FirebaseFirestore.getInstance().collection(COLLECTION_NAME)
.document(result.getId()).get().addOnSuccessListener(callbackInstance);
}
}
});
then when the subscriber's onComplete action is hit all the requests to Firebase should be completed.
I need to return a value from Method C in Class B.
I want my code to be processed like : 1 - 2 - 3 - 4 - 5
but it's processed like : 1 - 4 - 5 - 2 - 3
(As you can see, I put some logs in the code.)
I've been working with this problem over a week but I couldn't find the problem.
I removed some useless code in this question.
What is the problem and how can I figure this problem out?
I rewrite the post because someone said I shortened the code too much.
Hope it is enough!
Thank you in advance!
Class A
public class a() {
public void checkConnectivity(Context context) {
this.context = context;
myObservable.subscribe(mySubscriber);
}
private Observable<Integer> myObservable = Observable.create(
new Observable.OnSubscribe<Integer>() {
#Override
public void call(Subscriber<? super Integer> sub) {
Log.d("1", "1");
int connType = cc.getConnectionStatus(context);
sub.onNext(connType);
sub.onCompleted();
}
}
);
private Subscriber<Integer> mySubscriber = new Subscriber<Integer>() {
#Override
public void onNext(Integer connType) {
int code = c.getcode();
Log.d("4", "4");
textView.setText("code : " + code);
}
#Override
public void onCompleted() {
Log.d("5", "5");
}
#Override
public void onError(Throwable e) {
Log.d("-- NetPresenter", "onError");
}
};
}
Class C
public class C() {
public int getcode() {
observable
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Response<ResponseBody>>() {
public void onCompleted() {
Log.d("3", "3");
}
public void onError(Throwable e) {
code = 0;
}
public void onNext(Response<ResponseBody> response) {
code = 1;
Log.d("2", "2");
}
});
return code;
}
}
First of all RxJava is synchronous by default if nothing else specified.
These two lines
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
make your c.getcode() call asynchronous. If you remove these lines, you'll get what you need. I checked it by myself.
P.S. Please note that textView.setText("code : " + code); could throw exception (if you initiate whole chain not from UI thread). So you need to find workaround to run this code in UI thread (Handler with Looper.getMainLooper() could help).
Upd 1. try postById.execute() to run request in the same thread. You can achieve your goal if and only if you make all calls in the same thread. postById.enqueue initialize async call. So you broke it again.
I have 2 Observables that do 2 different jobs that returns their observables
First one : SyncDoctors for getting doctor list from my WebService
public Observable<List<Doctor>> SyncDoctors(){
Observable<List<Doctor>> observable = MyWebService.getInterface().GetAllDoctors();
observable.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.computation())
.subscribe(new Subscriber<List<Doctor>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(List<Doctor> doctors) {
if(doctors.size() == 0){
logger.debug("No Coming Doctors");
return;
}
DoctorDao doctorDao = MyApplication.getDaoSession().getDoctorDao();
doctorDao.deleteAll();
doctorDao.insertInTx(doctors);
logger.debug("Doctors are synced successfully to the database");
logger.info(doctors.size()+" doctors have been added to database");
}
});
return observable;
}
Second Observable for getting patients list from my webservice
public Observable<List<Patients>> SyncPatients(){
Observable<List<Patients>> observable = MyWebService.getInterface().GetAllPatients();
observable.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.computation())
.subscribe(new Subscriber<List<Patients>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(List<Patients> patients) {
if(patients.size() == 0){
logger.debug("No Coming Patients");
return;
}
PatientDao PatientDao = MyApplication.getDaoSession().getPatientDao();
patientDao.deleteAll();
PatientDao.insertInTx(Patients);
logger.debug("Patients are synced successfully to the database");
logger.info(Patients.size()+" Patients have been added to database");
}
});
return observable;
}
Now i want to sync both doctors and patients lists and after both syncs finish, i want to show it on the screen of the tablet:
I have function called SyncAll
public void SyncAll(){
Observable<List<Doctor>> doctorsObservable = SyncDoctors();
Observable<List<Patient>> patientsObservable = SyncPatients();
Observable.concat(doctorsObservable, patientsObservable)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.computation())
.subscribe(new Subscriber<Object>() {
#Override
public void onCompleted() {
// Here the code to show on ListView
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Object o) {
logger.debug("On SyncAll Next!!!");
}
});
}
onNext functions I save the list of doctors and list of patients to the database.
now when I call SyncDoctors() alone, it works
also when I call SyncPatients() alone, it works as well.
when I call SyncAll() the Doctors and Patients are not being saved to the database.
The Question is why the SyncDoctors() and SyncPatients() observables' onNext functions are called when I call SyncAll() !!
It is because you activate chain by calling .subscribe() in
Observable<List<Doctor>> doctorsObservable = SyncDoctors();
Observable<List<Patient>> patientsObservable = SyncPatients();
You first create observable, then subscribe to it of SyncDoctors() and SyncPatients();
After that you return this Observable, but web response is triggered upon observable creation.
To solve that use .map():
public Observable<List<Doctor>> SyncDoctors(){
final Observable<List<Doctor>> observable = MyWebService.getInterface().GetAllDoctors();
observable.observeOn(Schedulers.io())
// in your code you performed db io on main thread, here it is fixed
.subscribeOn(Schedulers.io())
.map(new Func1<List<Doctor>, List<Doctor>>() {
#Override
public List<Doctor> call(List<Doctor> doctors) {
if(doctors.size() == 0){
logger.debug("No Coming Doctors");
return;
}
DoctorDao doctorDao = MyApplication.getDaoSession().getDoctorDao();
doctorDao.deleteAll();
doctorDao.insertInTx(doctors);
logger.debug("Doctors are synced successfully to the database");
logger.info(doctors.size()+" doctors have been added to database");
return doctors;
}
})
.observeOn(AndroidSchedulers.mainThread());
// Notice: use Observable.defer() or you'll get the same result all the tim
return Observable.defer(new Func0<Observable<List<Doctor>>>() {
#Override
public Observable<List<Doctor>> call() {
return observable;
}
});
}
You should not use .concat(), because it executes chain elements consequently. use .zip().first() intead.
There is also one issue: you perform db operations on main thread.
move chain to main thread after db update
Version with .zip:
void syncAll(){
Observable<List<Doctor>> doctorsObservable = SyncDoctors();
Observable<List<Patient>> patientsObservable = SyncPatients();
Observable.zip(doctorsObservable, patientsObservable, new Func2<List<Doctor>, List<Patient>, Boolean>() {
#Override
public Boolean call(List<Doctor> doctors, List<Patient> patients) {
return true;
}
})
.first()
.subscribe(new Action1<Boolean>() {
#Override
public void call(Boolean aBoolean) {
logger.debug("On SyncAll Next!!!");
}
});
}
I created simple activity with infinity progres bar, and I'am trying to run time consuming method using RxJava to prevent UI thread from blocking, but everytime UI thread is blocked. I think my solution has problem with emitting Observable. Can anyone help me? I'am begginer in RX.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void doSomething(View view) {
doHeavyStuff()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(new Action1() {
#Override
public void call(Object o) {
Toast.makeText(getApplicationContext(), "FINISHED", Toast.LENGTH_SHORT).show();
}
})
.subscribe();
}
private Observable doHeavyStuff() {
for (int i = 0; i < 999999999; i++) {
for (int j = 0; j < 2; j++) {
}
}
return Observable.just(1);
}
With RxJava2 a possible solution is:
Version with lambdas:
Single.fromCallable(() -> loadInBackground())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe((resultObject) -> { updateUi(resultObject) });
Version without lambdas:
Single.fromCallable(new Callable<Object>() {
#Override
public Object call() throws Exception {
return loadInBackground();
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Object>() {
#Override
public void accept(Object resultObject) throws Exception {
updateUi(resultObject);
}
});
Example methods used above:
private Object loadInBackground() {
// some heavy load code
return resultObject;
}
private void updateUi(Object resultObject) {
// update your Views here
}
According to docs
Deprecated:
fromFunc0
Unnecessary now that Func0 extends Callable. Just call fromCallable(java.util.concurrent.Callable) instead.
So you could make the call in this way:
Observable.fromCallable(new Callable<Object>() {
#Override
public Object call() throws Exception {
return someMethod();
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).subscribe(new Action1<Object>() {
#Override
public void call(Object object) {
}
});
Your doHeavyStuff() executes computation on calling thread, you just wrap your result into Observable.
In order to wrap computation into observable you should use defer
Observable.defer(new Func0<Observable<Integer>>() {
#Override
public Observable<Integer> call() {
return Observable.just(doHeavyStuff());
}
});
then you can specify threads by subscribeOn and observeOn methods
kotlin use below code to work in background
Single.fromCallable {
// method that run in background
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe()
Also, you can use RxJavaAsyncUtil:
compile 'io.reactivex:rxjava-async-util:0.21.0'
Code:
Observable.fromFunc0(() -> doHeavyStuff())
I'm looking a way to define order(?) of observers.
#GET("/get_user_msgs")
Observable<PrivateMessagesResponse> getPrivateMessages(#QueryMap Map<String, String> params);
For example I gave a Observable from my Rest API created by Retrofit.
In my ListView I'm observing this Observable.
api.getPrivateMessages(params).subscribe(new Observer());
I also have an API wrapper for my Espresso tests and I'm subscribing to same Observable there. This way observer in API wrapper is called first and only then observer in ListView
is called.
public class IdlingWrapper implements Api, IdlingResource {
....
public IdlingWrapper(Api realApi) {
this.realApi = realApi;
}
...
public Observable<PrivateMessagesResponse> getPrivateMessages(#QueryMap Map<String, String> params); {
counter.incrementAndGet();
return wrapObservable(realApi.getPrivateMessages(params));
}
protected <T> Observable<T> wrapObservable(final Observable<PrivateMessagesResponse> observable) {
//what to do here?
}
}
Is there a way to force some observer to be notified after all others are done? Or something similar in that matter?
Something like
Observable observable = getObservable();
observable.subscribeAsLast(new LastObserver());
observable.subscribe(new ObserverA());
observable.subscribe(new ObserverB());
And so that ObserverA would be notified first, then ObserverB and only then LastObserver.
Or any other approach where I could find out when all registered observers were notified and completed.
I'm not exactly sure what you are trying to do in IdlingWrapper, but I think the current implementation is very fragile.
I think the most important thing that needs to happen is to guarantee the observable can only be called once.
Here is a quick implementation to demonstrate that as well as my implementation of wrapObservable.
public class Test {
private static int counter = 0;
private static final List<Observable<?>> list = Collections.synchronizedList(new ArrayList<>());
protected static <T> Observable<T> wrapObservable(final Observable<T> original) {
// run atleast once???
synchronized (list) {
list.add(original);
}
return Observable.create(new Observable.OnSubscribe<Void>() {
#Override
public void call(Subscriber<? super Void> subscriber) {
synchronized (list) {
counter++;
if (!list.contains(original)) {
subscriber.onError(new Exception("You can only subscribe once!"));
return;
}
list.remove(original);
}
// Sleep to make it easier to see things happening...
try {
Thread.sleep(3000);
} catch (InterruptedException ignored) {
}
subscriber.onCompleted();
}
}).flatMap(new Func1<Void, Observable<? extends T>>() {
#Override
public Observable<? extends T> call(Void o) {
return original;
}
}).finallyDo(new Action0() {
#Override
public void call() {
synchronized (list) {
counter--;
if (list.size() == 0 && counter == 0) {
System.err.println("finally");
}
}
}
});
}
public static void main(String[] args) throws InterruptedException {
for(int i = 0; i < 10; i++) {
// running in io thread for simulating async call.
Observable<String> test = wrapObservable(Observable.from("TEST!!!!!!")).subscribeOn(Schedulers.io());
test.subscribe(new Observer<String>() {
#Override
public void onCompleted() {
System.err.println("completed");
}
#Override
public void onError(Throwable e) {
System.err.println("error");
}
#Override
public void onNext(String s) {
System.err.println("next");
}
});
// example of calling the same observable twice.
test.subscribe(new Observer<String>() {
#Override
public void onCompleted() {
System.err.println("completed");
}
#Override
public void onError(Throwable e) {
System.err.println("error");
}
#Override
public void onNext(String s) {
System.err.println("next");
}
});
}
Thread.sleep(10000);
}
}
It seems, that this worked just fine.
protected <T> Observable<T> wrapObservable(final Observable<T> original) {
return Observable.create(new Observable.OnSubscribeFunc<T>() {
#Override
public Subscription onSubscribe(final Observer<? super T> t1) {
original.subscribe(new Observer<T>() {
#Override
public void onCompleted() {
t1.onCompleted();
uiThreadHandler.post(new Runnable() {
#Override
public void run() {
counter.decrementAndGet();
notifyIdle();
}
});
}
#Override
public void onError(Throwable e) {
t1.onError(e);
uiThreadHandler.post(new Runnable() {
#Override
public void run() {
counter.decrementAndGet();
notifyIdle();
}
});
}
#Override
public void onNext(T args) {
t1.onNext(args);
}
});
return Subscriptions.empty();
}
});
}
If you want to just use built in RxJava methods to order your observers, you can use flatMap and range to turn each item into multiple items each with a priority and then filter on priority. Observers are ordered based on how they filter.
Here's a trivial example:
Observable<Pair<Integer, Object>> shared = RxView.clicks(findViewById(R.id.textView))
.flatMap(c -> Observable.range(0, 2).map(i -> Pair.create(i, c)))
.share();
shared.filter(p -> p.first == 1)
.map(p -> p.second)
.doOnSubscribe(c -> Log.d(TAG, "first subscribed doOnSubscribe"))
.subscribe(c -> Log.d(TAG, "first subscribed onNext"));
shared.filter(p -> p.first == 0)
.map(p -> p.second)
.doOnSubscribe(c -> Log.d(TAG, "second subscribed doOnSubscribe"))
.subscribe(c -> Log.d(TAG, "second subscribed onNext"));
If you are doing this all over the place