3

In my app, I have a fragment that I'm using to display data, and a Settings activity that I use to change some settings in the app. When I go to the settings activity via the navigation drawer, and then return to the main screen (without changing settings), the app crashes with this exception:

java.lang.IllegalStateException: Fragment NowWeatherFragment{b7914f8 (0e67ee0d-8776-45b5-9fd7-ee69841c31d1)} not attached to a context

It seems to be happening after a response from an API call, and there was a decent explanation of why from another answer on SO, but nothing I've tried has been able to fix that.

I have seen several other questions about this, but I'm having trouble applying the answers to them to my situation. I tried checking for isAttached() and that didn't help, as well doing a null check for the fragment, but it's still getting the same exception

Rather than post all of the classes, some of which are very long, here is the github repo. It should be fairly lightweight and easy to run on a device through Android Studio https://github.com/jollygreenegiant/SimpleWeather

I expect that the app will just go back to the main screen and display the weather data like it does on launch.

Here is the full stack trace:

java.lang.IllegalStateException: Fragment NowWeatherFragment{b7914f8 (0e67ee0d-8776-45b5-9fd7-ee69841c31d1)} not attached to a context.
        at androidx.fragment.app.Fragment.requireContext(Fragment.java:765)
        at androidx.fragment.app.Fragment.getResources(Fragment.java:829)
        at com.jggdevelopment.simpleweather.fragments.NowWeatherFragment.setupViews(NowWeatherFragment.java:82)
        at com.jggdevelopment.simpleweather.fragments.NowWeatherFragment$3.onSharedPreferenceChanged(NowWeatherFragment.java:152)
        at android.app.SharedPreferencesImpl$EditorImpl.notifyListeners(SharedPreferencesImpl.java:612)
        at android.app.SharedPreferencesImpl$EditorImpl.commit(SharedPreferencesImpl.java:598)
        at com.jggdevelopment.simpleweather.fragments.MasterFragment.updateConditions(MasterFragment.java:263)
        at com.jggdevelopment.simpleweather.services.WeatherAPIUtils$2.onResponse(WeatherAPIUtils.java:92)
        at retrofit2.DefaultCallAdapterFactory$ExecutorCallbackCall$1$1.run(DefaultCallAdapterFactory.java:83)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7319)
        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:934)
Cameron
  • 1,281
  • 1
  • 19
  • 40

2 Answers2

5

You need to call unregisterOnSharedPreferenceChangeListener() in your onDestroyView() (the mirror of onCreateView(), which is where you register the listener).

As it is now, you're leaking your Fragment as SharedPreferences holds a strong reference to your listener, continuing to send it callbacks to its onSharedPreferenceChanged well after your Fragment's view is destroyed, causing the error message you're getting.

ianhanniballake
  • 191,609
  • 30
  • 470
  • 443
  • That worked, thank you. Can you elaborate a little on what is meant by a "strong" reference? How does that differ from other kinds of references? – Cameron Jul 09 '19 at 15:38
  • @cpgreen2 - a [weak reference](https://en.m.wikipedia.org/wiki/Weak_reference) only exists while something else references that object, ensuring that the object can be garbage collected. I used strong reference as shorthand for every other type of reference - one that keeps that object around even when it would otherwise be destroyed. – ianhanniballake Jul 09 '19 at 15:45
0

Fragment lifecycle is too complex and complicated

Activity myactivity = getActivity(); 
if (isAdded() && myactivity != null) {
...
}
Chanaka Weerasinghe
  • 5,404
  • 2
  • 26
  • 39
  • 1
    This doesn't actually fix the memory leak that causes them to receive callbacks after the Fragment's view is destroyed in the first place. – ianhanniballake Jul 09 '19 at 05:32