0

I have an app which works almost perfect for around 500 users, BUT from time to time I am getting in Android Vitals report about Null Pointer Exception (always that same). For all 500 users, I got six times this error. I am not sure it is a lot, but it bothers me that I have a bug in my code. The problem is that it does not happen every use of my app and is very intermittent. Since a few days I am trying to reproduce it to fix it, but with no luck at all. Can I have your help, please?

Stacktrace:

java.lang.RuntimeException: 
  at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:3270)
  at android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:3409)
  at android.app.ActivityThread.handleRelaunchActivityInner (ActivityThread.java:5279)
  at android.app.ActivityThread.handleRelaunchActivity (ActivityThread.java:5187)
  at android.app.servertransaction.ActivityRelaunchItem.execute (ActivityRelaunchItem.java:69)
  at android.app.servertransaction.TransactionExecutor.executeCallbacks (TransactionExecutor.java:135)
  at android.app.servertransaction.TransactionExecutor.execute (TransactionExecutor.java:95)
  at android.app.ActivityThread$H.handleMessage (ActivityThread.java:2016)
  at android.os.Handler.dispatchMessage (Handler.java:107)
  at android.os.Looper.loop (Looper.java:214)
  at android.app.ActivityThread.main (ActivityThread.java:7356)
  at java.lang.reflect.Method.invoke (Native Method)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:492)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:930)

// My app part start //////////////////////////////////////////////////////////////

Caused by: java.lang.NullPointerException: 
  at com.vulterey.nixieclockwidget.ConfigActivity.refreshClock (ConfigActivity.java:446)
  at com.vulterey.nixieclockwidget.ConfigActivity.access$000 (ConfigActivity.java:63)
  at com.vulterey.nixieclockwidget.ConfigActivity$SettingsFragment.onCreatePreferences (ConfigActivity.java:138)

// My app part end //////////////////////////////////////////////////////////////

  at androidx.preference.PreferenceFragmentCompat.onCreate (PreferenceFragmentCompat.java:160)
  at androidx.fragment.app.Fragment.performCreate (Fragment.java:2684)
  at androidx.fragment.app.FragmentStateManager.create (FragmentStateManager.java:280)
  at androidx.fragment.app.FragmentManager.moveToState (FragmentManager.java:1175)
  at androidx.fragment.app.FragmentManager.moveToState (FragmentManager.java:1356)
  at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState (FragmentManager.java:1434)
  at androidx.fragment.app.FragmentManager.moveToState (FragmentManager.java:1497)
  at androidx.fragment.app.FragmentManager.dispatchStateChange (FragmentManager.java:2625)
  at androidx.fragment.app.FragmentManager.dispatchCreate (FragmentManager.java:2571)
  at androidx.fragment.app.FragmentController.dispatchCreate (FragmentController.java:236)
  at androidx.fragment.app.FragmentActivity.onCreate (FragmentActivity.java:315)
  at androidx.appcompat.app.AppCompatActivity.onCreate (AppCompatActivity.java:106)
  at com.vulterey.nixieclockwidget.ConfigActivity.onCreate (ConfigActivity.java:71)
  at android.app.Activity.performCreate (Activity.java:7825)
  at android.app.Activity.performCreate (Activity.java:7814)
  at android.app.Instrumentation.callActivityOnCreate (Instrumentation.java:1306)
  at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:3245)

A part of the code from ConfigActivity.java file:

Line

63  public class ConfigActivity extends AppCompatActivity {...

        public static class SettingsFragment extends PreferenceFragmentCompat implements SharedPreferences.OnSharedPreferenceChangeListener {

            // Runs all services to enable/disable option preferences depend on user choices
            @Override
            public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
                setPreferencesFromResource(R.xml.root_preferences, rootKey);

            ...

            //Runs refreshClock on Create Preferences to show settings base on Shared Preferences state
            ConfigActivity refreshActivity = (ConfigActivity) this.getActivity();
138         refreshActivity.refreshClock(context);

            ...}

            // Refreshes view of the clock in Config Activity to visualise changes in options made by user
            private void refreshClock(Context context){...

            // Gets all colors from Preference Manager
            int hoursColor = PreferenceManager.getDefaultSharedPreferences(context).getInt("hoursColor", ContextCompat.getColor(context, R.color.clockTextColor));

            ...

            //Sets color of the clock elements with colors from Preference Manager
            TextView clockHours = activity.findViewById(R.id.clockHours);
446         clockHours.setTextColor(hoursColor);

        ...}

    ...}

The TextView "clockHours" exist in the configuration_activity.xml file.

So, my question is how to reproduce it? Or what is causing it?

Any help as usual much appreciated.

