Executing delete with room (rxjava)
Asked Answered
C

5

9

In room the @Delete annotation doesn't emit anything. This is what the dao looks like

@Dao
public interface UserDao {
    @Delete
    void deleteUser(User user);
    //We can't use Maybe or Single or anything here

}

This makes it a problem while doing something like

userRepository.deleteUser().subscribeOn since we have no emission coming the dao. I use the following code to call deleteUser on a background thread.

Observable.just(appDatabase).
            subscribeOn(SchedulerProvider.getInstance().computation()).

            subscribe(db -> {
                userRepository.logoutUser(loggedUser.getLoggedInUser());
                loggedUser.setLoggedInUser(null);


            }, this::handleError);

This works fine. However, in the subscribe method I now need to access the Android UI to display a toast announcing a successful delete. Naturally, I get this exception (since the observeOn is missing from the chain)

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

However when I put the observeOn like this

Observable.just(appDatabase).
        subscribeOn(SchedulerProvider.getInstance().computation()).
        observeOn(SchedulerProvider.getInstance().ui()).
        subscribe(db -> {
            userRepository.logoutUser(loggedUser.getLoggedInUser());
            loggedUser.setLoggedInUser(null);

            Message message = new Message(R.string.user_logged_out_msg);
            message.setMessageType(Message.MessageType.SUCCESS_MESSAGE);
            view.showMessages(Arrays.asList(message)); //this leads to a taost

        }, this::handleError);

I strangely get this exception:

cannot access database on the main thread since it may potentially lock the UI for a long period of time.
Civility answered 21/12, 2017 at 8:55 Comment(2)
While I don't have a definitive answer for you I can say that Room by default makes its RXJava calls on a background thread so you don't need to explicitly call subscibeOn() but you do need to make sure that you make the call to observe on to push the activity to the UI thread. This is the area I'd be looking at if I was you.Myranda
"I use the following code to call deleteUser on a background thread" -- you are not calling deleteUser(). You may want to synchronize your examples so that they are consistent.Measure
P
6

Based on info from this question: Run Void Method in Background (StackOverflow)

Using a Completable and subscribing on another thread like this:

Completable.fromAction(this::clearCachedData)
   .subscribeOn(Schedulers.io())
   .subscribe();

worked for me. clearCachedData method executes the query in Room that I'm calling.

My query is:

/**
 * Delete all data in the items table.
 */
@Query("DELETE FROM items")
void deleteWeatherItems();
Paez answered 19/9, 2018 at 16:1 Comment(0)
J
2

I know this is late. But I ran into the same problem and were able to solve it with following

In Dao class

@Query("DELETE FROM users")
    fun deleteAllUser()

Call it like this. This way you can subscribe and it will run in the background.

Single.fromCallable {
        user.deleteAllUser() //User Dao fun
    }
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(..do stuff..)
Jadeite answered 23/5, 2018 at 1:53 Comment(2)
Single.fromCallable expects a value to be returned. And you can't return null because it crashes with io.reactivex.exceptions.OnErrorNotImplementedException: The callable returned a null valueScleroma
@Scleroma it returns Unit, not null.Yamauchi
R
0

Replace Observable just method to create.

  Observable.create(new ObservableOnSubscribe<Object>() {
            @Override
            public void subscribe(@io.reactivex.annotations.NonNull ObservableEmitter<Object> e) throws Exception {
                e.onNext(tileDatabase.getCategoryDeo().deleteCategory(category));

            }
        })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<Object>() {
                    @Override
                    public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d) {
                    }

                    @Override
                    public void onNext(@io.reactivex.annotations.NonNull Object o) {
                        Toast.makeText(MainActivity.this, "Record deleted ", Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onError(@io.reactivex.annotations.NonNull Throwable e) {
                    }

                    @Override
                    public void onComplete() {
                    }
                });
Reimport answered 21/12, 2017 at 12:37 Comment(1)
This looks plausible. I will give this a shot in a few moments.Civility
M
0

Try to pass a list of users to deletion function and return the count of the deleted users (the code is in Kotlin).

@Dao
public interface UserDao {
    ...

    @Delete
    fun deleteUsers(users: List<User>): Int
}

This would be the database class:

@Database(entities = [User::class], version = 1)
abstract class UsersDatabase : RoomDatabase() {

    abstract fun userDao(): UserDao

    companion object {
        private var INSTANCE: UsersDatabase? = null

        fun getInstance(context: Context): UsersDatabase? {
            if (INSTANCE == null) {
                INSTANCE = Room.databaseBuilder(context.applicationContext, UsersDatabase::class.java, "my_database.db").build()
            }
            return INSTANCE
        }

        ...
}

And these the repository and the viewModel (Room Android Architecture):

class UsersRepository internal constructor(val application: Application) {

   private val userDao: UserDao = UsersDatabase.getInstance(application)!!.userDao()

   ...

   fun deleteUsers(users: List<User>): Single<Int> {
        return Single.fromCallable { userDao.deleteUsers(users) }
    }
}


class UsersViewModel(val app: Application) : AndroidViewModel(app) {
    private val repo: UsersRepository = UsesRepository(application = app)
    ...

    fun deleteUsers(users: List<User>): Single<Int> {
        return repo.deleteUsers(users).subscribeOn(Schedulers.io())
    }
}

And then in some activity or fragment:

class UsersActivity : AppCompatActivity() {

    private lateinit var viewModel: UsersViewModel
    ...

    override fun onCreate(savedInstanceState: Bundle?) {
    ...
       viewModel = ViewModelProviders.of(this).get(UsersViewModel::class.java)
    }

    fun removeUsers(users: List<User>) {
       ...
       viewModel.deleteUsers(users).subscribe(this::toast)
    }
    ...

    fun toast() {
        Toast.make(this, "Users deleted", Toast.LENGTH_SHORT).show
    }
}
Magistrate answered 11/9, 2018 at 22:6 Comment(0)
S
-1
@Query("DELETE FROM User.TABLE_NAME")
public void nukeTable();

@Query("DELETE FROM " + User.TABLE_NAME + " WHERE " + User.COLUMN_ID + " = :id")
int deleteById(long id);

These method could be useful.

Shan answered 21/12, 2017 at 10:2 Comment(1)
how to handle it on with rx javaCroup

© 2022 - 2024 — McMap. All rights reserved.