Context
In my android app, I use flyway to update my Db schema. Up to now I have one and only one migration, that creates 3 tables. Simple and usual business for flyway. It does work very well... most of the time! Sometimes it crashes with this error (full stack hereunder):
java.lang.ClassCastException: android.app.LoadedApk$WarningContextClassLoader
cannot be cast to dalvik.system.PathClassLoader
I have several questions related to that error :
What is LoadedApk.WarningContextClassLoader ?
Why is it sometimes used instead of dalvik.system.PathClassLoader ?
Is it a bug in my app, in Android system or in Flyway ?
I also noticed that I can inject explicitely the classloader inside flyway (Flyway.setClassLoader() )
How can I get for sure the right classloader ?
Note that I inject the Application Context in Flyway, not a particular activity's context.
Versions
I use org.flywaydb:flyway-core:4.0.2
I noticed this problem in Android 4.2.2 (JellyBean, api level 17) and 4.4 (KitKat, api level 19). AFAIK, it never occured on higher versions.
Stack trace
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.access$600 (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.access$600 (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)
Research update
Although its purpose is not clear to me, LoadedApk.WarningContextClassLoader appears to be a valid class loader, and should then be supported by Flyway. A pull request has been submitted.
In between, a workaround needs to be implemented, to inject the expected class loader:
private void injectClassLoader(Flyway flyway) {
ClassLoader classLoader = flyway.getClassLoader();
if (classLoader != null && !(classLoader instanceof PathClassLoader)){
flyway.setClassLoader(classLoader.getParent());
}
}
There's decent info the the commit message adding WarningContextClassLoader. I would guess passing the activity's context may help.
What is LoadedApk.WarningContextClassLoader ?
Why is it sometimes used instead of dalvik.system.PathClassLoader ?
Not really sure, but the purpose of this specific class loader seems to warn the user that its package could be sharing its virtual machine with other packages. More info here, thanks to #orip.
Is it a bug in my app, in Android system or in Flyway ?
Anyway the WarningContextClassLoader is still a valid class loader, and must be accepted by flyway. The cast made in AndroidScanner was just useless.
Solution
The bug is now fixed in flyway 4.1.
Related
I have recently noticed a lot of these crashes in my app.
I am not able to reproduce it myself.
Any idea what the issue might be?
I think it might happen when a bottom sheet dialog is about to be shown.
For reference my app supports 3 languages (English, Spanish and Bulgarian)
Exception java.lang.IllegalArgumentException: list[1] is a repetition
at android.os.LocaleList.<init> (LocaleList.java:193)
at androidx.appcompat.app.LocaleOverlayHelper.combineLocales (LocaleOverlayHelper.java:73)
at androidx.appcompat.app.LocaleOverlayHelper.combineLocalesIfOverlayExists (LocaleOverlayHelper.java:47)
at androidx.appcompat.app.AppCompatDelegateImpl.calculateApplicationLocales (AppCompatDelegateImpl.java:2470)
at androidx.appcompat.app.AppCompatDelegateImpl.applyApplicationSpecificConfig (AppCompatDelegateImpl.java:2426)
at androidx.appcompat.app.AppCompatDelegateImpl.onCreate (AppCompatDelegateImpl.java:514)
at androidx.appcompat.app.AppCompatDialog.<init> (AppCompatDialog.java:65)
at com.google.android.material.bottomsheet.BottomSheetDialog.<init> (BottomSheetDialog.java:95)
at com.google.android.material.bottomsheet.BottomSheetDialogFragment.onCreateDialog (BottomSheetDialogFragment.java:50)
at androidx.fragment.app.DialogFragment.prepareDialog (DialogFragment.java:648)
at androidx.fragment.app.DialogFragment.onGetLayoutInflater (DialogFragment.java:562)
at androidx.fragment.app.Fragment.performGetLayoutInflater (Fragment.java:1725)
at androidx.fragment.app.FragmentStateManager.createView (FragmentStateManager.java:492)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState (FragmentStateManager.java:261)
at androidx.fragment.app.FragmentManager.executeOpsTogether (FragmentManager.java:1862)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute (FragmentManager.java:1786)
at androidx.fragment.app.FragmentManager.execPendingActions (FragmentManager.java:1723)
at androidx.fragment.app.FragmentManager$4.run (FragmentManager.java:510)
at android.os.Handler.handleCallback (Handler.java:873)
at android.os.Handler.dispatchMessage (Handler.java:99)
at androidx.test.espresso.base.Interrogator.loopAndInterrogate (Interrogator.java:10)
at androidx.test.espresso.base.UiControllerImpl.loopUntil (UiControllerImpl.java:7)
at androidx.test.espresso.base.UiControllerImpl.loopUntil (UiControllerImpl.java:1)
at androidx.test.espresso.base.UiControllerImpl.injectMotionEvent (UiControllerImpl.java:5)
at androidx.test.espresso.action.MotionEvents.sendUp (MotionEvents.java:6)
at androidx.test.espresso.action.MotionEvents.sendUp (MotionEvents.java:1)
at androidx.test.espresso.action.Tap.sendSingleTap (Tap.java:5)
at androidx.test.espresso.action.Tap.-$$Nest$smsendSingleTap (Tap.java)
at androidx.test.espresso.action.Tap$1.sendTap (Tap.java:1)
at androidx.test.espresso.action.GeneralClickAction.perform (GeneralClickAction.java:4)
at androidx.test.espresso.ViewInteraction$SingleExecutionViewAction.perform (ViewInteraction.java:2)
at androidx.test.espresso.ViewInteraction.doPerform (ViewInteraction.java:21)
at androidx.test.espresso.ViewInteraction.-$$Nest$mdoPerform (ViewInteraction.java)
at androidx.test.espresso.ViewInteraction$1.call (ViewInteraction.java:6)
at androidx.test.espresso.ViewInteraction$1.call (ViewInteraction.java:1)
at java.util.concurrent.FutureTask.run (FutureTask.java:266)
at android.os.Handler.handleCallback (Handler.java:873)
at android.os.Handler.dispatchMessage (Handler.java:99)
at android.os.Looper.loop (Looper.java:193)
at android.app.ActivityThread.main (ActivityThread.java:6846)
at java.lang.reflect.Method.invoke (Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:883)
Fixed by updating to androidx.appcompat:appcompat:1.6.0-alpha04.
I was using androidx.appcompat:appcompat:1.6.0-alpha01, and according to this issue, versions below 1.6.0-alpha02 causes a crash when setting a locale from app that is part of the system languages.
I got following crash report:
java.lang.NullPointerException:
at com.myapp.actions.SellAction.getAction (SellAction.java:16)
at com.myapp.handler.SellHandler.sell (SellHandler.java:169)
at com.myapp.main.MyActivity.sell (MyActivity.java:537)
at com.myapp.main.Fragment2.lambda$onCreateView$0 (Fragment2.java:31)
at com.myapp.main.Fragment1$$InternalSyntheticLambda$1$c14fd7a0fe0c17e220e277448d1b71579f8038a9784d642f5a2e5b6a00f377eb$2.onClick$bridge (Fragment1.java)
at android.view.View.performClick (View.java:7862)
at android.widget.TextView.performClick (TextView.java:15004)
at android.view.View.performClickInternal (View.java:7831)
at android.view.View.access$3600 (View.java:879)
at android.view.View$PerformClick.run (View.java:29359)
at android.os.Handler.handleCallback (Handler.java:883)
at android.os.Handler.dispatchMessage (Handler.java:100)
at android.os.Looper.loop (Looper.java:237)
at android.app.ActivityThread.main (ActivityThread.java:8167)
at java.lang.reflect.Method.invoke (Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:496)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1100)
The code from Fragment2.lambda$onCreateView$0 (Fragment2.java:31) to SellAction.getAction is a valid stacktrace in my code. But usually (I don't see how this is possible) the Fragment1 (Fragment1$$InternalSyntheticLambda$1$c14fd7a0fe0c17e220e277448d1b71579f8038a9784d642f5a2e5b6a00f377eb$2.onClick$bridge (Fragment1.java)) has nothing to do with that code and I have no idea how it appears there. I don't know if that has something to do with the InternalSyntheticLambda. But I do not find anything about that on the internet (10 search results at google). Does anyone here has an idea what that is or how that can happen?
Looks like some handler or listener is leaking.
Try to look through all the call chain between
at com.myapp.actions.SellAction.getAction (SellAction.java:16)
and
at com.myapp.main.Fragment2.lambda$onCreateView$0 (Fragment2.java:31)
There might be view or some other object that is set up with listener which is not dropped correctly on view destroy. Special attention to Fragment2#onCreateView line 31.
I have a utility class that has constants and static methods.
Furthermore, that has one static field.
public class MyUtil implements IMyUtil {
public static String IS_DEBUG = false;
...
private static MyEnumType mMyEnum;
...
// static getter setter methods for mMyEnum
}
I have two fragments (FragmentA and FragmentB for the sake of simplicity).
FragmentA set the mMyEnum value, and then later use it with the getter method. When the user presses a button, I show FragmentB.
In the onActiviyCreated() method of FragmentB I get the mMyEnum value.
On my test devices everything is ok. But in the Google Play Console I see a bunch of NullPointerException errors at this line:
String testString = MyUtil.getMyEnum().getSomeStringValue();
I can't figure it out why can be myEnum null on that line and why I can't reproduce it. MyUtil obviously can't be null, getSomeStringValue() can be null, but it doesn't throw a NullPointerException, so the only myEnum can be null. But why? And if it can be null how can I reproduce it?
Thank you.
UPDATE #1:
Play shows a wide range of devices (Samsung A3, S6, S7, S8, LG X, G3, Huawei P9 etc.) and API versions (5.0 -> 7.1). Full stack trace is:
java.lang.RuntimeException:
at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2984)
at android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:3045)
at android.app.ActivityThread.-wrap14 (ActivityThread.java)
at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1642)
at android.os.Handler.dispatchMessage (Handler.java:102)
at android.os.Looper.loop (Looper.java:154)
at android.app.ActivityThread.main (ActivityThread.java:6776)
at java.lang.reflect.Method.invoke (Method.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:1496)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1386)
Caused by: java.lang.NullPointerException:
at com.mycompany.fragment.FragmentB.onActivityCreated (FragmentB.java:35)
at android.support.v4.app.Fragment.performActivityCreated (Fragment.java:2089)
at android.support.v4.app.FragmentManagerImpl.moveToState (FragmentManagerImpl.java:1133)
at android.support.v4.app.FragmentManagerImpl.moveToState (FragmentManagerImpl.java:1290)
at android.support.v4.app.FragmentManagerImpl.moveToState (FragmentManagerImpl.java:1272)
at android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated (FragmentManagerImpl.java:2149)
at android.support.v4.app.FragmentController.dispatchActivityCreated (FragmentController.java:201)
at android.support.v4.app.FragmentActivity.onStart (FragmentActivity.java:600)
at android.support.v7.app.AppCompatActivity.onStart (AppCompatActivity.java:178)
at android.app.Instrumentation.callActivityOnStart (Instrumentation.java:1256)
at android.app.Activity.performStart (Activity.java:6972)
at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2937)
at android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:3045)
at android.app.ActivityThread.-wrap14 (ActivityThread.java)
at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1642)
at android.os.Handler.dispatchMessage (Handler.java:102)
at android.os.Looper.loop (Looper.java:154)
at android.app.ActivityThread.main (ActivityThread.java:6776)
at java.lang.reflect.Method.invoke (Method.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:1496)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1386)
Long-lived Static members present a problem because Android may have freed memory and removed this value at some time (when no fragment is active with a reference to it). You also haven't explicitly said whether you can get to FragmentB without setting a value from FragmentA first, so this could also be your problem.
Additional note - a constant should be constant - so should have the final keyword.
My suggested solution would be to get rid of this bad architecture where two components are relying on an outside god-object to maintain their state. Pass your enum value as an Extra in the intent.
You can write to the bundle with
public static final String KEY_ENUM = "MyEnumKey";
bundle.putString(KEY_ENUM, myEnum.name()):
and then read it back with
Bundle bundle = intent.getExtras();
MyEnumTypemyEnum = MyEnumType.valueOf(bundle.getString(FragmentA.KEY_ENUM));
Another suggestion is to ensure that myEnum is never null (which by default it is). Declare it like:
private static MyEnumType mMyEnum = MyEnumType.MY_DEFAULT_VALUE;
The Google Developer Console shows that my app has received two of the same errors in the past month, but this RuntimeException doesn't specify a class or file for which the error stems from. There's nothing specific that I can see. Below are the error for two different devices:
Samsung Galaxy S8 Active (cruiserlteatt), 4096MB RAM, Android 7.0
Report 1 of 2
java.lang.RuntimeException:
at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2984)
at android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:3045)
at android.app.ActivityThread.handleRelaunchActivity
(ActivityThread.java:4978)
at android.app.ActivityThread.-wrap21 (ActivityThread.java)
at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1648)
at android.os.Handler.dispatchMessage (Handler.java:102)
at android.os.Looper.loop (Looper.java:154)
at android.app.ActivityThread.main (ActivityThread.java:6781)
at java.lang.reflect.Method.invoke (Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run
(ZygoteInit.java:1520)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1410)
Samsung Galaxy S7 (heroqltespr), 4096MB RAM, Android 7.0
Report 2 of 2
java.lang.RuntimeException:
at android.app.ActivityThread.performLaunchActivity
(ActivityThread.java:2947)
at android.app.ActivityThread.handleLaunchActivity
(ActivityThread.java:3008)
at android.app.ActivityThread.handleRelaunchActivity
(ActivityThread.java:4974)
at android.app.ActivityThread.-wrap21 (ActivityThread.java)
at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1656)
at android.os.Handler.dispatchMessage (Handler.java:102)
at android.os.Looper.loop (Looper.java:154)
at android.app.ActivityThread.main (ActivityThread.java:6688)
at java.lang.reflect.Method.invoke (Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run
(ZygoteInit.java:1468)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1358)
What could it be causing this error? These are the only two times it happened, and the log provided doesn't show a java class or xml file like other errors I am able to resolve.
Would greatly appreciate it if someone could help me resolve this,
many thanks.
According to the source code, here are two places in ActivityThread.performLaunchActivity where RuntimeException is thrown. They are:
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
and
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to start activity " + component
+ ": " + e.toString(), e);
}
}
As you can see:
They both provide a message for the RuntimeException that includes the exception message from the original exception.
They both pass the original exception (e) to the RuntimeException constructor so that it is properly chained.
Without the causal exception message (at least) and the causal exception stacktrace, OR your complete app source code, it is going to be next to impossible to diagnose your problem.
You need to figure out how to get the Google Developer Console to give you the missing info, or get logcat to log them. We can't help you without that missing info.
Apart from that, I can advise you that the best way to try to diagnose problems like this is to look at the Android source code. (It is not very helpful here ... but it would be if you had the missing diagnostic information.)
I've recently published my app on Google Play. I'm checking out the crashes from Firebase and one of the clusters with the highest number of exceptions is a fatal exception with the following stack trace:
Caused by android.renderscript.RSIllegalArgumentException: Unsuported element type.
android.renderscript.ScriptIntrinsicBlur.create (ScriptIntrinsicBlur.java:50)
com.kayimapps.autocolorsplash_android.Util.getBlurredBitmap (Util.java:234)
com.kayimapps.autocolorsplash_android.activities.IntroActivity.onCreate (IntroActivity.java:83)
android.app.Activity.performCreate (Activity.java:5283)
android.app.Instrumentation.callActivityOnCreate (Instrumentation.java:1097)
android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2209)
android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:2299)
android.app.ActivityThread.access$700 (ActivityThread.java:150)
android.app.ActivityThread$H.handleMessage (ActivityThread.java:1280)
android.os.Handler.dispatchMessage (Handler.java:99)
android.os.Looper.loop (Looper.java:137)
android.app.ActivityThread.main (ActivityThread.java:5283)
java.lang.reflect.Method.invokeNative (Method.java)
java.lang.reflect.Method.invoke (Method.java:511)
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:1102)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:869)
dalvik.system.NativeStart.main (NativeStart.java)
The getBlurredBitmap method that can be seen in the stack trace is also as follows:
public static Bitmap getBlurredBitmap(Bitmap input, Context context)
{
Bitmap result = null;
try {
RenderScript rsScript = RenderScript.create(context);
Allocation alloc = Allocation.createFromBitmap(rsScript, input);
ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(rsScript, alloc.getElement());
blur.setRadius(25);
blur.setInput(alloc);
result = Bitmap.createBitmap(input.getWidth(), input.getHeight(), input.getConfig());
Allocation outAlloc = Allocation.createFromBitmap(rsScript, result);
blur.forEach(outAlloc);
outAlloc.copyTo(result);
rsScript.destroy();
}
catch (RSIllegalArgumentException e)
{
FirebaseCrash.report(e);
}
return result;
}
I've looked everywhere for this exception to see its cause but so far I've found nothing. The interesting thing is that this exception is raised only in devices with Android 17 (4.2), and the only device I'm testing the app with is a Samsung Galaxy S3 with Android 17, which raises no exceptions on that exact same place.
I'm guessing that since RenderScript runs on GPU, maybe some devices simply don't support it because of their hardware. The devices that raise this exception are all sorts of devices with all sorts of brands though, so I can't really blame it on one manufacturer.
Does anyone have any idea on why this would happen, or maybe some pointers on where to look for the solution?
Edit: Turns out I am an idiot and the OS version of the phone I'm using is 18, not 17. The problems still persists on devices with Android 17 though.