Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android API 23 crashes with NoClassDefFoundError #989

Closed
05nelsonm opened this issue Oct 13, 2023 · 12 comments
Closed

Android API 23 crashes with NoClassDefFoundError #989

05nelsonm opened this issue Oct 13, 2023 · 12 comments

Comments

@05nelsonm
Copy link

05nelsonm commented Oct 13, 2023

Describe the bug
Android API 23 crashes because slf4j is not loaded when static call LoggerFactory.getLogger is made from NativeDB

This only affects API 23, as service loaders do not work properly; API 24+ works fine.

To Reproduce
Run on emulator for Android API 23

Expected behavior
Should not crash

Logs

10-13 08:33:48.415  5957  5972 W System.err: SLF4J: No SLF4J providers were found.
10-13 08:33:48.415  5957  5972 W System.err: SLF4J: Defaulting to no-operation (NOP) logger implementation
10-13 08:33:48.415  5957  5972 W System.err: SLF4J: See https://www.slf4j.org/codes.html#noProviders for further details.
10-13 08:33:48.437  5957  5972 I art     : Rejecting re-init on previously-failed class java.lang.Class<org.sqlite.core.NativeDB$$ExternalSyntheticLambda0>
10-13 08:33:48.437  5957  5972 I art     : Rejecting re-init on previously-failed class java.lang.Class<org.sqlite.core.NativeDB$$ExternalSyntheticLambda0>
10-13 08:33:48.438  5957  5972 I art     : Rejecting re-init on previously-failed class java.lang.Class<org.sqlite.core.NativeDB$$ExternalSyntheticLambda0>
10-13 08:33:48.440  5957  5972 I art     : Rejecting re-init on previously-failed class java.lang.Class<org.sqlite.core.NativeDB$$ExternalSyntheticLambda1>
10-13 08:33:48.440  5957  5972 I art     : Rejecting re-init on previously-failed class java.lang.Class<org.sqlite.core.NativeDB$$ExternalSyntheticLambda1>
10-13 08:33:48.440  5957  5972 I art     : Rejecting re-init on previously-failed class java.lang.Class<org.sqlite.core.NativeDB$$ExternalSyntheticLambda1>
10-13 08:33:48.445  5957  5972 I art     : Rejecting re-init on previously-failed class java.lang.Class<org.sqlite.core.NativeDB$$ExternalSyntheticLambda1>
10-13 08:33:48.446  5957  5970 E TestRunner: failed: givenEphemeralDriver_whenMultipleCloses_thenDoesNotThrowException(io.toxicity.sqlite.mc.driver.test.EphemeralAndroidTest)
10-13 08:33:48.447  5957  5970 E TestRunner: ----- begin exception -----
10-13 08:33:48.447  5957  5970 E TestRunner: java.lang.IllegalStateException: Failed to open ephemeral JDBC connection with test.db
10-13 08:33:48.447  5957  5970 E TestRunner: 	at io.toxicity.sqlite.mc.driver.PlatformDriver$Companion.create$driver_debug(PlatformDriver.kt:165)
10-13 08:33:48.447  5957  5970 E TestRunner: 	at io.toxicity.sqlite.mc.driver.SQLiteMCDriver$Factory$create$$inlined$withDispatcher$1.invokeSuspend(SQLiteMCDriver.kt:262)
10-13 08:33:48.447  5957  5970 E TestRunner: 	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
10-13 08:33:48.447  5957  5970 E TestRunner: 	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108)
10-13 08:33:48.447  5957  5970 E TestRunner: 	at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:115)
10-13 08:33:48.447  5957  5970 E TestRunner: 	at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:103)
10-13 08:33:48.447  5957  5970 E TestRunner: 	at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584)
10-13 08:33:48.447  5957  5970 E TestRunner: 	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:793)
10-13 08:33:48.447  5957  5970 E TestRunner: 	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:697)
10-13 08:33:48.447  5957  5970 E TestRunner: 	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:684)
10-13 08:33:48.447  5957  5970 E TestRunner: Caused by: java.lang.NoClassDefFoundError: org.sqlite.core.NativeDB$$ExternalSyntheticLambda1
10-13 08:33:48.447  5957  5970 E TestRunner: 	at org.sqlite.core.NativeDB.prepare(NativeDB.java:130)
10-13 08:33:48.447  5957  5970 E TestRunner: 	at org.sqlite.core.DB.prepare(DB.java:264)
10-13 08:33:48.447  5957  5970 E TestRunner: 	at org.sqlite.jdbc3.JDBC3Statement.lambda$execute$0$org-sqlite-jdbc3-JDBC3Statement(JDBC3Statement.java:53)
10-13 08:33:48.447  5957  5970 E TestRunner: 	at org.sqlite.jdbc3.JDBC3Statement$$ExternalSyntheticLambda1.call(D8$$SyntheticClass)
10-13 08:33:48.447  5957  5970 E TestRunner: 	at org.sqlite.jdbc3.JDBC3Statement.withConnectionTimeout(JDBC3Statement.java:459)
10-13 08:33:48.447  5957  5970 E TestRunner: 	at org.sqlite.jdbc3.JDBC3Statement.execute(JDBC3Statement.java:42)

