Implement RxJava with Sync Adapters - android

I'm trying to use RxJava through RxAndroid and its others librarys as RxLifecycle to update an Activity after fetching some data from a SyncAdapter. I know how to execute a network call throug a Service when it is call from the Activity. For example:
Observable<String> fetchFromGoogle = Observable.create(new Observable.OnSubscribe<String>() {
#Override
public void call(Subscriber<? super String> subscriber) {
try {
String data = fetchData("http://www.google.com");
subscriber.onNext(data); // Emit the contents of the URL
subscriber.onCompleted(); // Nothing more to emit
}catch(Exception e){
subscriber.onError(e); // In case there are network errors
}
}
});
fetchFromGoogle
.subscribeOn(Schedulers.newThread()) // Create a new Thread
.observeOn(AndroidSchedulers.mainThread()) // Use the UI thread
.subscribe(new Action1<String>() {
#Override
public void call(String s) {
view.setText(view.getText() + "\n" + s); // Change a View
}
});
The problem is that when using a SyncAdapter the SyncManager is the responsable to start the Service to fetch the data. Therefore I don't know how to create an Observable that suscribes on a thread that hasn't been started and that observes on the UI.
I was thinking in creating a Singleton Subject that my Activity can suscribe on, and in the SyncAdapter create an Observable that emitis an event when it is done (after suscribing the Singleton Subject).
What could be a better/right solution?

Related

Android RxJava Thread Reusal, Is it a bad practice?

I am using retrofit and Rxjava to handle api calls for my mvvm android application. Based on some tutorial, i am currently using RxJava like this.
ViewModel.java
CompositeDisposable disposable = new CompositeDisposable();
private void fetchTodolist(){
loading.setValue(true);
disposable.add(
service.getToDoList("A1833")
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableSingleObserver<ApiResponse<ArrayList<TodoItem>>>() {
#Override
public void onSuccess(ApiResponse<ArrayList<TodoItem>> value) {
if(value.getStatus() == 200){
//on call success code
} else {
//on call rejected code
}
}
#Override
public void onError(Throwable e) {
// on call error code
}
})
);
}
And now i want to cache the result of the api call on successful call into room database. So i need to use another async method and tried to reuse the new thread i created before. And here's the code.
private void fetchTodolist(){
loading.setValue(true);
Scheduler a = Schedulers.newThread();
disposable.add(
service.getToDoList("A1833")
.subscribeOn(a)
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableSingleObserver<ApiResponse<ArrayList<TodoItem>>>() {
#Override
public void onSuccess(ApiResponse<ArrayList<TodoItem>> value) {
if(value.getStatus() == 200){
a.scheduleDirect(new Runnable() {
#Override
public void run() {
long inserted = dao.insert(value);
}
});
} else {
//on call rejected code
}
}
#Override
public void onError(Throwable e) {
// on call error code
}
})
);
}
I wonder if it is a bad practice and will lead to a serious problem. And if so, what's the alternative.
Schedulers uses cached references thus newThread() returns the same Scheduler instance.
Schedulers.newThread() == Schedulers.newThread()
Generally you should avoid using newThread because it creates a new thread for every application of the operator. So if you run the sequence multiple times, new worker threads are created and dismissed without any kind of reuse. This is especially true for newThread().scheduleDirect which will start a new thread just for that single runnable and stop it afterwards.
It is recommended you use Schedulers.io() for IO operations so that those underlying worker threads are reused as much as possible later.

RxJava2 - Chaining observables that emit different types

