Search code examples
androidsqliteandroid-roomandroidx

Room 2.5.2 runtime crash - NoSuchFieldError: No instance field mDelegate of type SQLiteStatement


I'm trying to update the Room database library on an Android app I'm developing from version 2.4.3 to 2.5.2, these are the Room dependencies in my gradle.build file

dependencies {
    val room_version = "2.5.2"
    implementation("androidx.room:room-runtime:$room_version")
    implementation("androidx.room:room-ktx:$room_version")
    kapt("androidx.room:room-compiler:$room_version")

}

The app compiles and builds successfully, but unfortunately at runtime, when a query is used, Room crashes with NoSuchFieldError error.

FATAL EXCEPTION: Thread-29
Process: my.app, PID: 29657
java.lang.NoSuchFieldError: No instance field mDelegate of type Landroid/database/sqlite/SQLiteStatement; in class Landroidx/sqlite/db/framework/FrameworkSQLiteStatement; or its superclasses (declaration of 'androidx.sqlite.db.framework.FrameworkSQLiteStatement' appears in /data/app/~~8vd8qCDSVAv==/my.app/base.apk)
at androidx.sqlite.db.framework.FrameworkSQLiteStatement.executeInsert(Unknown Source:0)
at androidx.room.EntityInsertionAdapter.insertAndReturnId(EntityInsertionAdapter.kt:102)

After taking a quick look at the source code, it appears that my code at the stacktrace line

at androidx.sqlite.db.framework.FrameworkSQLiteStatement.executeInsert(Unknown Source:0)

points to this class

package androidx.sqlite.db.framework

import android.database.sqlite.SQLiteStatement
import androidx.sqlite.db.SupportSQLiteStatement

/**
 * Delegates all calls to a [SQLiteStatement].
 *
 * @constructor Creates a wrapper around a framework [SQLiteStatement].
 *
 * @param delegate The SQLiteStatement to delegate calls to.
 */
internal class FrameworkSQLiteStatement(
    private val delegate: SQLiteStatement
) : FrameworkSQLiteProgram(
    delegate
), SupportSQLiteStatement {
    override fun execute() {
        delegate.execute()
    }

    override fun executeUpdateDelete(): Int {
        return delegate.executeUpdateDelete()
    }

    override fun executeInsert(): Long {
        return delegate.executeInsert()
    }

    override fun simpleQueryForLong(): Long {
        return delegate.simpleQueryForLong()
    }

    override fun simpleQueryForString(): String? {
        return delegate.simpleQueryForString()
    }
}

Which can be found here https://androidx.tech/artifacts/sqlite/sqlite-framework/2.3.0-source/androidx/sqlite/db/framework/FrameworkSQLiteStatement.kt.html

That's the source code for the androidx.sqlite:sqlite-framework dependency version 2.3.0.

And based on the stacktrace logs, it seem that Room is instead pointing to an older version based on the "mDelegate" field.

https://androidx.tech/artifacts/sqlite/sqlite-framework/2.0.1-source/androidx/sqlite/db/framework/FrameworkSQLiteStatement.java.html

That's androidx.sqlite:sqlite-framework version 2.0.1, which has a field defined as "mDelegate".

It looks like (correct me if I'm wrong) that the Room library at runtime is looking for classes from an old version of sqlite, or that the Room library in its entirety along with its transitive dependencies on sqlite are all running older versions at runtime, despite what I actually have at compile time.

Tried looking everywhere for what might be causing this. Nothing came up.

Things I've observed:

  • I'm currently on Room version 2.4.3, and the issue started appearing exactly on version 2.5.0-beta01. I slowly incremented version numbers from the android website until I found out exactly where it started appearing. I tested every version between 2.5.0-beta01 and 2.5.2 and they're all causing this runtime crash.

  • The issue exists in different phones from different OEMs, it isn't related to a specific device.

  • I ran the gradle dependencies tree task and didn't find any transitive room/sqlite dependencies being used by any libraries!

Things I tried:

  • I tried updating all the other Android dependencies, at least most of them (My project has several dozens of dependencies). I tried updating all the Androidx and Kotlin dependencies and tried commenting out most of the third party dependencies.

  • I tried switching from kapt() to ksp() and later annotationProcessor() for the room compiler dependency. Didn't matter.

  • I tried forcing Room and sqlite versions through strictly {} and resolutionStrategy{} blocks in my gradle.build file.

      configurations.all {
      resolutionStrategy {
          force("androidx.room:room-runtime:2.5.2")
          force("androidx.room:room-ktx:2.5.2")
          force("androidx.room:room-compiler:2.5.2")
      force("androidx.sqlite:sqlite-framework:2.3.1")
      }
      }
    
    
      dependencies {
      implementation("androidx.room:room-runtime") {
          version {
              strictly("2.5.2")
              }
          }
      // etc for the rest of the room and sqlite dependencies 
      }
    

Due to this issue, I'm now forced to be stuck on an old version of Room, potentially indefinitely, and libraries like Workmanager 2.8.1 that implicitly/transitively depends on Room versions older than 2.5.0 cannot be updated, since I'm required to update Room with it as well.


Solution

  • It looks like that the cause of this crash is old version of sentry library. I updated sentry library in build.gradle of app to last one and all works.

    plugins {
    ...
    id "io.sentry.android.gradle" version "3.12.0"
    

    }