偶尔收到 WarningContextClassLoader 而不是 PathClassLoader 导致 flyway 崩溃

Occasionally receiving WarningContextClassLoader instead of PathClassLoader causing flyway to crash

上下文

在我的 android 应用程序中,我使用 flyway 来更新我的 Db 模式。到目前为止,我只有一个迁移,它创建了 3 个表。 flyway 的简单和常规业务。它确实工作得很好......大多数时候!有时它会因这个错误而崩溃(下面是完整的堆栈):

java.lang.ClassCastException: android.app.LoadedApk$WarningContextClassLoader
cannot be cast to dalvik.system.PathClassLoader

我有几个与该错误相关的问题:

我还注意到我可以在 flyway 中显式注入 classloader (Flyway.setClassLoader() )

请注意,我在 Flyway 中注入了应用程序上下文,而不是特定的 activity 上下文。

版本

我使用 org.flywaydb:flyway-core:4.0.2

我在 Android 4.2.2(JellyBean,api 级别 17)和 4.4(KitKat,api 级别 19)中注意到了这个问题。据我所知,它从未出现在更高版本上。

堆栈跟踪

Exception java.lang.RuntimeException: Unable to start activity ComponentInfo{com.mycompany.myapp.free.debug/com.mycompany.MainActivity}: java.lang.ClassCastException: android.app.LoadedApk$WarningContextClassLoader cannot be cast to dalvik.system.PathClassLoader
    android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2306)
    android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:2358)
    android.app.ActivityThread.access0 (ActivityThread.java:156)
    android.app.ActivityThread$H.handleMessage (ActivityThread.java:1340)
    android.os.Handler.dispatchMessage (Handler.java:99)
    android.os.Looper.loop (Looper.java:153)
    android.app.ActivityThread.main (ActivityThread.java:5299)
    java.lang.reflect.Method.invokeNative (Method.java)
    java.lang.reflect.Method.invoke (Method.java:511)
    com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:833)
    com.android.internal.os.ZygoteInit.main (ZygoteInit.java:600)
    dalvik.system.NativeStart.main (NativeStart.java)

Caused by java.lang.ClassCastException: android.app.LoadedApk$WarningContextClassLoader cannot be cast to dalvik.system.PathClassLoader
    org.flywaydb.core.internal.util.scanner.classpath.android.AndroidScanner.<init> (AndroidScanner.java:46)
    org.flywaydb.core.internal.util.scanner.Scanner.<init> (Scanner.java:38)
    org.flywaydb.core.Flyway.execute (Flyway.java:1353)
    org.flywaydb.core.Flyway.info (Flyway.java:1040)
    com.mycompany.db.FlywayHelper.info (FlywayHelper.java:54)
    com.mycompany.db.FlywayHelper.migrate (FlywayHelper.java:45)
    com.mycompany.db.GW2DatabaseHelper.migrate (GW2DatabaseHelper.java:56)
    com.mycompany.db.DbInit.initKeyGuild (DbInit.java:62)
    com.mycompany.db.DbInit.init (DbInit.java:42)
    com.mycompany.business.BootApp.boot (BootApp.java:79)
    com.mycompany.MainActivity.onCreate (MainActivity.java:51)
    android.app.Activity.performCreate (Activity.java:5122)
    android.app.Instrumentation.callActivityOnCreate (Instrumentation.java:1081)
    android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2270)
    android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:2358)
    android.app.ActivityThread.access0 (ActivityThread.java:156)
    android.app.ActivityThread$H.handleMessage (ActivityThread.java:1340)
    android.os.Handler.dispatchMessage (Handler.java:99)
    android.os.Looper.loop (Looper.java:153)
    android.app.ActivityThread.main (ActivityThread.java:5299)
    java.lang.reflect.Method.invokeNative (Method.java)
    java.lang.reflect.Method.invoke (Method.java:511)
    com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:833)
    com.android.internal.os.ZygoteInit.main (ZygoteInit.java:600)
    dalvik.system.NativeStart.main (NativeStart.java)

研究更新

虽然我不清楚它的目的,LoadedApk.WarningContextClassLoader 似乎是一个有效的 class 加载器,然后 Flyway 应该支持它。 pull request 已提交。

在两者之间,需要实施变通方法,以注入预期的 class 加载器:

private void injectClassLoader(Flyway flyway) {
    ClassLoader classLoader = flyway.getClassLoader();
    if (classLoader != null && !(classLoader instanceof PathClassLoader)){
        flyway.setClassLoader(classLoader.getParent());
    }
}

commit message adding WarningContextClassLoader 有不错的信息。我想传递 activity 的上下文可能会有帮助。

What is LoadedApk.WarningContextClassLoader ?

Why is it sometimes used instead of dalvik.system.PathClassLoader ?

不太确定,但是这个特定的 class 加载器的目的似乎是警告用户它的包可能正在与其他包共享它的虚拟机。 More info here,感谢@orip。

Is it a bug in my app, in Android system or in Flyway ?

无论如何,WarningContextClassLoader 仍然是一个有效的 class 加载器,并且必须被 flyway 接受。在 AndroidScanner 中制作的 cast 毫无用处。

解决方案