I'm learning RxJava2 and I need to chain three observables:
The first one performs operations on the data:
Completable performOperations(Data data); // performs expensive operations.
The second one uploads data to a server1 and emits percentage progress.
Observable<Integer> uploadToServer1(Data data); // while it performs the upload, it calls several times onNext(progress) and finally calls onComplete().
The third one just informs to a server2 that the upload was done.
Completable informUploadedToServer2(Data data); // just calls a REST API.
I would like to show the progress in my Activity of the second observable and finally show success when the third one finishes successfully. If any of the three observables throws an exception I should show the error in the Activity as well.
I've tried to use concat to chain but it won't compile because uploadToServer1 emits the Integer type and the rest doesn't.
public void upload(Data data, MyCallback callback) {
Observable.concat(performOperations(data).toObservable(), uploadToServer1(data), informUploadedToServer2(data))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new DisposableObserver<Integer>() {
#Override
public void onNext(Integer integer) {
callback.onProgressChanged(integer);
}
#Override
public void onError(Throwable e) {
callback.onError();
}
#Override
public void onComplete() {
callback.onSuccess();
}
});
}
I've seen that if I change to
Observable.concat(performOperations(data).<Integer>toObservable(), uploadToServer1(data), informUploadedToServer2(data).<Integer>toObservable())
it will work, however, is this the recommended approach?
What is more, what if the first observable emits non-Integers?, for example, a DataDiff object which would describe the modification after a certain operation was performed:
Observable<DataDiff> performOperations(Data data);
How should I subscribe so that I can listen for onNext(Integer) and also onNext(DataDiff) so that the Activity can update the view accordingly?
Thanks.
I would do that in a different way, a more "streamy" approach.
First performOperations(), then use andThen operator to concatenate with the Observable<Integer>, and then you can use concatWith so that after that all the elements from the Observable<Integer> are emitted informUploadedToServer2 is executed. You can then handle the Integer emitted in the subscription consumer, if you observeOn(AndroidSchedulers.mainThread) you can than safely notify your Activity there
performOperations(data)
.andThen(uploadToServer1(data))
.concatWith(informUploadedToServer2(data))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Integer>() {
#Override
public void accept(Integer integer) throws Exception {
// notify your Activity here
}
});
In case you needed to intercept the completion of one of the streams, you could use doOnComplete, for instance
performOperations(data)
.doOnComplete(new Action() {
#Override
public void run() throws Exception {
// after performOperations has completed but before
// uploadToServer1 has started
}
})
.andThen(uploadToServer1(data))
// ...
In case performOperations() returned an Observable<DataDiff>, you could use doOnNext to intercept all the events, and then use ignoreElements operator to convert it to a Completable and then use andThen as before
performOperations()
.doOnNext(new Consumer<DataDiff>() {
#Override
public void accept(DataDiff dataDiff) throws Exception {
// handle DataDiff here
}
})
.ignoreElements()
.andThen(uploadToServer1())
// ...

AsyncTask in Fragments to perform Database update

I have over 10 fragments that execute the same kind of task which is :
Retrieving the Data from the server using Retrofit
Starting an Async Task to update the Database (Using ORMLite)
Once the Database is updated, retrieving the new data from the Database
Notify Dataset has changed in the adapter
I'm wondering if it's useless to put the update database code inside an AsyncTask within my fragment once I retrieve the data from the server?
I have trouble understanding what run on the UI thread and what doesn't and should be started as his own thread through an AsyncTask
Here my code:
private void getLocalIncidentTemplate() {
mIncidentTemplate.clear();
mIncidentTemplate.addAll(GenericDAO.getInstance(EntityGroup.class).queryForAll());
Collections.sort(mIncidentTemplate);
Log.e(TAG, "Incident Template count:" + mIncidentTemplate.size());
mListAdapter.notifyDataSetChanged();
spinner.setVisibility(View.GONE);
}
private void getRemoteIncidentTemplate() {
Call<EntityIncident> call = meepServices.getIncidentTemplate();
call.enqueue(new Callback<EntityIncident>() {
#Override
public void onResponse(Call<EntityIncident> call, Response<EntityIncident> response) {
if (response.isSuccessful()) {
new updateIncidentTemplateTask().execute(response.body());
}
}
#Override
public void onFailure(Call<EntityIncident> call, Throwable t) {
t.getStackTrace();
Log.e(TAG, t.toString());
Utils.showToastMessage(getActivity(), "Error retrieving Incidents", true);
}
});
}
private class updateIncidentTemplateTask extends AsyncTask<EntityCategories, Void, Boolean> {
#Override
protected Boolean doInBackground(EntityCategories... params) {
updateIncidents(params[0]);
return true;
}
protected void onPostExecute(Boolean b) {
getLocalIncidentTemplate();
spinner.setVisibility(View.GONE);
}
}
Here is the Database Update Using ORMlite:
private void updateIncident(EntityCategories categories) {
try {
categories.setId("MobilePlan");
//Update base categories
GenericDAO.getInstance(EntityCategories.class).addOrUpdate(categories);
for (EntityCategories.EntityCategory currentCategory : new ArrayList<>(categories.getCategories())) {
if (currentCategory.getmPlans() != null) {
for (EntityPlan myPlan : new ArrayList<>(currentCategory.getmPlans())) {
EntityPlan oldPlan = GenericDAO.getInstance(EntityPlan.class).queryById(String.valueOf(myPlan.getmId()));
myPlan.setCategories(currentCategory);
if (oldPlan != null) {
if (!myPlan.getmDateModification().equals(oldPlan.getmDateModification())) {
GenericDAO.getInstance(EntityPlan.class).addOrUpdate(myPlan);
}
} else {
GenericDAO.getInstance(EntityPlan.class).addOrUpdate(myPlan);
}
}
} else {
continue;
}
GenericDAO.getInstance(EntityLabel.class).addOrUpdate(currentCategory.getmLabel());
currentCategory.setCategories(categories);
GenericDAO.getInstance(EntityCategories.EntityCategory.class).addOrUpdate(currentCategory);
}
} catch (Exception e) {
e.printStackTrace();
}
Log.d(TAG, "DATA updated");
}
For your particular case, you should use the AsyncTask to retrieve data from the backend and place it in the database.
Remember that AsyncTask has three main methods:
onPreExecute() that runs on the UI thread. Useful when you need to prep something that requires UI thread (touching views and whatnot)
doInBackGround() this runs on background thread
onPostExecute() runs also on the UI thread.
In onPostExecute() you could notify your adapter of the new data.
If I were you, I'd use loaders to get notified and retrieve the data off the database. So that the complete chain would be some:
AsyncTask pulls data from the backend and stores it in the database
Your loader will get notified that something changed inside the database and will pull the data from it and call onLoadFinished() method inside your activity/fragment
onLoadFinished() passes the data to the view adapter.
I haven't gone into detail as to how to implement this. I just presented the overall architecture.
I have trouble understanding what run on the UI thread and what doesn't and should be started as his own thread
The short answer:
Everything that might block the UI thread (in other words, might take time) should run on a worker thread (threadpool or dedicated)
DB actions and network requests are classic examples for actions that should always run asynchronously.
In your case I would just use an ORM to wrap all the interaction with the DB, such as ORMlite or any other you find more suitable, in that case you will not have to concern yourself with the inner workings and just provide callbacks for when your calls have finished (successfully or not)

