Why I am getting “Cannot remove Fragment attached

2019-08-21 11:52发布

问题:

I am trying to remove a fragment but very rarely on Crashlytics I am able to see a bug java.lang.IllegalStateException: Cannot remove Fragment attached to a different FragmentManager.

I have a separate fragment for fetching user's current location. I use below code to open the fragment.

private void openLocationFragment() {
    LocationFragment locationFragment = LocationFragment.newInstance();
    FragmentManager fragmentManager = getSupportFragmentManager();
    fragmentManager.beginTransaction()
            .replace(R.id.fragmentContainer, locationFragment, "location_fragment")
            .commitAllowingStateLoss();
}

Now In fragment as soon as i get a location update, I call method onLocationFetched using listener attached to the activity.

Inside this method I remove the fragment using below code.

@Override
public void onLocationFetched(Location location) {
    FragmentManager fragmentManager = getSupportFragmentManager();
    LocationFragment locationFragment = (LocationFragment) fragmentManager.findFragmentByTag("location_fragment");
    if (locationFragment != null) {
        fragmentManager.beginTransaction()
                .remove(locationFragment) // Here is the exception
                .commitAllowingStateLoss();
    }
    if(location == null){
        fetchUserDetails();
    }else
        fetchCity(location);
}

StackTrace:

Fatal Exception: **java.lang.IllegalStateException: Cannot remove Fragment attached to a different FragmentManager.** 

Fragment LocationFragment{32a2ed0 (d41fc341-baf2-4266-948a-866fba7e57b5) id=0x7f09028b location_fragment} is already attached to a FragmentManager.
       at androidx.fragment.app.BackStackRecord.remove(BackStackRecord.java:316)
       at com.avail.easyloans.feature.marketplace.activities.ActivityMarketplace.onFragmentFetched(ActivityMarketplace.java:909)
       at com.avail.easyloans.base.fragments.LocationFragment.sendLocationToClient(LocationFragment.java:192)
       at com.avail.easyloans.base.fragments.LocationFragment.access$000(LocationFragment.java:46)
       at com.avail.easyloans.base.fragments.LocationFragment$4.onSuccess(LocationFragment.java:220)
       at com.avail.easyloans.base.fragments.LocationFragment$4.onSuccess(LocationFragment.java:214)
       at com.google.android.gms.tasks.zzn.run(zzn.java:4)
       at android.os.Handler.handleCallback(Handler.java:836)
       at android.os.Handler.dispatchMessage(Handler.java:103)
       at android.os.Looper.loop(Looper.java:203)
       at android.app.ActivityThread.main(ActivityThread.java:6339)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1084)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:945)

What's wrong am I doing here?

回答1:

I expect that each Activity is responsible for maintaining its own Fragments. This would means that if the Fragment you are trying to access is associated with another Activity's FragmentManager, then your app will fail.

I can think of a couple of ways around this, depending on the behaviour you want. If you don't mind having two LocationFragments having their own separate lifecycles in two separate activities, you can access them with findFragmentById() on your FragmentManager. I.e.:

private void openLocationFragment() {
    FragmentManager fragmentManager = getSupportFragmentManager();
    final Fragment current = fragmentManager.findFragmentById(R.id.fragmentContainer);
    if(current == null || !(current instanceof LocationFragment)) {
        fragmentManager.beginTransaction()
            .replace(R.id.fragmentContainer, LocationFragment.newInstance())
            .commitAllowingStateLoss();
    }
}

And do the same in your other Activity. Alternatively, you can invoke your other Activity that hosts the LocationFragment in onLocationFetched()