Issue
I'm trying to run simple test to my project but this exception is holding me back and i cannot find out why.
Im doing a simple test to insert some new customers to my database. My first problem i encountered when inserting customers was that I could NOT insert data in the Main Thread so I used an AsyncTask.execute(new Runnable())
but this time i ran into this problem.
I've provided the following classes that should give a good insight I hope..
TestClass:
@RunWith(RobolectricTestRunner.class)
public class ExampleUnitTest {
ShopDatabase db;
private CustomerDao customerDao;
@Before
public void createDB() {
Context context = ApplicationProvider.getApplicationContext();
db = Room.inMemoryDatabaseBuilder(context, ShopDatabase.class).build();
customerDao = db.customerDao();
}
@Test
public void createUser(){
final Customer customer = new Customer("Test", "Test test", Date.valueOf("2020-10-10"));
AsyncTask.execute(new Runnable() {
@Override
public void run() {
customerDao.insert(customer);
}
});
Customer customerFound = customerDao.getCustomerByName("Test", "Test test");
assertEquals(customerFound.getFirstName(), customer.getFirstName(), "Could not find user..");
}
@After
public void closeDB() {
db.close();
}
Repository:
public class Repository {
private CustomerDao customerDao;
private LiveData<List<Customer>> allCustomers;
private Customer customer;
public Repository(Application application) {
// Get DB instance
ShopDatabase db = ShopDatabase.getInstance(application);
customerDao = db.customerDao();
allCustomers = customerDao.getAllCustomers();
}
public void insert(Customer customer) {
new InsertCustomerAsyncTask(customerDao).execute(customer);
}
public Customer getCustomerByName(String first, String last) {
return customerDao.getCustomerByName(first, last);
}
public LiveData<List<Customer>> getAllCustomers() {
return allCustomers;
}
// Inner Async class to insert customers
private static class InsertCustomerAsyncTask extends AsyncTask<Customer, Void, Void> {
private CustomerDao customerDao;
public InsertCustomerAsyncTask(CustomerDao customerDao) {
this.customerDao = customerDao;
}
@Override
protected Void doInBackground(Customer... customers) {
customerDao.insert(customers[0]);
return null;
}
}
Database:
@Database(entities = {Customer.class}, version = 1)
@TypeConverters({Converters.class})
public abstract class ShopDatabase extends RoomDatabase {
private static ShopDatabase instance;
public abstract CustomerDao customerDao();
public static synchronized ShopDatabase getInstance(Context context) {
if (instance == null) {
instance = Room.databaseBuilder(context.getApplicationContext(),
ShopDatabase.class,"shop_database")
.fallbackToDestructiveMigration()
.addCallback(roomCallback)
.build();
}
return instance;
}
private static RoomDatabase.Callback roomCallback = new RoomDatabase.Callback() {
@Override
public void onCreate(@NonNull SupportSQLiteDatabase db) {
super.onCreate(db);
new PopulateDbAsyncTask(instance).execute();
}
};
private static class PopulateDbAsyncTask extends AsyncTask<Void, Void, Void> {
private CustomerDao customerDao;
PopulateDbAsyncTask(ShopDatabase db) {
customerDao = db.customerDao();
}
@Override
protected Void doInBackground(Void... voids) {
// Customers
long c = customerDao.insert(new Customer("One", "A", Date.valueOf("2019-05-10")));
long c2 = customerDao.insert(new Customer("Two", "B", Date.valueOf("2020-07-10")));
long c3 = customerDao.insert(new Customer("Three", "C", Date.valueOf("1860-12-10")));
return null;
}
}
}
Firstly I thought it was because my app was not running so it meant my database was closed but that was not the case. I had my app open while testing.
Secondly - when I check if the database is open after i instantiate my database (db = Room.inMemoryDatabaseBuilder(context, ShopDatabase.class).build();
) - db.isOpen()
-> it returns false every time.
What can cause the problem?
Solution
when I check if the database is open after i instantiate my database (
db = Room.inMemoryDatabaseBuilder(context, ShopDatabase.class).build();
) - db.isOpen() -> it returns false every time.
The database is not opened until you attempt to access it. You can force accessing it (getting a Writable or Readable database/connection aka opening the database file itself which is all done behind the scenes on your behalf) before you return the instance. e.g.
......
instance.getOpenHelper().getWritableDatabase();
return instance;
Example
using :-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
shopDatabase = ShopDatabase.getInstance(this);
boolean dbopen = shopDatabase.isOpen();
if (dbopen); //<<<<<<<<<< breakpoint here
}
With :-
public static synchronized ShopDatabase getInstance(Context context) {
if (instance == null) {
instance = Room.databaseBuilder(context.getApplicationContext(),
ShopDatabase.class,"shop_database")
.fallbackToDestructiveMigration()
.addCallback(roomCallback)
.build();
}
//instance.getOpenHelper().getWritableDatabase();
return instance;
}
results in
changing to use :-
public static synchronized ShopDatabase getInstance(Context context) {
if (instance == null) {
instance = Room.databaseBuilder(context.getApplicationContext(),
ShopDatabase.class,"shop_database")
.fallbackToDestructiveMigration()
.addCallback(roomCallback)
.build();
}
instance.getOpenHelper().getWritableDatabase(); //<<<<<<<<< ADDED
return instance;
}
results in :-
I could NOT insert data in the Main Thread
You can if you add .allowMainThreadQueries()
when building
e.g.
public static synchronized ShopDatabase getInstance(Context context) {
if (instance == null) {
instance = Room.databaseBuilder(context.getApplicationContext(),
ShopDatabase.class,"shop_database")
.fallbackToDestructiveMigration()
.allowMainThreadQueries()
.addCallback(roomCallback)
.build();
}
instance.getOpenHelper().getWritableDatabase();
return instance;
}
- You may wish to not use this though.
Answered By - MikeT