Progress Callbacks In RxAndroid

Started using RxJava and I want to be able to show a progress bar alongside my subscription.
I have a List of entities that I emit using Observable.from. I want to convert each entity into a JSONObject by using an Observable in a flatMap. See below for code.
Observable.from(entities)
.flatMap(new Func1<Entity, Observable<JSONObject>>() {
#Override
public Observable<JSONObject> call(Entity entity) {
return entityToJSON(entity);
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Object>() {
#Override
public void call(Object o) {
// on next
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
throwable.printStackTrace();
Log.e(LOG_TAG,"Error: "+throwable.getLocalizedMessage());
}
}, new Action0() { // onComplete
#Override
public void call() {
// onComplete
}
});
My Question
How do I, during this conversion to JSON return progress updates that I can update my ProgressDialog with?
Possible Solution
As there is a ProgressDialog up, I run this on the main thread and pass the ProgressDialog to the Observable to update. I believe this would work but I want to stay off the ui thread.
You can use
.doOnNext()
For every converted element and change progress bar.
If you want chage multiple times, you need do this on entityToJSON(entity) method. But entityToJSON works on Schedulers.io() thread, and you need use runOnUiThread method or uiHandler from this method. Read more: Accessing views from other thread (Android)
Communicating with the UI Thread: https://developer.android.com/training/multiple-threads/communicate-ui.html

How would I run this statement using RxJava?

Rx way of doing things can be very complex for none and for many reasons...
but I feel there ARE simple ways to do simple things with RX...
How would I simply perform this statement on a background thread and receive the response on the ui thread?
All functions of this object need to run on a background thread. Get, put, clear, and delete.
String city = Paper.get("city");
The base object in Rx is Observable. That object usually wraps an OnSubscribe object, which is simply an extension of Action1 that takes a Subscriber as a parameter.
What all that means is that you just need to define a class that wraps your call and passes the result to the Subscriber:
public class RxPaperGet implements Observable.OnSubscribe<String> {
#Override
public void call(Subscriber<? super String> t1) {
try {
t1.onNext(Paper.get("city"));
} catch (Throwable t) {
t1.onError(t);
return;
}
t1.onCompleted();
}
}
That's a basic example. Now, you would want to wrap that so you can call any function, and not just Paper.get("city"). Something like https://github.com/ReactiveX/RxJavaAsyncUtil/blob/0.x/src/main/java/rx/util/async/operators/OperatorFromFunctionals.java#L44 does that, by allowing you to pass an arbitrary Callable.
Which in your case, would implement as:
Observable<String> res = OperatorFromFunctionals.fromCallable(() -> Paper.get("city"));
(In case you're wondering, this is java8 lambdas brought to android by retrolambda. quite nice to remove the verbosity of Rx)
Once you have your observable, you can subscribe on it, and get results. To execute on the background, and retrieve the results on the ui thread, you would do:
res.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
AndroidSchedulers is provided by rx-android.
Then you can simply be calledback with the result:
.subscribe(city -> Log.d(TAG, city));
That returns a subscription, which is useful if you need to cancel it.
Overall:
OperatorFromFunctionals.fromCallable(() -> Paper.get("city"))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(city -> Log.d(TAG, city));
EDIT: This is not correct. Will not delete the answer though to preserve the comments.
Very simple example:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getPaper()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<String>() {
#Override
public void call(String s) {
Log.d("xxx", s);
}
});
}
private Observable<String> getPaper() {
return Observable.just(Paper.get());
}
where Paper.get() is a long running operation that returns a String. Check the docs for Scheduler.
Don't forget to observe on the main thread if you want to change the UI after receiving the result of your operation, else you will get an exception for changing the UI from outside the UI thread.

Categories

Resources