GCX-HCI / tray

a SharedPreferences replacement for Android with multiprocess support
Apache License 2.0
2.29k stars 273 forks source link

NullPointerException at unregisterOnTrayPreferenceChangeListener in Service #141

Closed tomwassink closed 3 years ago

tomwassink commented 4 years ago

tray Version 0.12.0

tray is initialized in onStartCommand of a service

Device(s) (issues reported in Play store on Samsung Galaxy S8, A8 and S8+, but in pre launch test on multiple other devices as well )

minSdkVersion 21, compile and targetSDK 29

Stacktrace

java.lang.RuntimeException: at android.app.ActivityThread.handleStopService (ActivityThread.java:3948) at android.app.ActivityThread.access$1800 (ActivityThread.java:237) at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1828) at android.os.Handler.dispatchMessage (Handler.java:106) at android.os.Looper.loop (Looper.java:214) at android.app.ActivityThread.main (ActivityThread.java:7078) at java.lang.reflect.Method.invoke (Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:494) at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:964) Caused by: java.lang.NullPointerException: at com.android.internal.util.Preconditions.checkNotNull (Preconditions.java:128) at android.content.ContentResolver.unregisterContentObserver (ContentResolver.java:1993) at net.grandcentrix.tray.provider.ContentProviderStorage.unregisterOnTrayPreferenceChangeListener (ContentProviderStorage.java:362) at net.grandcentrix.tray.core.AbstractTrayPreference.unregisterOnTrayPreferenceChangeListener (AbstractTrayPreference.java:173) at com.twom.bico.services.JepsterService.destroyService (JepsterService.java:583) at com.twom.bico.services.JepsterService.onDestroy (JepsterService.java:345) at android.app.ActivityThread.handleStopService (ActivityThread.java:3928) ```

For some reason nullpointer exceptions are reported when the code wants to unregister the listener. In the code this is called just before stopping the Service. Code:

...

    if (mMultiProcessPreferences != null && mAppPrefsListener != null){
        mMultiProcessPreferences.unregisterOnTrayPreferenceChangeListener(mAppPrefsListener);
    }

    // Stop running service in foreground.
    this.stopForeground(true);

    // Cancel the persistent notification.
    mNotificationManager.cancel(NOTIFICATION_ID);

    //Stop service
    stopSelf();

}

Do you have any explanation or hint why this happens? I can't reproduce it in Android Studio, but i have got multiple reports in the Play Store and also in the automatic pre-launch tests.

Thanks a lot in advance!

jannisveerkamp commented 4 years ago

This exception is thrown when the Listener is not set or was already unregistered. Maybe your unregister is called twice for some reason? Can you give a little bit more Code?

tomwassink commented 4 years ago

Thanks for your fast reply!

Variable definition in Service:

private mAppPrefsListener = new OnTrayPreferenceChangeListener() {
            @Override
            public void onTrayPreferenceChanged(final Collection<TrayItem> items) {
                for (TrayItem item : items) {
                    if (PrefsUtilities.keyIsSpeedSensorGpsUse(item)){
                        mAlwaysUseGpsSpeedDistance = PrefsUtilities.getSpeedSensorGpsUse(getMultiProcessPreferences());
                    }
                    if (item.key().equals(Constants.SCREEN_REFRESH_RATE)){
                        mScreenRefreshRate = getScreenRefreshRatePreference();
                    }
                }
            }
        };

RegisterListener in Service onCreate:

getMultiProcessPreferences().registerOnTrayPreferenceChangeListener(mAppPrefsListener);

onDestroy in Service:

    @Override
    public void onDestroy(){
        if (!isDestroyed) {
            destroyService();
        }
        super.onDestroy();
    }

In class that extends MultiDexApplication class a onDestroy method which is called when Service (a foreground service) has to stop (i.e. quit app):

   public void onDestroy() {
        stopService(new Intent(this, JepsterService.class));
        isJepsterServiceStarted = false;
    }

I just changed to test the code to initialize the mAppPrefsListner via a method which is called in the onCreate method of the Service just before register the listner. Then in the onDestroy part I will set the mAppPrefsListner variable to null after unregister (and as you can see I already check if it is null before..). I will resubmit a beta update to see the results of the launch report.

tomwassink commented 4 years ago

After changing code as written above I had no issues in the launch report. Published an update today, so let's see if errors will be reported. Keep you posted.

tomwassink commented 3 years ago

Since update two weeks ago no errors reported. I will close the issue. Thanks for your help.