Why is Android O failing with "does not belong to this FragmentManager!"
Asked Answered
R

8

30

I have migrated my application to Android O in Android Studio 3

Running on an Android O emulator all my dialogFragments now fail with :-

java.lang.IllegalStateException: Fragment MyDialogFragment{43ccf50 #2 MyDialogFragment} declared target fragment SettingsFragment{ceed549 #0 id=0x7f0f0142 android:switcher:2131689794:0} that does not belong to this FragmentManager!
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1316)
at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1624)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1689)
at android.support.v4.app.BackStackRecord.executeOps(BackStackRecord.java:794)
at android.support.v4.app.FragmentManagerImpl.executeOps(FragmentManager.java:2470)
at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2260)
at android.support.v4.app.FragmentManagerImpl.optimizeAndExecuteOps(FragmentManager.java:2213)
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2122)
at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:746)
at android.os.Handler.handleCallback(Handler.java:769)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6535)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)

I have made no code changes whatsoever.

What has changed in Android O that previously working DialogFragments now fail display?

Android Studio 3.0 Canary 1
Build #AI-171.4010489, built on May 15, 2017
JRE: 1.8.0_112-release-b736 x86_64
JVM: OpenJDK 64-Bit Server VM by JetBrains s.r.o
Mac OS X 10.11.6

    compileSdkVersion 'android-O'
    buildToolsVersion "26.0.0-rc2"

   AndroidManifest.xml
    defaultConfig {
        minSdkVersion 16
        targetSdkVersion 'O'
        }

compile 'com.android.support:appcompat-v7:26.0.0-beta1'
compile 'com.android.support:cardview-v7:26.0.0-beta1'
compile 'com.android.support:design:26.0.0-beta1'
    compile 'com.android.support:percent:26.0.0-beta1'

  dependencies {
        classpath 'com.android.tools.build:gradle:3.0.0-alpha1'

    }
Rajkot answered 26/5, 2017 at 14:22 Comment(4)
It's a bug because I saw other reports, so you need to fill a bug report in the issue tracker. However I'm not sure about if the bug is inside v4 support library or in Android itself. So you should try to use built-in fragments instead of v4 just to see if something changes.Toritorie
Any one found out if it's the support lib?Newel
I think this issue was caused by Android O "fixing" Fragment related bugs. I believe previous versions of android allowed broken code to work as expected. I could be wrong.Rajkot
Guys I checked by replacing Fragment to V4 SupportFragment.. but in that also this issue is there... as Oreo kept check for their Old bug...Jeopardous
T
17

I had the same problem, definitely an android bug. It happens when you are showing a fragment from another fragment using it as target. As workaround you can use:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
    getActivity().getFragmentManager().beginTransaction().add(dialogFrag, "dialog").commit();
else
    getChildFragmentManager().beginTransaction().add(dialogFrag,"dialog").commit();
Toritorie answered 25/6, 2017 at 9:1 Comment(3)
Yes , this solution works correctly after N_MR1 we should have to use getFragmentManager while from JELLY_BEAN_MR1 to N_MR1 it seems getChildFragmentManager should be sused.Eniwetok
Hey, but if I use getFragmentManager() instead of getChildFragmentManager(), the childFragment.getParent() will return null. How do you fix that?Absquatulate
Using getFragmentManager() instead of getChildFragmentManager() would work but it affects getParentFragment(). If you do not use a getChildFragmentManager() for nested fragment then you will not be able the get the parent fragment by using getParentFragment() in child/nested fragment.Whisk
T
54

For me this was not only an issue on Android O but also on older versions. The oldest version I tested was API Level 16.

I was instantiating my fragments using this code:

MyFragment myFragment = MyFragment.newInstance();
myFragment.setTargetFragment(ParentFragment.this, 0);
myFragment.show(getActivity().getSupportFragmentManager(), null);

