utility class's static member (null) lifecycle - java

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;

Related

Localelist init: IllegalArgumentException: list[1] is a repetition

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.

InternalSyntheticLambda in Crash report

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.

Sporadic crashes when trying to get application object

My app is reporting an occasional NullPointerException on the following line:
MyApplication.myAppInstance.updateDay();
from which I understand that myAppInstance is null. The above line is being called in the onStart() method of one of the app's main activity right below super.onStart()
In the application class I have:
public static MyApplication myAppInstance;
public MyApplication() {
myAppInstance = this;
}
And that's the only place the variable gets written to in code.
The crash only happens on Android 9.
What would be causing this? The app kinda assumes that the application instance will always be available.
I should note that this crash only appears in the Play Console and not in Crashlytics.
java.lang.RuntimeException:
at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:3194)
at android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:3302)
at android.app.ActivityThread.-wrap12 (Unknown Source)
at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1891)
at android.os.Handler.dispatchMessage (Handler.java:108)
at android.os.Looper.loop (Looper.java:166)
at android.app.ActivityThread.main (ActivityThread.java:7425)
at java.lang.reflect.Method.invoke (Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run (Zygote.java:245)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:921)
Caused by: java.lang.NullPointerException:
at com.timSim.myScheduler.MainActivity.onStart (MainActivity.java:667)
at android.app.Instrumentation.callActivityOnStart (Instrumentation.java:1339)
at android.app.Activity.performStart (Activity.java:7392)
at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:3157)
This is happening to a very small number of users, so I can't replicate it.
To use Application class. You need to declare it in the manifests.xml.
Try this:
<application
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:name=".MyApplication"
.../>
And
class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
// TODO:
}
}

JavaSecurityException using ClipData on Android

I am trying to paste text using ClipData on Android. The following code gives a java.lang.SecurityException. This does not happen all of the time. Should I declare any uses-permission to read the clipboard?
java.lang.RuntimeException:
at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2750)
at android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:2811)
at android.app.ActivityThread.-wrap12 (ActivityThread.java)
at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1528)
at android.os.Handler.dispatchMessage (Handler.java:102)
at android.os.Looper.loop (Looper.java:154)
at android.app.ActivityThread.main (ActivityThread.java:6316)
at java.lang.reflect.Method.invoke (Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:872)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:762)
Caused by: java.lang.SecurityException:
at android.os.Parcel.readException (Parcel.java:1683)
at android.database.DatabaseUtils.readExceptionFromParcel (DatabaseUtils.java:183)
at android.database.DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel (DatabaseUtils.java:146)
at android.content.ContentProviderProxy.openTypedAssetFile (ContentProviderNative.java:692)
at android.content.ContentResolver.openTypedAssetFileDescriptor (ContentResolver.java:1163)
at android.content.ContentResolver.openTypedAssetFileDescriptor (ContentResolver.java:1107)
at android.content.ClipData$Item.coerceToText (ClipData.java:332)
at br.com.sicoob.camera.clipboard.ClipboardInterface.getText (ClipboardInterface.java:35)
at br.com.sicoobnet.InicioWap.verificarExisteBoletoCopiado (InicioWap.java:312)
at br.com.sicoobnet.InicioWap.verificarAcao (InicioWap.java:308)
at br.com.sicoobnet.InicioWap.onCreate (InicioWap.java:126)
at android.app.Activity.performCreate (Activity.java:6757)
at android.app.Instrumentation.callActivityOnCreate (Instrumentation.java:1119)
at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2703)
This question is fixed to my project, but to everyone who whants to know, this is how I fix.
First I figured out when this error happens. It was when I don't have any text in clipboard and my application tried to go to external storage and the error was thrown.
Then to fix that I just change my method to verify has a text inside clipboard.
It was like that:
ClipboardManager clipboard = getManager(context);
ClipData clip = clipboard.getPrimaryClip();
return clip != null && clip.getItemCount() > 0;
Then it was like that:
ClipboardManager clipboard = getManager(context);
clipboard.getPrimaryClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN);

Occasionally receiving WarningContextClassLoader instead of PathClassLoader causing flyway to crash

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.

Categories