Closed DamianJaeger closed 1 year ago
Hello, thanks for reporting this, it seems there is some assertion that got triggered in the JNI code which is supposed to call into realm core. @cmelchior can you shed some light here?
It looks like a problem with ProGuard or DexGuard. JNI cannot call back into the JVM if the methods there are obfuscated.
We do ship proguard files that should automatically handle this for Android, but this doesn't work automatically for JVM and we have seen issues with DexGuard sometimes not applying it correctly.
@DamianJaeger I'll move this issue to realm-kotlin and we will continue the discussion there. But does any of the above apply to your project?
Methods should not be obfuscated, it is a debug build with neither ProGuard nor DexGuard enabled.
Is it a JVM or Android project?
Android
Hmm, that sounds weird. Can you post your build.gradle file? Or can you share the project showing this behavior?
It is a multi-module project and I can not share it in its entirety. Here are the project and app level build files:
Project level build.gradle.kts:
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath("com.android.tools.build:gradle:8.0.1")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.21")
classpath("com.google.dagger:hilt-android-gradle-plugin:2.45")
classpath("com.google.gms:google-services:4.3.15")
classpath("io.realm.kotlin:gradle-plugin:1.8.0")
}
}
plugins {
id("io.realm.kotlin") version "1.8.0" apply false
}
allprojects {
repositories {
google()
mavenCentral()
}
}
tasks.register("clean", Delete::class) {
delete(rootProject.buildDir)
}
App level:
plugins {
id("com.android.application")
id("kotlin-android")
kotlin("kapt")
id("dagger.hilt.android.plugin")
id("io.sentry.android.gradle") version "3.6.0"
id("com.mikepenz.aboutlibraries.plugin") version "10.6.2"
id("io.realm.kotlin")
id("com.google.gms.google-services")
}
android {
compileSdk = AppConfig.compileSdk
defaultConfig {
applicationId = "..."
minSdk = AppConfig.minSdkVersion
targetSdk = AppConfig.targetSdkVersion
versionCode = 1
versionName = "DEBUG"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
debug {
applicationIdSuffix = Constants.BUILD_TYPE_DEBUG_ID_SUFFIX
versionNameSuffix = Constants.BUILD_TYPE_DEBUG_VERSION_NAME_SUFFIX
}
create(Constants.BUILD_TYPE_BETA) {
initWith(getByName(Constants.BUILD_TYPE_RELEASE))
versionNameSuffix = Constants.BUILD_TYPE_BETA_VERSION_NAME_SUFFIX
}
create(Constants.BUILD_TYPE_INTERNAL) {
initWith(getByName(Constants.BUILD_TYPE_RELEASE))
versionNameSuffix = Constants.BUILD_TYPE_INTERNAL_VERSION_NAME_SUFFIX
}
}
flavorDimensions += "flavor"
productFlavors {
create("internalTesting") {
dimension = "flavor"
applicationIdSuffix = ".internal"
}
create("default") {
dimension = "flavor"
isDefault = true
}
}
buildFeatures {
compose = true
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
isCoreLibraryDesugaringEnabled = true
}
kotlinOptions {
jvmTarget = AppConfig.jvmTarget
}
composeOptions {
kotlinCompilerExtensionVersion = "1.4.7"
}
namespace = "..."
}
val internalImplementation by configurations
val betaImplementation by configurations
dependencies {
<dependencies to other modules>
// bottom sheet dialog
implementation("com.holix.android:bottomsheetdialog-compose:1.3.0")
// AboutLibraries
implementation("com.mikepenz:aboutlibraries-core:10.6.1")
implementation("com.mikepenz:aboutlibraries-compose:10.6.1")
// reorderable
implementation("org.burnoutcrew.composereorderable:reorderable:0.9.6")
// androidX
implementation("androidx.constraintlayout:constraintlayout-compose:1.0.1")
implementation("androidx.core:core-splashscreen:1.0.1")
implementation("androidx.datastore:datastore-preferences:1.0.0")
// coil
implementation("io.coil-kt:coil-compose:2.4.0")
// Google login
implementation("com.google.android.gms:play-services-auth:20.5.0")
// Accompanist
implementation("com.google.accompanist:accompanist-systemuicontroller:0.28.0")
implementation("com.google.accompanist:accompanist-webview:0.28.0")
// Material theme
implementation("com.google.android.material:material:1.9.0")
// Desugaring
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.3")
// Hilt
implementation("com.google.dagger:hilt-android:2.45")
kapt("com.google.dagger:hilt-compiler:2.45")
kapt("androidx.hilt:hilt-compiler:1.0.0")
// Firebase app distribution
betaImplementation("com.google.firebase:firebase-appdistribution:16.0.0-beta08")
debugImplementation("com.google.firebase:firebase-appdistribution-api-ktx:16.0.0-beta08")
internalImplementation("com.google.firebase:firebase-appdistribution-api-ktx:16.0.0-beta08")
releaseImplementation("com.google.firebase:firebase-appdistribution-api-ktx:16.0.0-beta08")
// test
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
androidTestImplementation("com.google.dagger:hilt-android-testing:2.45")
}
kapt {
correctErrorTypes = true
}
Note the following dependencies in a module the app module depends on
api("io.realm.kotlin:library-base:1.8.0")
api("io.realm.kotlin:library-sync:1.8.0")
This seems to be related to waitForInitialRemoteData
:
fun App.getUserRealmInstance(): Realm {
val user = currentUser ?: throw NotLoggedInException()
val config = SyncConfiguration
.Builder(
user = user,
partitionValue = userPartition(),
schema = SCHEMA,
)
.syncClientResetStrategy(clientResetStrategy)
.errorHandler(errorHandler)
.waitForInitialRemoteData() // Removing this line prevents the crash
.build()
return Realm.open(config)
}
I have been able to figure out the following:
There are the entities NotificationsEntity
and NotificationItemEntity
:
@PersistedName("DBNotificationsEntity")
class NotificationsEntity(
@PrimaryKey @PersistedName(FIELD_NAME_ID) var id: ObjectId = ObjectId(),
@PersistedName("_partition") var partition: String = "",
...
) : RealmObject {
constructor() : this(id = ObjectId())
val notifications: RealmResults<NotificationItemEntity> by backlinks(NotificationItemEntity::notification)
}
@PersistedName("DBNotificationItemEntity")
class NotificationItemEntity(
@PrimaryKey @PersistedName("_id") var id: ObjectId = ObjectId(),
@PersistedName("_partition") var partition: String = "",
@PersistedName("notification") var notification: NotificationsEntity? = null,
...
) : RealmObject {
constructor() : this(id = ObjectId())
}
If waitForInitialRemoteData
is used, the aforementioned crash happens. If not, opening the realm fails with
java.lang.IllegalStateException: [RLM_ERR_SCHEMA_VALIDATION_FAILED]: Schema validation failed due to the following errors:
- Property 'DBNotificationItemEntity.notification' of type 'object' has unknown object type 'NotificationsEntity'
- Property 'DBNotificationItemEntity.notification' declared as origin of linking objects property 'DBNotificationsEntity.notifications' links to type 'NotificationsEntity'
After renaming the class NotificationsEntity
to DBNotificationsEntity
(to match the persisted name), everything works as expected, even when using waitForInitialRemoteData
.
Thank you for that detailed report @DamianJaeger, we are looking into it.
Has this to be moved to Kotlin? @cmelchior ... Should I move the issue?
@nicola-cab It is already moved
Hi @DamianJaeger This might be a problem with incremental compilation. Can you reproduce this when doing a full build?
Yes, I can reproduce this with a clean build. I can reproduce it by renaming any entity class in the project in a similar relationship to not match the name given in the @PersistedName
annotation. This leads to a schema validation error when not using waitForInitialRemoteData
and the crash when using it.
Ah sorry, we identified the problem with errors for waitForInitialRemoteData
. It is a bug we will fix shortly.
But we also have found something that needs further investigation, but incremental compilation might not work correctly when using @PersistedName
on classes.
The reason is that when you have a Parent -> Child
relationship, we use our compiler plugin to embed some information about Child
in Parent
so when you just refactor Child
the metainformation (the class name), is not changed in Parent
as the Kotlin compiler do not think it needs to recompile it. We are still looking into it though.
Both the bugs you ran into have been fixed in 1.9.1 which has just been released to Maven Central. Sorry for the bad luck of running into two bugs at once.
SDK and version
SDK : Kotlin Version: 1.8.0
Observations
Crash log / stacktrace
Steps & Code to Reproduce