In one of my Android activities I need to perform multiple queries to Firebase to finally show something to the user.
In summary, I need to check in a Users reference to check which course step he is currently in, then I need to read the contents of the Course to load it.
What I´m currently doing is that I have two nested listeners like this:
ref1.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
ref2.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//do the work
}
});
}
});
Is there a better way to do this queries when you need them sequentially?
TL;DR
Just like Parse did with Bolts, Google also provided a task framework that implement JavaScript promises. So, instead of nesting listeners, you can create a sequence of tasks.
The result will be sent to addOnSuccessListener if all tasks execute successfully.
If any of them fail during the use case execution, the sequence will be aborted and the exception is passed to addOnFailureListener.
public Task<Course> execute() {
return Tasks.<Void>forResult(null)
.then(new GetUser())
.then(new GetCourse());
}
public void updateInBackground() {
Tasks.<Void>forResult(null)
.then(new GetUser())
.then(new GetCourse())
.addOnSuccessListener(this)
.addOnFailureListener(this);
}
#Override
public void onFailure(#NonNull Exception error) {
Log.e(TAG, error.getMessage());
}
#Override
public void onSuccess(Customer customer) {
// Do something with the result
}
Description
Suppose you wish to download two object of type User and Course from Firebase.
You need to create the first task of your sequence using the Tasks API. Your options are:
Create a successful task using Tasks.forResult
Create a TaskCompletionSource, set the result or exception values, then return a task.
Create a task from a callable.
I prefer the first option mostly due code legibility. If you you need run the tasks in your own executor, you should use the first or second option.
Now let's create two Continuation tasks two download each one:
class GetUser implements Continuation<Void, Task<User>> {
#Override
public Task<User> then(Task<Void> task) {
final TaskCompletionSource<User> tcs = new TaskCompletionSource();
ref1.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onCancelled(DatabaseError error) {
tcs.setException(error.toException());
}
#Override
public void onDataChange(DataSnapshot snapshot) {
tcs.setResult(snapshot.getValue(User.class));
}
});
return tcs.getTask();
}
}
and
class GetCourse implements Continuation<User, Task<Course>> {
#Override
public Task<Course> then(Task<User> task) {
final User result = task.getResult();
final TaskCompletionSource<Course> tcs = new TaskCompletionSource();
ref2.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onCancelled(DatabaseError error) {
tcs.setException(error.toException());
}
#Override
public void onDataChange(DataSnapshot snapshot) {
tcs.setResult(snapshot.getValue(Course.class));
}
});
return tcs.getTask();
}
}
According the documentation, call getResult() and allow the RuntimeExecutionException to propagate to propagate failure from the completed Task.
The RuntimeExecutionException will be unwrapped such that the Task returned by continueWith(Continuation) or continueWithTask(Continuation) fails with the original exception.
According to the Firebase blog: https://firebase.googleblog.com/2016/09/become-a-firebase-taskmaster-part-3_29.html
We could implement a chain of asynchronous task as the following:
public Task<ClassReturnedByTask3> wrapAllTask() {
return Tasks.call(new Task1())
.continueWithTask(new Task2())
.continueWithTask(new Task3());
}
Where Task1 through Task3 are defined as:
static class Task1 implements Callable<ClassReturnedByTask1> {
#Override
public ClassReturnedByTask1 call() throws Exception {
ClassReturnedByTask1 result = new ClassReturnedByTask1();
return result;
}
}
static class Task2 implements Continuation<ClassReturnedByTask1, Task<ClassReturnedByTask2>> {
#Override
public Task<ClassReturnedByTask2> then(Task<ClassReturnedByTask1> task) {
final TaskCompletionSource<ClassReturnedByTask2> tcs = new TaskCompletionSource();
ClassReturnedByTask1 resultFromTask1 = task.getResult();
ClassReturnedByTask2 result = new ClassReturnedByTask2();
tcs.setResult(result);
return tcs.getTask();
}
}
static class Task3 implements Continuation<ClassReturnedByTask2, Task<ClassReturnedByTask3>> {
#Override
public Task<ClassReturnedByTask3> then(Task<ClassReturnedByTask2> task) {
final TaskCompletionSource<ClassReturnedByTask3> tcs = new TaskCompletionSource();
ClassReturnedByTask2 resultFromTask2 = task.getResult();
ClassReturnedByTask3 result = new ClassReturnedByTask3();
tcs.setResult(result);
return tcs.getTask();
}
}
To execute the wrapAllTask() function, you can run:
Task<ClassReturnedByTask3> tasks = wrapAllTask();
tasks.addOnSuccessListener(new OnSuccessListener<ClassReturnedByTask3>() {
#Override
public void onSuccess(ClassReturnedByTask3 resultFromTask3) {
// do something
}
});
Related
I am new to ReactiveX and I have a case where I want my observable to emit data to a late subscriber(whenever the observer subscribes, observable should emit the same data that it emitted previously). I made this Observable class that provide ReplaySubject's same instance to all observers (it is singleton class).
public class AccountsObservable {
private static ConnectableObservable<String> hotObservable;
private static AccountsObservable accountsObservable;
public static AccountsObservable getObject() {
if (accountsObservable == null) {
accountsObservable = new AccountsObservable();
}
return accountsObservable;
}
public ConnectableObservable<String> getObservable() {
if (hotObservable == null) {
Observable<String> observable = ReplaySubject.create(new ObservableOnSubscribe<String>() {
#Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
emitter.onNext("XYZ");
emitter.onComplete();
}
});
hotObservable = observable.replay();//publish
}
return hotObservable;
}
}
Similarly, this is the observer class that creates new observer instance.
public class AccountsObserver {
AccountsFetchListener listener;
public AccountsObserver(AccountsFetchListener listener) {
this.listener = listener;
}
public Observer<String> getObserver() {
return new Observer<String>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(String accounts) {
listener.onSuccess(accounts);
}
#Override
public void onError(Throwable e) {
listener.onFailure();
}
#Override
public void onComplete() {
}
};
}
public interface AccountsFetchListener {
void onSuccess(String accounts);
void onFailure();
}
}
Here is the function where I test these observables
private void testObs() {
ConnectableObservable<String> observable = AccountsObservable.getObject().getObservable();
Observer<String> observer = new AccountsObserver(new AccountsObserver.AccountsFetchListener() {
#Override
public void onSuccess(String accounts) {
Log.e("DATA -> ", accounts);
}
#Override
public void onFailure() {
}
}).getObserver();
observable.subscribe(observer);
observable.connect();
}
I called this function "testObs()" 5 times but it emitted data only 2 times. The problem seems to be in AccountsObservable class where I provide ReplaySUbject's instance. Thanks
Your code runs fine as it is, your logs are being suppressed in logcat as per this:
We declared an application as too chatty once it logs more than 5 lines a second. Please file a bug against the application's owner that is producing this developer-verbose-debug-level class logging spam. The logs are 256KB, that means the application is creating a DOS attack and shortening the logs timepan to 6 seconds(!) making it useless for all others.
You can avoid this behaviour by whitelisting your app for logcat:
adb logcat -P '<pid or uid of your app>'
Here task (object) is being saved to fireBase Database, but for setting ListID and ID there r few conditions:
if this is user's first task then set both to 20k
if User already have task Lists in the firebase then add listId of thet task List to present task's listID and add (ID of user's last task + 1) to Id of present task.
What i want the sequence to be ( see Log.e(..) statements in the code ) :
1-> 2-> 3-> 4-> 5-> 6-> 7
What is actually happening : 1-> 4-> 5-> 7-> 2-> 3
(Also, Never going to 6 ... For which i reffered following :
Can't reach some lines debugging android app
&
Android studio gradle breakpoint No executable code found at line ...... But all in vain)
( DBToFireb(..) is a method where changeListener is being used inside onComplete(..) of DatabaseReference.CompletionListener() )
public static boolean addTaskToFireBase(final Activity activity, final com.rb.eztask.model.TodoTask task, final ChangeListener changeListener)
{
mDatabase=FirebaseDatabase.getInstance().getReference("tasks");
final String taskId = mDatabase.push().getKey();
task.setTaskId(taskId);
final DatabaseReference myFieBaseDBChecker=FirebaseDatabase.getInstance().getReference("users").
child(Userkey).child("tasks");
myFieBaseDBChecker.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String s=dataSnapshot.getValue().toString();
if(s!=null)
{
if(s.contentEquals(","))
{
task.setId(20000);
task.setListId(20000);
task.setKey(UserKey);
DBToFireb(activity, taskId, task, changeListener);
}
else
{ String tasks[]=s.split(",");
final String lastTask = tasks[tasks.length-1];
DatabaseReference compareDb;
for(int i=0;i<tasks.length;i++)
{
if(tasks[i].length()>2)
{Log.e("im","1");
compareDb=FirebaseDatabase.getInstance().getReference("task").child(tasks[i]);
compareDb.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.e("im","2");
TodoTask taskToCompare=dataSnapshot.getValue(TodoTask.class);
Log.e("im","3");
if(taskToCompare.getBasketName().equals(task.getBasketName()))
{
task.setListId(taskToCompare.getListId());
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
Log.e("im","4");
compareDb=FirebaseDatabase.getInstance().getReference("tasks").child(lastTask);
Log.e("im","5");
compareDb.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.e("im","6");
TodoTask lastTaskInFireB = dataSnapshot.getValue(TodoTask.class);
task.setListId(lastTaskInFireB.getListId()+1);
task.setId(lastTaskInFireB.getId()+1);
task.setKey(UserKey);
DBToFireb(activity, taskId, task, changeListener);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Log.e("im","7");
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
return true;
}
onDataChange (and firebase callbacks in general) are invoked asynchonously
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 am using Firebase in my app, along with RxJava.
Firebase is capable of notify your app whenever something changed in the backend data (addition, removals, changes, ...).
I am trying to combine the feature of Firebase with RxJava.
The data I am listening for is called Leisure, and the Observable emits LeisureUpdate which contains a Leisure and the type of update (add, remove, moved, changed).
Here is my method which allows to subscribe to this events.
private Observable<LeisureUpdate> leisureUpdatesObservable;
private ChildEventListener leisureUpdatesListener;
private int leisureUpdatesSubscriptionsCount;
#NonNull
public Observable<LeisureUpdate> subscribeToLeisuresUpdates() {
if (leisureUpdatesObservable == null) {
leisureUpdatesObservable = Observable.create(new Observable.OnSubscribe<LeisureUpdate>() {
#Override
public void call(final Subscriber<? super LeisureUpdate> subscriber) {
leisureUpdatesListener = firebase.child(FirebaseStructure.LEISURES).addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
final Leisure leisure = convertMapToLeisure((Map<String, Object>) dataSnapshot.getValue());
subscriber.onNext(new LeisureUpdate(leisure, LeisureUpdate.ADDED));
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
final Leisure leisure = convertMapToLeisure((Map<String, Object>) dataSnapshot.getValue());
subscriber.onNext(new LeisureUpdate(leisure, LeisureUpdate.CHANGED));
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
final Leisure leisure = convertMapToLeisure((Map<String, Object>) dataSnapshot.getValue());
subscriber.onNext(new LeisureUpdate(leisure, LeisureUpdate.REMOVED));
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
final Leisure leisure = convertMapToLeisure((Map<String, Object>) dataSnapshot.getValue());
subscriber.onNext(new LeisureUpdate(leisure, LeisureUpdate.MOVED));
}
#Override
public void onCancelled(FirebaseError firebaseError) {
subscriber.onError(new Error(firebaseError.getMessage()));
}
});
}
});
}
leisureUpdatesSubscriptionsCount++;
return leisureUpdatesObservable;
}
First off, I would like to use Observable.fromCallable() method in order to create the Observable, but I guess it is impossible, since Firebase uses callbacks, right?
I keep a single instance of the Observable in order to always have one Observable where multiple Subscriber can subscribe.
The problem comes when everyone unsubscribe and I need to stop listening for the events in Firebase.
I didn't find anyway to make the Observable understand if there is any subscription still. So I keep counting how many calls I got to subscribeToLeisuresUpdates(), with leisureUpdatesSubscriptionsCount.
Then every time someone wants to unsubscribe it has to call
#Override
public void unsubscribeFromLeisuresUpdates() {
if (leisureUpdatesObservable == null) {
return;
}
leisureUpdatesSubscriptionsCount--;
if (leisureUpdatesSubscriptionsCount == 0) {
firebase.child(FirebaseStructure.LEISURES).removeEventListener(leisureUpdatesListener);
leisureUpdatesObservable = null;
}
}
This is the only way I found to make the Observable emits items when there is a subscriber, but I feel like there must be an easier way, specially understanding when there is no more subscribers listening to the observable.
Anyone who encountered a similar problem or have a different approach?
You can use Observable.fromEmitter, something along these lines
return Observable.fromEmitter(new Action1<Emitter<LeisureUpdate>>() {
#Override
public void call(final Emitter<LeisureUpdate> leisureUpdateEmitter) {
final ValueEventListener listener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// process update
LeisureUpdate leisureUpdate = ...
leisureUpdateEmitter.onNext(leisureUpdate);
}
#Override
public void onCancelled(DatabaseError databaseError) {
leisureUpdateEmitter.onError(new Throwable(databaseError.getMessage()));
mDatabaseReference.removeEventListener(this);
}
};
mDatabaseReference.addValueEventListener(listener);
leisureUpdateEmitter.setCancellation(new Cancellable() {
#Override
public void cancel() throws Exception {
mDatabaseReference.removeEventListener(listener);
}
});
}
}, Emitter.BackpressureMode.BUFFER);
Put this in your Observable.create() at the end.
subscriber.add(Subscriptions.create(new Action0() {
#Override public void call() {
ref.removeEventListener(leisureUpdatesListener);
}
}));
I suggest you to check as reference(or just use it) one of the next libraries:
RxJava : https://github.com/nmoskalenko/RxFirebase
RxJava 2.0: https://github.com/FrangSierra/Rx2Firebase
One of them works with RxJava and the other one with the new RC of RxJava 2.0. If you are interested of it, you can see the differences between both here.
Here is a sample code for using RxJava2 with Firebase's CompletionListener:
Completable.create(new CompletableOnSubscribe() {
#Override
public void subscribe(final CompletableEmitter e) throws Exception {
String orderKey = FirebaseDatabase.getInstance().getReference().child("orders").push().getKey();
FirebaseDatabase.getInstance().getReference().child("orders").child(orderKey).setValue(order,
new DatabaseReference.CompletionListener() {
#Override
public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {
if (e.isDisposed()) {
return;
}
if (databaseError == null) {
e.onComplete();
} else {
e.onError(new Throwable(databaseError.getMessage()));
}
}
});
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
Another amazing library which will help you to wrap all firebase realtime database logic under rx patterns.
https://github.com/Link184/Respiration
Here you can create your firebase repository and extend it from GeneralRepository for example:
#RespirationRepository(dataSnapshotType = Leisure.class)
public class LeisureRepository extends GeneralRepository<Leisure>{
protected LeisureRepository(Configuration<Leisure> repositoryConfig) {
super(repositoryConfig);
}
// observable will never emit any events if there are no more subscribers
public void killRepository() {
if (!behaviorSubject.hasObservers()) {
//protected fields
behaviorSubject.onComplete();
databaseReference.removeEventListener(valueListener);
}
}
}
You can "kill" your repository in that way:
// LeisureRepositoryBuilder is a generated class by annotation processor, will appear after a successful gradle build
LeisureRepositoryBuilder.getInstance().killRepository();
But I think for your situation will be better to extend com.link184.respiration.repository.ListRepository to avoid data mapping from java.util.Map to Leisure model through LeisureUpdate
So I'll try to keep this question as to-the-point as possible, but it will involve code snippets that traverse an entire codepath.
For context, I am fairly new and completely self-taught for Android dev, so please notify me of any clear misunderstandings/poor organization throughout. The main focus of the question is bug I am experiencing now, which is that, after a network request, the variable that was supposed to be set as a result of that network request is null, because the code moved forward before the network request completed.
Here is my activity method. It is supposed to populate the mFriends variable with the result of mUserPresenter.getUserList(), which is (unfortunately) null:
/**
* Grabs a list of friends, populates list with UserAdapter
*/
#Override
public void onResume(){
super.onResume();
mUserPresenter = new UserPresenter();
mFriends = mUserPresenter.getUserList();
if (mGridView.getAdapter() == null) {
UserAdapter adapter = new UserAdapter(getActivity(), mFriends);
mGridView.setAdapter(adapter);
}
else{
((UserAdapter)mGridView.getAdapter()).refill(mFriends);
}
}
Here is how I am structuring my UserPresenter method getUserList:
public List<User> getUserList()
{
ApiService.get_friends(this);
return mUserList;
}
The real magic happens in the ApiService class:
public static void get_friends(final UserPresenter userPresenter){
ApiEndpointInterface apiService = prepareService();
apiService.get_friends().
observeOn(AndroidSchedulers.mainThread())
.subscribe(
new Action1<List<User>>()
{
#Override
public void call(List<User> users) {
userPresenter.setList(users);
}
}
);
}
My thinking was, that by calling userPresenter.setList(users) in ApiService, that would set mUserList to the response from the api request. However, instead, mUserList == null at the time that getUserList responds.
Any ideas of how I can structure this?
I have also started to learn something similar. Here, I would rather use callbacks.
In your presenter,
public void setList(List<User> users) {
yourView.setUserList(users);
}
And your activity which implements a view (MVP)
#Override
public void setUserList(List<User> users) {
((UserAdapter)mGridView.getAdapter()).refill(mFriends);
}
Also, check that retrofit is not returning null list.
I have a made a small app when I was learning about all this. It fetches user data from GitHub and shows in a list. I was also working with ORMLite and Picasso so some db stuff is there. Dagger Dependency is also used (but you can ignore that). Here's the link.
Here's how my Presenter behaves:
private DataRetrieverImpl dataRetriever;
#Override
public void getUserList(String name) {
dataRetriever.getUserList(name);
}
#Override
public void onEvent(DataRetrieverEvent event) {
UserList userList = (UserList)event.getData();
mainView.setItems(userList);
}
DataRetrieverImpl works as a module (sort of).
private DataRetriever dataRetriever;
restAdapter = new RestAdapter.Builder().setEndpoint(SERVER_END_POINT).build();
dataRetriever = restAdapter.create(DataRetriever.class);
public void getUserList(final String name) {
Log.i(TAG, "getting user list for: " + name);
Observable<UserList> observable = dataRetriever.getUserList(name);
Log.i(TAG, "subscribe to get userlist");
observable.subscribe(new Action1<UserList>() {
#Override
public void call(UserList userList) {
eventBus.post(new DataRetrieverEvent("UserList", userList));
// save to database
for (User user : userList.getItems()) {
Log.i(TAG, user.getLogin());
try {
dbHelper.create(user);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
throwable.printStackTrace();
}
});
}
And DataRetriever is interface for retrofit. I'm sorry for the naming confusion.
public interface DataRetriever {
#GET("/search/users")
public Observable<UserList> getUserList(#Query("q") String name);
}
Any my Activity,
#Override
public void setItems(final UserList userList) {
runOnUiThread(new Runnable() {
#Override
public void run() {
UserAdapter userAdapter = (UserAdapter)recyclerView.getAdapter();
userAdapter.setUserList(userList);
userAdapter.notifyItemRangeInserted(0, userAdapter.getItemCount());
}
});
}