Environment (please complete the following information):

  • OS: Android API 23
  • CPU architecture: x86_64
  • sqlite-jdbc version: 3.43.2.0

Additional context

@gotson
Copy link
Collaborator

gotson commented Oct 13, 2023

Api 23 is android 6, from 2015. Not sure we want to do the extra mile for something that old.

@05nelsonm
Copy link
Author

Api 23 is android 6, from 2015. Not sure we want to do the extra mile for something that old.

Yeah I understand that for sure. Some things to consider though:

  • There are people who are not as fortunate and are rocking pretty old Android versions due to device manufacturers stopping OS updates after about 2 years from their device release dates. Android's pretty popular globally!
  • This minSdk bump from 23 to 24 is attributed to logging which is only utilized in 2 places for trace level logging 🤷‍♂️

@05nelsonm
Copy link
Author

05nelsonm commented Oct 13, 2023

Testing it now, but seems like removing logging from NativeDB, and declared imports for org.slf4j.... fixes the issue.

@05nelsonm
Copy link
Author

Yup, works

Git Patch
CI Run

@gotson
Copy link
Collaborator

gotson commented Oct 14, 2023

From your log it seems SLF4J loads fine: 10-13 08:33:48.415 5957 5972 W System.err: SLF4J: No SLF4J providers were found.

This is a standard error message when no providers are found.

It seems to break afterwards, but i don't know how Android logging works with SLF4J.

@05nelsonm
Copy link
Author

05nelsonm commented Oct 14, 2023

From your log it seems SLF4J loads fine: 10-13 08:33:48.415 5957 5972 W System.err: SLF4J: No SLF4J providers were found.

This is a standard error message when no providers are found.

Right right. Everywhere else, logging works fine. I tested adding a debug log message upon JDBC registration and there were no issues when running on API 23.

It seems to break afterwards, but i don't know how Android logging works with SLF4J.

I have a weird feeling that it has something to do with how art (Android Run Time) handles the loading of JNI interfaces, thus the re-init failures; it does not like having org.slf4j references. There were a lot of changes between API 23 and 24.

I tried inlining the LoggerFactory.getLogger(NativeDB.class) to the callsite and removing the static logger ref, still was a no-go. Also tried moving logger reference to DB and accessing it from NativeDB, same results (no-go).

@05nelsonm
Copy link
Author

05nelsonm commented Oct 14, 2023

So, I wrapped the logging in a try/catch block and everything worked.

try {
    logger.atTrace()
            .setMessage("DriverManager [{}] [SQLite EXEC] {}")
            .addArgument(() -> Thread.currentThread().getName())
            .addArgument(sql)
            .log();
} catch (NoClassDefFoundError e) {}

Still had the art: re-init failure log messages though.

Going to investigate further, but it seems like the builder class returned via .atTrace() is not available???

@05nelsonm
Copy link
Author

Tried crashing things on API 23 by adding the following to JDBC

    static {
        try {
            DriverManager.registerDriver(new JDBC());
            logger.atTrace().setMessage("REGISTERED").log();
        } catch (SQLException e) {
            logger.atError().setCause(e).log();
        }
    }

So the builder returned by .atTrace is in fact there... Definitely something with how art is loading the JNI interface which references org.slf4j.

@05nelsonm
Copy link
Author

Changed the following in NativeDB

    /** @see org.sqlite.core.DB#_exec(java.lang.String) */
    @Override
    public synchronized int _exec(String sql) throws SQLException {
        logger.trace("DriverManager");
//        try {
//            logger.atTrace()
//                    .setMessage("DriverManager [{}] [SQLite EXEC] {}")
//                    .addArgument(() -> Thread.currentThread().getName())
//                    .addArgument(sql)
//                    .log();
//        } catch (NoClassDefFoundError e) {}
        return _exec_utf8(stringToUtf8ByteArray(sql));
    }

image

Seems almost as if the version of slf4j that is being used in the JNI interface is the one that ships with Android API 23, instead of 2.0.9 being shipped with the sqlite-jdbc jar...

@gotson
Copy link
Collaborator

gotson commented Oct 16, 2023

I don't think this has anything to do with JNI. JNI is about loading native libs. SLF4J is used in the Java code.

I don't know Android that well, but i'm pretty sure there's a lot of Java libraries that use SLF4J and can be used on Android. You would need to understand exactly how SLF4J works on Android, whether it's provided by the ART, or by other libraries. It may also be that the SLF4J implementation used on Android is not compatible with SLF4J 2.

@gotson
Copy link
Collaborator

gotson commented Oct 18, 2023

@05nelsonm 3.43.2.1-SNAPSHOT has been published, it uses SLF4J 1.7, please give it a try and report whether it is working better.

@05nelsonm
Copy link
Author

@05nelsonm 3.43.2.1-SNAPSHOT has been published, it uses SLF4J 1.7, please give it a try and report whether it is working better.

Just ran latest release of 3.43.2.1. Everything works now for API 23!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants