Mr. Techie
Mr. Techie

Reputation: 886

Problem in adding Room for data storage in android using java

I was trying to implement Room for persistent data storage in java. But I ran into problems with it.

In first try:

MyDao.java

@Dao
public interface MyDao {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void addUser(UserDetails userDetails);

}

MyDatabase.java

@Database(entities = {User.class}, version = 1)
public abstract class MyDatabase extends RoomDatabase {

    public abstract MyDao myDao();
}

with the above the app crashes with error:
java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
whenever I try to insert a data into the database.

In the second try:

MyDao.java

@Dao
public interface MyDao {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    ListenableFuture<Integer> addUser(UserDetails userDetails);

}

MyDatabase.java

@Database(entities = {User.class}, version = 1)
public abstract class MyDatabase extends RoomDatabase {

    public abstract MyDao myDao();
}

changed the return type to hold ListenableFuture<Integer> because official doc says to do so. The app does not build successfully and shows the following error:
error: Not sure how to handle insert method's return type.

So, my question is how can I correctly and successfully insert data in the database using room with java.
Please help.

Upvotes: 0

Views: 221

Answers (3)

Mr. Techie
Mr. Techie

Reputation: 886

After wandering on the internet for ages, I came to know that it is possible to write insert query using following 2 methods:

  1. Using a thread class

Operations.java

public class Operations{

    private MyDao myDao;

    public Operations(Context context){
        MyDatabase db = Room.databaseBuilder(context, MyDatabase.class, "database")
                .fallbackToDestructiveMigration()
                .build();
        myDao = db.myDao();
    }

    public void insertUser(UserDetails userDetails){
        new Thread{
            @override
            public void run(){
                myDao.addUser(userDetails);
            }
        }.start();
    }

}

MyDao.java

@Dao
public interface MyDao {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void addUser(UserDetails userDetails);

}

MyDatabase.java

@Database(entities = {User.class}, version = 1)
public abstract class MyDatabase extends RoomDatabase {

    public abstract MyDao myDao();
}

OR

  1. Using ExecutorService class

Operations.java

public class Operations{

    private MyDao myDao;

    public Operations(Context context){
        MyDatabase db = Room.databaseBuilder(context, MyDatabase.class, "database")
                .fallbackToDestructiveMigration()
                .build();
        myDao = db.myDao();
    }

    public void insertUser(UserDetails userDetails){
        MyDatabase.databaseWriteExecutor.execute(()->{
               notesDb.notesDao().addNote(note));
           }
    }

}

MyDao.java

@Dao
public interface MyDao {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void addUser(UserDetails userDetails);

}

MyDatabase.java

@Database(entities = {User.class}, version = 1)
public abstract class MyDatabase extends RoomDatabase {
    
    public static final ExecutorService databaseWriteExecutor =
            Executors.newFixedThreadPool(2);

    public abstract MyDao myDao();
}

Upvotes: 0

Sergey Melnik
Sergey Melnik

Reputation: 131

  1. For working with Room in UI thread you need initialize database as below

     database = Room.databaseBuilder(YOUR_CONTEXT, AppDatabase.class, "database")
         .allowMainThreadQueries() //this string need for working in UI thread
         .build();
    

But it not recommended to do in production code

  1. I never used Guava, but here is a guide for working with ListenableFuture.

And as Dominik Wuttke said you may do asynhronous work with coroutines or RxJava instead of ListenableFuture.

Upvotes: 0

Dominik Wuttke
Dominik Wuttke

Reputation: 555

You basically created the database with your first run of the app and were trying to access the database on the main thread which throws an error.

When you changed your database and tried to run the app, you changed the database scheme without providing a migration to the new version.

The solution for the changed database is, open the device manager and delete your database, this forces the system to create a new instance of the database with your updated database scheme. Or you can provide a migration which is necessary when you are updating the database scheme for an app already in production

The solution for the error, that you are trying to access the database from the mainthread is, that database operations need to be handled asynchronous and are not allowed to block the main thread.

You need to create a thread where you access the database, with kotlin you can simply create a coroutine and run the operation as a coroutine. With Java you should make use of RXJava

Upvotes: 0

Related Questions