Regards Adam

David Wasser
  • 93,459
  • 16
  • 209
  • 274
vulterey
  • 63
  • 6
  • This whole static nested Fragment thing is weird to me. It's sort of a hacky way to communicate from Fragment back to Activity. Do you really even need a PreferenceFragmentCompat here? Can you not just recreate these preference options directly in `ConfigActivity`? – Gavin Wright Aug 31 '20 at 23:12
  • `clockHours` is null, obviously. – user207421 Aug 31 '20 at 23:49
  • Can you share in which method this code resides? Its due to the clock hours textview being null. The cause might be a stale listener which is still listening even after the activity and fragment have been destroyed. You need to add the OnSharedPreferenceChangeListener in onResume of your fragment and remove this listener in onPause. – Muddassir Ahmed Sep 01 '20 at 00:47
  • @GavinWright I'm using Fragment to save the user choices to SharedPreferences as well as to visualise in the real-time user preferences. – vulterey Sep 01 '20 at 09:23
  • @MarquisofLorne Good call. I also managed to discover it today morning. Thanks. – vulterey Sep 01 '20 at 09:23
  • @MuddassirAhmed For test purposes I set clockHours as a null in the code, so I am getting NLP every time I am entering ConfigActivity screen. So by this, we have confirmation that this is clockHours. I do have OnSharedPreferencesListener in both locations: onResume: getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this) and onPause: getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this) The question is, what causes the clockHours to be null and why it so intermittent? – vulterey Sep 01 '20 at 09:42
  • @MuddassirAhmed refreshClock method resides directly in ConfigActivity class, but I am calling it from onCreatePreferences method sitting inside public static class SettingsFragment. – vulterey Sep 01 '20 at 09:51
  • If its inside the activity then why is it finding the view like so activity.findViewById(R.id.clockHours)? shouldn't it be able to do findViewById(R.id.clockHours) directly? – Muddassir Ahmed Sep 01 '20 at 13:03
  • @MuddassirAhmed I've just changed it. Now the question is will this help? Or how I can try to break it to make sure that NLP will not happen again? – vulterey Sep 01 '20 at 18:46
  • So in theory clockHours can never be null. Because the program should never get to this point if the ConfigActivity has been destroyed. – Muddassir Ahmed Sep 02 '20 at 00:46
  • Also findViewById would always be called with the reference of ConfigActivity so it should if setContentView is done properly is should always be able to get a reference to R.id.clockHours – Muddassir Ahmed Sep 02 '20 at 00:47
  • @MuddassirAhmed for help me gain some sort of certainty that the problem is solved. I am still puzzled a bit what in the first place sometimes makes clockHours null but sometimes not. Well, I am going to have to do what most developers do: try to fix something around the issue and check will it pop out after the next release :D Thank you very much for your help. – vulterey Sep 02 '20 at 21:59
  • What is the value of variable `activity` inline 445? Is it possible this is referencing an `Activity` where the content view has not been set? Or maybe the content view was set to a different layout XML than the one you expect (and therefore does not contain a `View` with ID `clockHours` – David Wasser Sep 04 '20 at 08:28
  • @DavidWasser In the ConfigActivity.java I have set-up configuration_activity.xml layout file which contains `clockHours` `View`, so as far as I don't mess around with some unknown to me way with the `context` or else (very possible ;)), I cannot see my mistake. It doesn't help that the issue is very intermittent. It would be much much easier if the error will pop in whenever the user enters the configuration activity screen. But it doesn't, and this is what makes me wonder what a weird bug I have in my code as it doesn't seem like standard Null Pointer Exception error. – vulterey Sep 05 '20 at 22:07
  • Where (in what method) do you set the value of the variable `activity`? – David Wasser Sep 05 '20 at 22:13
  • @DavidWasser So far I followed MuddassirAhmed advice and removed `activity` from all entries in `refreshClock` method. So now it looks like that: `TextView clockHours = findViewById(R.id.clockHours); clockHours.setTextColor(hoursColor);` I tested it released for public and so far I don't have any errors. However, considering that the error was very intermittent, I may be back with some new findings. It has gone around 24 hours since I released it. It may take around a week to see the first problems if they come back and around 2 weeks to get confident that the problem is solved. – vulterey Sep 08 '20 at 09:24
  • It seems that my issue came back. Despite removing `activity` from all entries in `refreshClock` method. I got recently another NPE this time one method down below which is even more puzzling as in the one method above I also used hoursColor variable. So, in theory, the first (top) method should cause NPE, not next one after it. I'm lost. If anyone feels any more clever than I, help me, please. – vulterey Sep 16 '20 at 17:09

0 Answers0