Why annotationProcessor "androidx.room:room-compiler:$room_version" is ever required?

42 views Asked by At

Previous, I am building my app using

def room_version = '2.5.0'
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"

Today, when I remove

annotationProcessor "androidx.room:room-compiler:$room_version"

The app is still fully build-able. My app does use @Dao, @Entity, ...

May I know, why is it so? I thought the reason we are using annotationProcessor, because we want to support @Dao, @Entity, ...?

1

There are 1 answers

0
MikeT On BEST ANSWER

The Room compiler annotation processor generates java code from the respective annotations (descried below). The generated code is only then invoked at run time. Hence the lack of the code does not result in a compile time issue (but a run time issue/exception).

Primarily it generates code for each @Database annotated class and for each @Dao annotated interface or abstract class.

  • The generated code is placed in the java (generated) folder (visible from the Android view of the project explorer).

    • for each @Database abstract class there will be a class that is the same name as the @Database annotated class but suffixed with _Impl.

      • It is the entities parameter of the @Database annotation that defines the @Entity annotated classes that are then deemed to be the tables that Room will use.
    • for each @Dao annotated abstract class or interface (typically the latter) there will be a a class that is the same name as the @Dao annotated class/interface but suffixed with _Impl

Without the annotation processer for the compiler (aka removing the line), then there will be no complaint to compile time as effectively you are saying that you are not using Room.

However, if you then try to run the App (if it attempts to use Room), you will then get a run time exception stating that the @Database class suffixed with _Impl does not exist, even though there is no compile time error.

Demonstration

A project consists of an abstract class TheDatabase annotated with @Database as per:-

@Database(entities = {ArtistOriginal.class,ArtistOther1.class,ArtistOther2.class, ArtistOther3.class},version = 1,exportSchema = false)
  • i.e 4 @Entity annotated classes.

Within the TheDatabase is also the line:-

abstract AllDAOs getAllDAOs();
  • i.e. a single (all encompassing @Dao annotated interface/abstract class (the latter in this demo)) it including

:-

@Dao
abstract class AllDAOs {
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    abstract long insert(ArtistOriginal artistOriginal);
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    abstract long insert(ArtistOther1 artistOther1);
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    abstract long insert(ArtistOther2 artistOther2);
}
  • pretty simple, just the ability to insert into each of the 4 tables.

If the build gradle (module) excludes the room compiler as per:-

dependencies {

    implementation 'androidx.appcompat:appcompat:1.6.1'
    implementation 'com.google.android.material:material:1.11.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
    implementation 'androidx.room:room-runtime:2.6.1'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
    /* annotationProcessor 'androidx.room:room-compiler:2.6.1' */
}

Activity Code for MainActivity is

:-

public class MainActivity extends AppCompatActivity {

    TheDatabase db;
    AllDAOs dao;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        db = TheDatabase.getInstance(this);
        dao = db.getAllDAOs();

        SupportSQLiteDatabase sdb = db.getOpenHelper().getWritableDatabase();
        /* Ignored as no result will be returned */
        Cursor cursor = sdb.query("INSERT INTO ArtistOriginal (name) VALUES ('Candido_via_query');");
        DatabaseUtils.dumpCursor(cursor);
        cursor.close();
        logDataInDatabase(sdb,"QRYINS");
        /* artist_id will be generated (1??? for the first run) */
        sdb.execSQL("INSERT INTO ArtistOriginal (name) VALUES('Candido_via_execsql');");
        logDataInDatabase(sdb,"EXEINS");
        /* In this case the artist_id is 0 as int cannot be null */
        ArtistOriginal ao = new ArtistOriginal();
        ao.Name = "Candidi_via_@Insert";
        long aiid = dao.insert(ao);
        logDataInDatabase(sdb,"DAOINS");
        /* In this case the artist_id is 0 as int cannot be null */
        ArtistOther1 ao1 = new ArtistOther1();
        ao1.Name = "Candido_via_@Insert";
        long ao1id = dao.insert(ao1);
        /* In this case the artist_id member is null as it is an Integer Object */
        ArtistOther2 ao2 = new ArtistOther2();
        ao2.Name = "Candido_via_@Insert";
        long ao2id = dao.insert(ao2);


