0

I have a very simple Activity:

public class ActivityLogin extends FragmentActivity {

    /**
     *
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        this.setContentView(R.layout.activity_login);
        this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);

        FragmentManager fragmentManager = this.getSupportFragmentManager();
        FragmentLogin fragment = (FragmentLogin) fragmentManager.findFragmentByTag(EnumFragmentTags.LOGIN.getValue());

        if (fragment == null) {
            Log.v("Activity", "Fragment is null");

            fragment = new FragmentLogin();
            fragmentManager.beginTransaction().add(R.id.login_layout, fragment, EnumFragmentTags.LOGIN.getValue()).commit();
        } else {
            Log.v("Activity", "Fragment is not null");
        }
    }
}

As you can see i only create my Fragment if it is null. The Fragment does not retain instance: So setRetainInstance(false). When i do some work in the background and call this.getResources() in my Fragment i always get:

Fragment not attached to Activity

after i rotated my device. If i dont rotate everything is fine. If i rotate i get the above error. If i use this.getActivity.getResources() instead i get:

java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.Resources android.support.v4.app.FragmentActivity.getResources()'

So for some reason the activity does not get attached to the Fragment.

Why?

Do i have to use setRetainInstance(true) on my Fragment to get this working? But i thought doing this would lead into Memory issues...

Mulgard
  • 9,877
  • 34
  • 129
  • 232

1 Answers1

2

When i do some work in the background and call this.getResources()

This is your problem. If you are doing work in background, and the Activity rotates (it is destroyed and recreated), the fragment is no longer attached to the Activity so you get that error.

A simple solution is to check if your fragment is still attached before trying to access the Activity or resources and stop the work at that point if detached, but a better solution is to offload the background work to a dedicated fragment with setRetainInstance(true) or a service.

Basically, you need to be aware of the Activity lifecycle, if you rotate the device then the Activity and the Fragment will be destroyed and recreated, but you still have the initial Fragment instance because it has a background task that keeps an implicit reference to the Fragment, but that Fragment is no longer attached.

Francesc
  • 25,014
  • 10
  • 66
  • 84
  • My `AsyncTasks` are all inside `DialogFragment` with `setRetainInstance(true)`. So i already did what you mentioned. The Task is running perfectly until it's end but i have a `callback` to my `Fragment` when the work is done. And at this moment after the orientation change i get the described behaviour. I need to somehow reattach the activity. But if that is not possible i will just use `setRetainInstance(true)` on all my fragments. – Mulgard May 07 '16 at 22:10
  • You are calling back into a detached fragment after rotation; you are keeping a reference (a callback) to the fragment, but that fragment may no longer be alive, so you have that issue. You are also leaking memory, the fragment can't be garbage collected although it is detached until the task completes. The proper way to fix this is to remove the callback on rotation, and when the fragment is recreated, re-attach the callback on the new instance of the fragment. – Francesc May 07 '16 at 22:12
  • so i will use `setRetainInstance(true)` and everything will be fine. thx. – Mulgard May 07 '16 at 22:15