Where ParentFragment.this is a custom class extending android.support.v4.app.Fragment, MyFragment also extends this class and is a child fragment of the ParentFragment fragment (hence it's name).

I thought that I had to use a SupportFragmentManager (the getSupportFragmentManager() method) because I am using a fragment of the support package so I tried to call getActivity().getSupportFragmentManager() to get an activity reference that supported this method.

This does not seem to be the correct way though. I changed those calls to:

MyFragment myFragment = MyFragment.newInstance();
myFragment.setTargetFragment(ParentFragment.this, 0);
myFragment.show(getFragmentManager(), null);

so the fragment decides on it's own which FragmentManager to use and the error is gone now.

Hope this helps someone.

Trogon answered 6/8, 2017 at 17:57 Comment(2)
while this solution works I can't find why it does not work with getChildFragmentManager()Babara
it's working for me but the IDE is saying Argument 'getFragmentManager()' might be null. I set a if (getFragmentManager() != null) and it went away.Windsor
T
17

I had the same problem, definitely an android bug. It happens when you are showing a fragment from another fragment using it as target. As workaround you can use:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
    getActivity().getFragmentManager().beginTransaction().add(dialogFrag, "dialog").commit();
else
    getChildFragmentManager().beginTransaction().add(dialogFrag,"dialog").commit();
Toritorie answered 25/6, 2017 at 9:1 Comment(3)
Yes , this solution works correctly after N_MR1 we should have to use getFragmentManager while from JELLY_BEAN_MR1 to N_MR1 it seems getChildFragmentManager should be sused.Eniwetok
Hey, but if I use getFragmentManager() instead of getChildFragmentManager(), the childFragment.getParent() will return null. How do you fix that?Absquatulate
Using getFragmentManager() instead of getChildFragmentManager() would work but it affects getParentFragment(). If you do not use a getChildFragmentManager() for nested fragment then you will not be able the get the parent fragment by using getParentFragment() in child/nested fragment.Whisk
L
6

I had the same case as Markus Ressel but I was using getChildFragmentManager(). I replaced that with getFragmentManager() and it resolved the issue.

UPDATE: I've now been working with childFragmentManager and have some feedback.

When dealing with inner fragments that are hosted by a fragment (so a fragment within a fragment) use the childFragmentManager. This fragment manager differs from the activities getSupportFragmentManager. The two are not the same. It's a separation of concerns.

So I've made a rule that fragments hosting child fragments will always use the childFragmentManager and things not inside host fragments can use getSupportfragmentManager.

Languishing answered 28/8, 2017 at 4:12 Comment(0)
K
6

I just faced the same issues in the project that I am currently working on when we moved to Android Studio 3 and upgraded the support library to version 26. All of a sudden, without changing the code, we got tons of this exception. In the end I found out the following:

Google added a new "sanity check" in the sources of the v4 Fragment Manager in January this year (not sure into what release that went) that refuses to add a fragment using a Fragment Manager, if it has a target fragment set and the target fragment cannot be found in the active fragments of the same Fragment Manager. The patch is described in detail here

Ealier versions seem to not have cared about that. Took me a few days to update all the areas in our code where fragments that were added using the Support Fragment Manager used the Child Fragment Manager for their subfragments with the parent fragment as target. Well, late penalty for writing bad code.

Konyn answered 3/11, 2017 at 20:3 Comment(2)
Thanks for pointing to this patch. It somehow does not explain two things: 1. Why is it a bad idea? 2. What is the right or better way for child fragments to call and send data back to parent fragments. Any idea?Heteromerous
@Heteromerous Having two fragments in two different fragment managers means that the lifecycles of these two fragments are uncoordinated. It could for example happen that the target fragment of a fragment has already been deleted from its fragment manager but will not be garbage collected as there is still a reference on it. If you don't use an architecture as for example MVVM where you have a view model that can be referenced by all fragments you can instead for example call methods in the Activity that created the fragments. The lifecycles of an Activity and its fragments is much better coordinated.Konyn
P
2

Recently, my app experienced the same issue when I targeted it for Android O. As a solution, use:

myDialogFragment.show(SettingsFragment.this.getFragmentManager(), TAG);

instead of:

myDialogFragment.show(getFragmentManager(), TAG);
// or  
myDialogFragment.show(getSupportFragmentManager(), TAG);
// or  
myDialogFragment.show(getChildFragmentManager(), TAG);
Piedadpiedmont answered 24/1, 2019 at 10:14 Comment(0)
G
2

while working on an app, I encountered the same problem and I solved it by (in kotlin)

chooseRegionFragment.setTargetFragment(this@ParentFragment, REQUEST_CODE_CHOOSE_REGION)

chooseRegionFragment.show([email protected], TAG)

it translates to

chooseRegionFragment.setTargetFragment(ParentFragment.this, REQUEST_CODE_CHOOSE_REGION); 

chooseRegionFragment.show(ParentFragment.this.getParentFragmentManager, TAG);

in java

Gaff answered 24/4, 2020 at 19:25 Comment(0)
F
1

my app work well until upgrade target version to 27 then i face same issue when call setTargetFragment (Fragment fragment, int requestCode)

example:

chooseRegionFragment.setTargetFragment(ParentFragment.this, REQUEST_CODE_CHOOSE_REGION); 

just change to:

chooseRegionFragment.setTargetFragment(getRootParentFragment(this), REQUEST_CODE_CHOOSE_REGION);

getRootParentFragment(this) this method will find root parent of fragments for you

/** 
   * find root parent of fragment 
   */ 
  public static Fragment getRootParentFragment(Fragment fragment) { 
    Fragment parent = fragment.getParentFragment(); 
    if(parent == null) 
      return fragment; 
    else 
      return getRootParentFragment(parent); 
  } 
Footloose answered 24/5, 2018 at 1:29 Comment(0)
W
1

Use below solution and you do not need to worry about which fragment managers you are dealing with,

Assuming that you must have used a BaseFragment.

First create an interface:

public interface OnRequestUpdateListener {
void onRequestUpdate(final int requestCode, final Intent data);

void setRequestFragment(final BaseFragment requestFragment, int requestCode);

BaseFragment getRequestFragment();

int getRequestCode();

}

Implement that interface in your BaseFragment

public class BaseFragment extends Fragment implements OnRequestUpdateListener {
private BaseFragment requestFragment;

private int requestCode;

@Override
public void onRequestUpdate(int requestCode, Intent data) {
 // you can implement your logic the same way you do in onActivityResult
}

@Override
public void setRequestFragment(BaseFragment requestFragment, int requestCode) {
    this.requestFragment = requestFragment;
    this.requestCode = requestCode;
}

@Override
public BaseFragment getRequestFragment() {
    return requestFragment;
}

@Override
public int getRequestCode() {
    return requestCode;
}

}

Then, replace the setTargetFragment with setRequestFragment and replace getTargetFragment with getRequestFragment.

Here, you could also user onRequestUpdate in place of onActivityResult.

This is a custom solution without bothering about the which fragment manager you are using.

Using getFragmentManager() instead of getChildFragmentManager() would also work but it affects getParentFragment(). If you do not use a getChildFragmentManager() for nested fragment then you will not be able the get the parent fragment by using getParentFragment() in child/nested fragment.

Whisk answered 1/3, 2019 at 6:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.