        Cursor csr = db.query("" +
                "SELECT 'ArtistOriginal',* FROM ArtistOriginal " +
                "UNION ALL SELECT 'ArtistOther1',* FROM ArtistOther1 " +
                "UNION ALL SELECT 'ArtistOther2',* FROM ArtistOther2",
                null
        );
        DatabaseUtils.dumpCursor(csr);
        csr.close();
    }

    void logDataInDatabase(SupportSQLiteDatabase sdb,String tag_suffix) {
        Log.d("CSRDUMP_" + tag_suffix,"Dumping Cursor");
        Cursor csr = db.query("" +
                        "SELECT 'ArtistOriginal' AS t,* FROM ArtistOriginal " +
                        "UNION ALL SELECT 'ArtistOther1',* FROM ArtistOther1 " +
                        "UNION ALL SELECT 'ArtistOther2',* FROM ArtistOther2",
                null);
        DatabaseUtils.dumpCursor(csr);
        csr.close();
    }
}
  • code used is from another answer (and hence the apparent complexity/untypical use)

Then there is no issue compiling the project:-

BUILD SUCCESSFUL in 3s
31 actionable tasks: 31 executed

The Android View shows:-

enter image description here

  • i.e. there is no java code generated for Room (just for the build)

However if an attempt is made to run the App then:-

2024-03-18 09:54:04.872 30483-30483/? E/AndroidRuntime: FATAL EXCEPTION: main
    Process: a.a.so77759888javaroomsimpleextratable, PID: 30483
    java.lang.RuntimeException: Unable to start activity ComponentInfo{a.a.so77759888javaroomsimpleextratable/a.a.so77759888javaroomsimpleextratable.MainActivity}: java.lang.RuntimeException: Cannot find implementation for a.a.so77759888javaroomsimpleextratable.TheDatabase. TheDatabase_Impl does not exist
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2913)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6669)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
     Caused by: java.lang.RuntimeException: Cannot find implementation for a.a.so77759888javaroomsimpleextratable.TheDatabase. TheDatabase_Impl does not exist
        at androidx.room.Room.getGeneratedImplementation(Room.kt:58)
        at androidx.room.RoomDatabase$Builder.build(RoomDatabase.kt:1351)
        at a.a.so77759888javaroomsimpleextratable.TheDatabase.getInstance(TheDatabase.java:19)
        at a.a.so77759888javaroomsimpleextratable.MainActivity.onCreate(MainActivity.java:21)
  • i.e. The expected TheDatabase_Impl does not exist.

Now with the Room compiler annotation processor

:-

dependencies {

    implementation 'androidx.appcompat:appcompat:1.6.1'
    implementation 'com.google.android.material:material:1.11.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
    implementation 'androidx.room:room-runtime:2.6.1'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
    annotationProcessor 'androidx.room:room-compiler:2.6.1'
}

The build works as per :-

BUILD SUCCESSFUL in 3s
31 actionable tasks: 11 executed, 20 up-to-date

Android View :-

enter image description here

  • i.e. the expected java code has been generated

If an attempt is made to run the App then:-

The app runs without an exception. The Log includes some expected output:-

2024-03-18 10:01:34.082 I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@8c94956
2024-03-18 10:01:34.082 I/System.out: <<<<<
2024-03-18 10:01:34.082 D/CSRDUMP_QRYINS: Dumping Cursor
2024-03-18 10:01:34.084 I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@8f4e6c4
2024-03-18 10:01:34.086 I/System.out: 0 {
2024-03-18 10:01:34.086 I/System.out:    t=ArtistOriginal
2024-03-18 10:01:34.086 I/System.out:    Artist_id=1
2024-03-18 10:01:34.086 I/System.out:    Name=Candido_via_query
2024-03-18 10:01:34.086 I/System.out: }
2024-03-18 10:01:34.086 I/System.out: <<<<<
2024-03-18 10:01:34.087 D/CSRDUMP_EXEINS: Dumping Cursor
2024-03-18 10:01:34.087 I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@e2e29ad
2024-03-18 10:01:34.088 I/System.out: 0 {
2024-03-18 10:01:34.088 I/System.out:    t=ArtistOriginal
2024-03-18 10:01:34.088 I/System.out:    Artist_id=1
2024-03-18 10:01:34.088 I/System.out:    Name=Candido_via_query
....

App Inspection also confirms that the database exists and contains data e.g. :-

enter image description here

If the Room compiler annotation processor is removed, then again the code will compile but as the code is not generated (the old code being removed):-

enter image description here

  • note unsure if options could be set to retain the previously generated code