I am new to RXJava2, so need some help, I have to implement the functionality of favourite, when user clicks on fav then it's inserted in the greendo database and when he unfavourite the same event then it's removed from the greendo database.
I am able to insert and fetch the result but not getting the idea how to remove it.
This line in the below code returning me void mDaoSession.getFavouriteDao().deleteByKey(favourite.getId());
It's saying incompatible type then I how to make observable compatible with void return type.
#Override
public Observable<Long> deleteFavouriteEvent(Favourite favourite) {
return Observable.fromCallable(new Callable<Long>() {
#Override
public Long call() throws Exception {
return mDaoSession.getFavouriteDao().deleteByKey(favourite.getId());
}
});
}
Insertion working fine :
#Override
public Observable<Long> insertFavouriteEvent(Favourite favourite) {
return Observable.fromCallable(new Callable<Long>() {
#Override
public Long call() throws Exception {
return mDaoSession.getFavouriteDao().insert(favourite);
}
});
}
You can use Completable.fromAction instead of Observable.
public Completable deleteFavouriteEvent(Favourite favourite) {
return Completable.fromAction(new Action() {
#Override
public void run() throws Exception {
mDaoSession.getFavouriteDao().deleteByKey(favourite.getId());;
}
});
}
#Delete / #Insert / #Update all of them takes Collection of Entity or the Entity itself.
You have to directly pass favourite object instead of favourite.getId()
And also scheduling a function with void as return type use Completable instead of Observable / Flowable.
Related
I have query that inserts a row
#Dao
public interface NoteDao {
#Insert
Long insert(Note note);
}
I'm using RxJava to perform the query on the background thread:
public void insert(Note note) {
Single.fromCallable(() -> noteDao.insert(note))
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<Long>() {
#Override
public void onSubscribe(#NonNull Disposable d) {
}
#Override
public void onSuccess(#NonNull Long aLong) {
Log.d(TAG, "onSuccess: New row Id: " + aLong);
}
#Override
public void onError(#NonNull Throwable e) {
}
});
}
Currently it successfully returns, the newly created primary key for the inserted DAO. How would I return the entire inserted row, WITHOUT performing a second query using the new row id?
In postgresql I would do something like this:
`INSERT INTO note_table(note_title, note_description) VALUES ($1, $2) RETURNING *`
Not sure how to do it with the Room library
As stated in the documents of Transcation, if you want to perform two queries in one shot you have to use transactions and there is no other option as far as I know for standard database operations
check below as here we are doing you should do similar
#Dao
public interface NoteDao {
#Insert
Long insert(Note note);
##Query(“SELECT * FROM Note WHERE noteId = :id)
Long getNote(id Long);
#Transaction
public void insertAndRetrieve(Note note):Note {
// Anything inside this method runs in a single transaction.
val id = insert(note);
return getNote(id);
}
}
I have a really tricky problem.
I have a method of void insert(User user) in my DAO. I wrapped it with :
public Completable insertUser(User user) {
return Completable.fromAction(() -> userDao.insert(user))
.subscribeOn(Schedulers.io())
which returns a Completable.
In my ViewModel, I just return the same thing:
public Completable insertUser(User user) {
return userDao.insertUser(user);
And in my UI, I observe on the completable :
vm.insertUser(vm.getSomeUsers())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CompletableObserver() {
#Override
public void onSubscribe(Disposable d) {
// Do nothing
}
#Override
public void onComplete() {
userAdapter.populate( ? ) // here I need the refreshed user
}
#Override
public void onError(Throwable e) {
}
});
The problem is that my insert does not return the updated user, I cannot change it to Observable<User> insertUser() because I'm using a lower version of Room.
So my question is, how can I populate the adapter with the updated user ? I have another method in my dao Flowable<User> getUsers(), but I have to use another observer to retrieve the users, so that leads to nested observers.
What is the best way to do it ?
You can do it as simple as
public Single<User> insertUser(User user) {
return Completable.fromAction(() -> userDao.insert(user))
.subscribeOn(Schedulers.io())
.andThen(Single.just(user));
It will return user you just inserted.
I have 2 streams, the first stream is a stream which takes data from database and call onCompleted() after finish taking data. The second stream is a stream that takes live data from server and never call onCompleted(). What I want to do is to create an operator that can do an action if the first stream(upstream) is an empty stream. Here is the sample:
getItemFromDatabase()
.lift(new DoIfEmptyOperator<Item>(new Action0() {
#Override
public void call() {
//Database is empty
System.out.println("Yeay successfully do an action");
}
}))
.concatWith(getItemFromServer()) // -----> intentionally never complete
.subscribe(new Subscriber<StoryItem>() {
#Override
public void onCompleted() {
//dosomething...
}
#Override
public void onError(Throwable e) {
//dosomething...
}
#Override
public void onNext(StoryItem storyItem) {
//dosomething
}
}));
Here is the code of DoIfEmptyOperator:
import rx.Observable;
import rx.Subscriber;
import rx.functions.Action0;
public class DoIfEmptyOperator<T> implements Observable.Operator<T,T>{
private Action0 action;
private boolean isEmpty = true;
public DoIfEmptyOperator(Action0 action) {
this.action = action;
}
#Override
public Subscriber<? super T> call(final Subscriber<? super T> childSubscriber) {
Subscriber<T> parentSubscriber = new Subscriber<T>() {
#Override
public void onCompleted() {
if(isEmpty) {
action.call();
}
childSubscriber.onCompleted();
}
#Override
public void onError(Throwable e) {
childSubscriber.onError(e);
}
#Override
public void onNext(T t) {
isEmpty = false;
childSubscriber.onNext(t);
}
};
childSubscriber.add(parentSubscriber);
return parentSubscriber;
}
}
However the action is never executed because the parentSubscriber onCompleted() is not firing, because the downstream never completed. If I remove
.concatWith(getItemFromServer())
then the action is executed. Any clue about how to solve the problem? I have dived to the source code of Observable.switchIfEmpty() but still have no clue about how it works.
I would advise against creating an operator.
This could be easily done with existing operators like this:
getItemFromDatabase()
.toList()
.flatMap(list -> {
if (list.isEmpty()) {
// side effect here
}
return getItemFromServer();
});
Have you thought about switchIfEmpty()? As an example of the usage of this operator - I have created some code on GitHub at the following link:
https://github.com/rs146/rxjava-simple/blob/master/src/test/java/SwitchIfEmpty.java
switchIfEmpty() is called when no items are emitted.
However, if you want to get items from the api or the db, then you can do something like the following:
Observable.concat(getFromDatabase(), getFromApi()).first();
As long as both getFromDatabase() and getFromApi() return the same Observable Type. This is a common Rx idiom in Android apps. It basically states that if an item's is not emitted from the database, then go fetch the result from the API instead.
In the code below, the loadMoreStrings() method is have it's call() method execute before the getLabelsFromServer() completes. I'm still learning RxJava but I just can't get this to run properly.
private void fetchLabels() {
listObservable = Observable.fromCallable(new Callable<List<String>>() {
#Override
public List<String> call() {
return apiService.getLabelsFromServer();
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
loadMoreStrings();
}
#Override
public void loadMoreStrings() {
stringListObservable.subscribe(new Action1<List<String>>() {
#Override
public void call(List<String> label) {
myStrings.addAll(label);
}
});
}
Because Observable type is lazy and it's not going to emit any items until you subscribe to it.
From code that you provided, you're not subscribing to listObservable. So it completes immediately and your loadMoreStrings() method is getting called.
I'm new into rxJava and it's making my head spin. Basically I'm pulling data from youtube api with retrofit which gives back Observable and with youtubeDataMapper I'm mappng it into Youtube Pojo object which contains String videoID. So my question is, how to make this method return that string instead of Completable?
This is my method:
#Override
public Completable downloadVideoUrl(String query) {
addSubscription(youtubeApi.getYoutubeId(query, Constants.youtubeApi)
.map(youtubeDataMapper::map)
.subscribeOn(subscribeScheduler)
.observeOn(observeScheduler)
.subscribe());
return Completable.complete();
}
You have two choices:
Make your downloadVideoUrl return Observable instead of Completable:
Preferred way:
#Override
public Completable downloadVideoUrl(String query) {
return youtubeApi.getYoutubeId(query, Constants.youtubeApi)
.map(youtubeDataMapper::map)
.subscribeOn(subscribeScheduler)
.observeOn(observeScheduler);
}
Notice lack of subscribe operator here.
Then wherever you want to get videoId:
downloadVideoUrl(query)
.subscribe(new Subscriber<String>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(String videoId) {
// do whatever you want with videoId
}
});
Use toBlocking().first()
This is not preffered as you block current Thread until Observable finishes
#Override
public String downloadVideoUrl(String query) {
return youtubeApi.getYoutubeId(query, Constants.youtubeApi)
.map(youtubeDataMapper::map)
.subscribeOn(subscribeScheduler)
.observeOn(observeScheduler)
.toBlocking().first();
}
First of all, it is better to make Retrofit return Single instead of Observable because you are expecting a single server response (and not a sequence of responses).
Secondly, Completable.complete() is a factory method for a Completable that does nothing at all. So you don’t need it here.
Regarding String videoID, it depends on what you are planning to do with it. Also, I have no idea what your .addSubscription() is doing.
I would suggest doing something like the following:
class YourClass {
private final CompositeSubscription compositeSubscription = new CompositeSubscription();
// you must call compositeSubscription.clear() either in class .finalize() or on some UI lifecycle event
void yourMethod() {
final Single videoID = youtubeApi.getYoutubeId(query, Constants.youtubeApi)
.map(youtubeDataMapper::map)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
final Subscription subscription = videoID.subscribe(new SingleSubscriber() {
#Override
public void onSuccess(String value) {
// TODO: do whatever with the value
}
#Override
public void onError(Throwable error) {
// TODO: log and/or display error
}
});
compositeSubscription.add(subscription);
}
}