0

DISCLAIMER: This is not a "please fix my broken app" question. I'm not looking for a solution or a workaround. In fact, I've already provided one myself. This question is about why the API was designed this way.

roomDB = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "my-db")
        .addCallback(new RoomDatabase.Callback() {

            @Override
            public void onCreate(@NonNull SupportSQLiteDatabase db) {
                super.onCreate(db);
                roomDB.getDao().insert(someEntity);
            }
        })
        .build();

This code leads straight into an Exception:

java.lang.IllegalStateException: getDatabase called recursively

Why? Because the onCreate callback already provides a database instance in its parameter and the line roomDB.getDao().insert(someEntity); is somehow trying to get another. The consequence of onCreate providing a database instance is that the database cannot be accessed with the abstractions Room provides, but only directly. The documentation even warns about not to work with the SQLiteDatabase directly.

I have to say this again: The very first time, when I want to access my shiny new Room database, I'm forced to work with the low level SQLiteDatabase instead because of the design of the API.

My question is: Why? Is this a bug in the API design? Or are there legitimate use cases, where accessing the SQLiteDatabase directly is preferable?

If there was no SupportSQLiteDatabase instance in the parameters to onCreate, I would expect this code to work fine:

roomDB = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "my-db")
        .addCallback(new RoomDatabase.Callback() {

            @Override
            public void onCreate() {
                roomDB.getDao().insert(someEntity);
            }
        })
        .build();

EDIT: The database is ready, when the onCreate callback is invoked:

db = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "my-db")
        .addCallback(new RoomDatabase.Callback() {

            @Override
            public void onCreate(@NonNull SupportSQLiteDatabase db) {
                super.onCreate(db);
                Log.d("DB", "onCreate called");
            }
        })
        .build();

Log.d("DB", "database ready");

Because control flow already moved past the setup of the database, when the onCreate callback is invoked:

D/DB: database ready
...
D/DB: onCreate called
user1785730
  • 3,150
  • 4
  • 27
  • 50
  • Questions of the form "why did Developer X make Decision Y?" are usually not good for Stack Overflow. Frequently, only Developer X can answer, and that is statistically unlikely to occur. Anyone else can only offer guesses. – CommonsWare Aug 07 '20 at 15:52
  • I would agree if the question was about some obscure library nobody ever heard of. But I do not think this applys to Android. And I'm open to the possibility that I'm missing something and there are use cases where one might need the SQLiteDatabase instance provided. – user1785730 Aug 07 '20 at 15:55
  • "But I do not think this applys to Android" -- there are still only a handful of Google engineers who would have been involved in the decision. "I'm open to the possibility that I'm missing something" -- well, for one, you are assuming that the DAOs would work at this point in the database-creation process. That may or may not be correct. The developer may need to execute some PRAGMAs or otherwise prep the database before the DAOs will work. So, if I had to guess, they went this route for safety's sake. – CommonsWare Aug 07 '20 at 16:34
  • I was hoping that the design of the Android API is not the secret knowledge of a handful of Google engineers. As for the other concern, I would argue that the database is ready when `onCreate` is invoked. I've updated my question to show why. – user1785730 Aug 07 '20 at 17:44
  • "I've updated my question to show why" -- you have shown that the work is done [lazily](https://en.wikipedia.org/wiki/Lazy_evaluation). – CommonsWare Aug 07 '20 at 18:21

0 Answers0