Obsolete
Status Update
Comments
me...@thomaskeller.biz <me...@thomaskeller.biz> #2
I could workaround this problem by explicitely removing the fragment in question in onSaveInstanceState():
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
FragmentManager fm = getChildFragmentManager();
myFragment = new MyFragment();
fm.beginTransaction().add(R.id.container, myFragment).commit();
}
@Override
public void onSaveInstanceState(Bundle state) {
if (myFragment != null) {
FragmentManager fm = getChildFragmentManager();
fm.beginTransaction().remove(myFragment).commit();
}
super.onSaveInstanceState(state);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
FragmentManager fm = getChildFragmentManager();
myFragment = new MyFragment();
fm.beginTransaction().add(R.id.container, myFragment).commit();
}
@Override
public void onSaveInstanceState(Bundle state) {
if (myFragment != null) {
FragmentManager fm = getChildFragmentManager();
fm.beginTransaction().remove(myFragment).commit();
}
super.onSaveInstanceState(state);
}
me...@thomaskeller.biz <me...@thomaskeller.biz> #3
Hrm... after playing around even more I think this is a problem of a setTargetFragment() call. I basically did the following:
myFragment = new MyFragment();
fm.beginTransaction().add(R.id.container, myFragment).commit();
myFragment.setTargetFragment(this, 0);
When setTargetFragment(...) is removed, this crash does no longer occur.
myFragment = new MyFragment();
fm.beginTransaction().add(R.id.container, myFragment).commit();
myFragment.setTargetFragment(this, 0);
When setTargetFragment(...) is removed, this crash does no longer occur.
me...@thomaskeller.biz <me...@thomaskeller.biz> #4
I found a different way of implementing this. Instead of relying on setTargetFragment(), which is apparently buggy implemented, API-17's getParentFragment() can be used, which is luckily also available in the support library. With this the crash is gone and the workaround described in #1 is not needed.
mr...@gmail.com <mr...@gmail.com> #5
This is probably not a bug.
When re-creating your previous state, the child-Fragment is looking for the instance fo it's target-Fragment (which has been destroyed). As the target can not be found, an IllegalStateException is thrown.
One solution would be to set the target-Fragment to retain it's instance. Another possible solution might be updating the target-Fragment (by calling setTargetFragment(...) on the child) in your Activity's onCreate(), but I haven't tried this.
When re-creating your previous state, the child-Fragment is looking for the instance fo it's target-Fragment (which has been destroyed). As the target can not be found, an IllegalStateException is thrown.
One solution would be to set the target-Fragment to retain it's instance. Another possible solution might be updating the target-Fragment (by calling setTargetFragment(...) on the child) in your Activity's onCreate(), but I haven't tried this.
me...@thomaskeller.biz <me...@thomaskeller.biz> #6
Both are UI fragments, so I cannot retain their instances without leaking activities.
Your explanation sounds reasonable, though I have no idea why my base fragment (the target fragment in question) should not be existant, because it _should_ be created before its child fragments are created, no?
Anyways, even if this is wanted behaviour, the exception could at least be improved, because "target states" and "index 2" are implementation details of the FragmentManager and do not tell me anything unless I conduct the source code.
Your explanation sounds reasonable, though I have no idea why my base fragment (the target fragment in question) should not be existant, because it _should_ be created before its child fragments are created, no?
Anyways, even if this is wanted behaviour, the exception could at least be improved, because "target states" and "index 2" are implementation details of the FragmentManager and do not tell me anything unless I conduct the source code.
mr...@gmail.com <mr...@gmail.com> #7
Sorry, I was wrong altogether. My previous comment was mostly based on assumptions, but now I've looked into the source a bit, it appears my assumptions were completely off.
In short, it appears Fragment#setTargetFragment() is not meant for communication between a child and parent Fragment, but rather for communication between sibling-Fragments.
Now for an explanation. On line 847 in FragmentManager.java (v4 support lib source, revision 13) you can see that a Fragment will look for it's "targetFragment" in it's own FragmentManager (note: FragmentManager rather than child-FragmentManager). If you have set-up the Fragments as a parent and child, obviously these will not be present in the same FragmentManager. So, when trying to restore the Fragments from state, the child cannot find the target-Fragment (the parent) in the same FragmentManager, causing an IllegalStateException to be thrown.
Also as far as the exception goes; the FragmentManager will have dumped it's contents just above the Exception in your Logcat output. This is where you can look up which Fragment the Exception is referring to.
In short, it appears Fragment#setTargetFragment() is not meant for communication between a child and parent Fragment, but rather for communication between sibling-Fragments.
Now for an explanation. On line 847 in FragmentManager.java (v4 support lib source, revision 13) you can see that a Fragment will look for it's "targetFragment" in it's own FragmentManager (note: FragmentManager rather than child-FragmentManager). If you have set-up the Fragments as a parent and child, obviously these will not be present in the same FragmentManager. So, when trying to restore the Fragments from state, the child cannot find the target-Fragment (the parent) in the same FragmentManager, causing an IllegalStateException to be thrown.
Also as far as the exception goes; the FragmentManager will have dumped it's contents just above the Exception in your Logcat output. This is where you can look up which Fragment the Exception is referring to.
fs...@gmail.com <fs...@gmail.com> #8
Hi, I'm having this same issue, did anyone find a fix that doesn't need to remove the fragment in onSaveInstanceState()?
Thanks.
Thanks.
[Deleted User] <[Deleted User]> #9
I have got the same issue too. also using setTargetFragment to communicate between same level fragments. why is it happening?
en...@google.com <en...@google.com>
ta...@gmail.com <ta...@gmail.com> #10
This issue is not obsolete, at least not when adding a DialogFragment to one of the fragments in a FragmentStatePagerAdapter.
Android 5.01, targetApi 21, Support Library 21.0.3.
Setup:
-----
Having fragments in a v4.app.FragmentStatePagerAdapter. One of these fragments can have a DialogFragment shown to the user with dialog.show(getChildFragmentManager(), "dialog") and the DialogFragment has the parent set with setTargetFragment().
Action:
------
Application is sent to the background, destroyed, and recreated from state.
Observations:
------------
The application crashes with the below pasted exception when the dialog is still open. If the dialog fragment does not have the target fragment set, the crash does not occur. The workaround by #3 above works, using getParentFragment() instead of of using the target works.
java.lang.IllegalStateException: Fragment no longer exists for key android:target_state: index 8
at android.support.v4.app.FragmentManagerImpl.getFragment(FragmentManager.java:584)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:883)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1126)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1108)
at android.support.v4.app.FragmentManagerImpl.dispatchCreate(FragmentManager.java:1912)
at android.support.v4.app.Fragment.performCreate(Fragment.java:1776)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:913)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1126)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1108)
at android.support.v4.app.FragmentManagerImpl.dispatchCreate(FragmentManager.java:1912)
at android.support.v4.app.Fragment.performCreate(Fragment.java:1776)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:913)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1126)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1108)
at android.support.v4.app.FragmentManagerImpl.dispatchCreate(FragmentManager.java:1912)
at android.support.v4.app.FragmentActivity.onCreate(FragmentActivity.java:266)
at android.support.v7.app.ActionBarActivity.onCreate(ActionBarActivity.java:122)
Android 5.01, targetApi 21, Support Library 21.0.3.
Setup:
-----
Having fragments in a v4.app.FragmentStatePagerAdapter. One of these fragments can have a DialogFragment shown to the user with dialog.show(getChildFragmentManager(), "dialog") and the DialogFragment has the parent set with setTargetFragment().
Action:
------
Application is sent to the background, destroyed, and recreated from state.
Observations:
------------
The application crashes with the below pasted exception when the dialog is still open. If the dialog fragment does not have the target fragment set, the crash does not occur. The workaround by #3 above works, using getParentFragment() instead of of using the target works.
java.lang.IllegalStateException: Fragment no longer exists for key android:target_state: index 8
at android.support.v4.app.FragmentManagerImpl.getFragment(FragmentManager.java:584)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:883)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1126)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1108)
at android.support.v4.app.FragmentManagerImpl.dispatchCreate(FragmentManager.java:1912)
at android.support.v4.app.Fragment.performCreate(Fragment.java:1776)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:913)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1126)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1108)
at android.support.v4.app.FragmentManagerImpl.dispatchCreate(FragmentManager.java:1912)
at android.support.v4.app.Fragment.performCreate(Fragment.java:1776)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:913)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1126)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1108)
at android.support.v4.app.FragmentManagerImpl.dispatchCreate(FragmentManager.java:1912)
at android.support.v4.app.FragmentActivity.onCreate(FragmentActivity.java:266)
at android.support.v7.app.ActionBarActivity.onCreate(ActionBarActivity.java:122)
en...@google.com <en...@google.com>
ab...@bungie.com <ab...@bungie.com> #12
Although using Parent Fragment avoids the crash, it's not a good solution for me because I need the request code functionality of Target Fragment.
However, using getFragmentManager() instead of getChildFragmentManager() solved the problem for me while still being able to use Target Fragment.
However, using getFragmentManager() instead of getChildFragmentManager() solved the problem for me while still being able to use Target Fragment.
lb...@gmail.com <lb...@gmail.com> #13
Any news about this and how to solve it?
I'm using support library 22.1.1 and got this today...
I'm using support library 22.1.1 and got this today...
jh...@themeetgroup.com <jh...@themeetgroup.com> #14
@#13, you can still use the request code functionality, but you need to save+restore the request code separately, and just pass null as the target fragment:
fragment.setTargetFragment(null, MY_REQUEST_CODE);
then in the child fragment, because the FragmentManager refuses to save the target request code if the target fragment is null, you have to manually save that fragment's request code in onSaveInstanceState(), and restore it in onCreate():
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putInt(STATE_REQUEST_CODE, getTargetRequestCode());
setTargetFragment(null, -1);
super.onSaveInstanceState(outState);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null && savedInstanceState.containsKey(STATE_REQUEST_CODE)) {
setTargetFragment(null, savedInstanceState.getInt(STATE_REQUEST_CODE));
}
}
fragment.setTargetFragment(null, MY_REQUEST_CODE);
then in the child fragment, because the FragmentManager refuses to save the target request code if the target fragment is null, you have to manually save that fragment's request code in onSaveInstanceState(), and restore it in onCreate():
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putInt(STATE_REQUEST_CODE, getTargetRequestCode());
setTargetFragment(null, -1);
super.onSaveInstanceState(outState);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null && savedInstanceState.containsKey(STATE_REQUEST_CODE)) {
setTargetFragment(null, savedInstanceState.getInt(STATE_REQUEST_CODE));
}
}
ha...@google.com <ha...@google.com> #15
The problem is that you are showing the Fragment via its parent's ChildFragmentManager. If you do so, you should use getParentFragment(). If you want to use targetFragments, use getFragmentManager() instead.
ma...@gmail.com <ma...@gmail.com> #16
Android 4.2.2, targetApi 25, Support Library 21.0.3
Setup:
-----
Having activity A which is adding fragment F. F is adding child fragment F2 using ChildFragmentManager and setting itself as F2 targetFragment.
-> Activity A
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
F f = F.newInstance();
transaction.add(R.id.content, f);
transaction.commit();
-> Fragment F
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
F2 f2 = F2.newInstance();
f2.setTargetFragment(this, REQUEST_CODE_F2);
transaction.add(R.id.content, f2);
transaction.commit();
Action:
------
Application is send to the background and activity A get destroyed. I couldn't reproduce it in normal conditions but when using don't keep activities from developer options is reproducible every time.
Observations:
------------
The application crashes with the following exception. If fragment F doesn't set target fragment for fragment F2 the crash does not occur.
java.lang.RuntimeException: Unable to start activity ComponentInfo{test.test.A}: java.lang.IllegalStateException: Fragment no longer exists for key android:target_state: index 1
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2377)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2429)
at android.app.ActivityThread.access$800(ActivityThread.java:151)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1342)
at android.os.Handler.dispatchMessage(Handler.java:110)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:5341)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:825)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:641)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.IllegalStateException: Fragment no longer exists for key android:target_state: index 1
at android.support.v4.app.FragmentManagerImpl.getFragment(FragmentManager.java:873)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1209)
at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1523)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1585)
at android.support.v4.app.FragmentManagerImpl.dispatchCreate(FragmentManager.java:2827)
at android.support.v4.app.Fragment.restoreChildFragmentState(Fragment.java:1289)
at android.support.v4.app.Fragment.onCreate(Fragment.java:1260)
at test.test.F.onCreate(F.java:52)
at android.support.v4.app.Fragment.performCreate(Fragment.java:2172)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1243)
at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1523)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1585)
at android.support.v4.app.FragmentManagerImpl.dispatchCreate(FragmentManager.java:2827)
at android.support.v4.app.FragmentController.dispatchCreate(FragmentController.java:190)
at android.support.v4.app.FragmentActivity.onCreate(FragmentActivity.java:353)
at android.support.v7.app.AppCompatActivity.onCreate(AppCompatActivity.java:88)
at test.test.A.onCreate(MainActivity.java:19)
at android.app.Activity.performCreate(Activity.java:5343)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1088)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2331)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2429)
at android.app.ActivityThread.access$800(ActivityThread.java:151)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1342)
at android.os.Handler.dispatchMessage(Handler.java:110)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:5341)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:825)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:641)
at dalvik.system.NativeStart.main(Native Method)
Setup:
-----
Having activity A which is adding fragment F. F is adding child fragment F2 using ChildFragmentManager and setting itself as F2 targetFragment.
-> Activity A
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
F f = F.newInstance();
transaction.add(R.id.content, f);
transaction.commit();
-> Fragment F
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
F2 f2 = F2.newInstance();
f2.setTargetFragment(this, REQUEST_CODE_F2);
transaction.add(R.id.content, f2);
transaction.commit();
Action:
------
Application is send to the background and activity A get destroyed. I couldn't reproduce it in normal conditions but when using don't keep activities from developer options is reproducible every time.
Observations:
------------
The application crashes with the following exception. If fragment F doesn't set target fragment for fragment F2 the crash does not occur.
java.lang.RuntimeException: Unable to start activity ComponentInfo{test.test.A}: java.lang.IllegalStateException: Fragment no longer exists for key android:target_state: index 1
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2377)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2429)
at android.app.ActivityThread.access$800(ActivityThread.java:151)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1342)
at android.os.Handler.dispatchMessage(Handler.java:110)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:5341)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:825)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:641)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.IllegalStateException: Fragment no longer exists for key android:target_state: index 1
at android.support.v4.app.FragmentManagerImpl.getFragment(FragmentManager.java:873)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1209)
at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1523)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1585)
at android.support.v4.app.FragmentManagerImpl.dispatchCreate(FragmentManager.java:2827)
at android.support.v4.app.Fragment.restoreChildFragmentState(Fragment.java:1289)
at android.support.v4.app.Fragment.onCreate(Fragment.java:1260)
at test.test.F.onCreate(F.java:52)
at android.support.v4.app.Fragment.performCreate(Fragment.java:2172)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1243)
at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1523)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1585)
at android.support.v4.app.FragmentManagerImpl.dispatchCreate(FragmentManager.java:2827)
at android.support.v4.app.FragmentController.dispatchCreate(FragmentController.java:190)
at android.support.v4.app.FragmentActivity.onCreate(FragmentActivity.java:353)
at android.support.v7.app.AppCompatActivity.onCreate(AppCompatActivity.java:88)
at test.test.A.onCreate(MainActivity.java:19)
at android.app.Activity.performCreate(Activity.java:5343)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1088)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2331)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2429)
at android.app.ActivityThread.access$800(ActivityThread.java:151)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1342)
at android.os.Handler.dispatchMessage(Handler.java:110)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:5341)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:825)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:641)
at dalvik.system.NativeStart.main(Native Method)
al...@burda-forward.de <al...@burda-forward.de> #17
All Fragments must be managed by the same FragmentManager to prevent this crash!
In your case the target fragment (Fragment F) is managed by the activitys FragmentManager and the Fragment F2 is managed by the child FragmentManager. This results in a not restorable target fragment, because the child FragmentManager does not know that the target fragment can be found in the activitys FragmentManager...
In your case the target fragment (Fragment F) is managed by the activitys FragmentManager and the Fragment F2 is managed by the child FragmentManager. This results in a not restorable target fragment, because the child FragmentManager does not know that the target fragment can be found in the activitys FragmentManager...
[Deleted User] <[Deleted User]> #18
@#18 Thanks. The explanation seem reasonable. I replaced getChildFragmentManager() with getFragmentManager() to get the activity's fragment manager and this seems to fix the issue. But now other problem occurs.
The original logic is that Activity A is managing 3 fragments and depending on user selection it display the appropriate fragment. By default Activity A is adding Fragment F (which is adding it's child fragment in the activity's fragment manager) and then the user selects to display some other fragment. I use FragmentManager.replace() method to remove fragment F and add the other fragment, but when I press home button the app crashes with
java.lang.IllegalStateException: Failure saving state: F2 has target not in fragment manager: F
I think the problem is that since I am not using childFragmentManager() the child fragment F2 is not removed when it's parent and this lead to a fragment with reference to a fragment which is not in the fragment manager.
If I use the activity's fragment manager should I manually take care of this kind of dependencies (remove child fragments when parent is removed)?
The original logic is that Activity A is managing 3 fragments and depending on user selection it display the appropriate fragment. By default Activity A is adding Fragment F (which is adding it's child fragment in the activity's fragment manager) and then the user selects to display some other fragment. I use FragmentManager.replace() method to remove fragment F and add the other fragment, but when I press home button the app crashes with
java.lang.IllegalStateException: Failure saving state: F2 has target not in fragment manager: F
I think the problem is that since I am not using childFragmentManager() the child fragment F2 is not removed when it's parent and this lead to a fragment with reference to a fragment which is not in the fragment manager.
If I use the activity's fragment manager should I manually take care of this kind of dependencies (remove child fragments when parent is removed)?
ga...@gmail.com <ga...@gmail.com> #19
I just encountered this issue today and it took me a while to realize that the only reason this is happening is because the internal state persistence code silently doesn't support target fragments coming from another FragmentManager.
The bug wasn't obvious to me at first because I didn't encounter any exception, but my app crashed due to an infinite recursion in one of my Fragments due to getTargetFragment() returning itself. The current state saving code will happily store the index of a target fragment contained in a separate FragmentManager which causes one of these two things to happen during state restoration:
- The target index is out of bounds and an IllegalStateException is thrown.
- The target index is in bounds, but refers to an unexpected Fragment.
I am surprised that this issue has been around for so long. It seems like a considerable oversight in the implementation of the target fragment API, and the simplest fix would be adding a note in the documentation and adding the missing FragmentManager instance comparison to the state saving code (which would prevent unexpected behavior and make the problem a lot more obvious when it happens).
I thought about also adding such a check to setTargetFragment(), but this would lead to incompatibility for any application that currently works when using a target from another manager, either because they don't use state persistence or because they manually remove the target fragment before it happens.
I am currently setting up my environment and will attempt to submit a patch ASAP.
The bug wasn't obvious to me at first because I didn't encounter any exception, but my app crashed due to an infinite recursion in one of my Fragments due to getTargetFragment() returning itself. The current state saving code will happily store the index of a target fragment contained in a separate FragmentManager which causes one of these two things to happen during state restoration:
- The target index is out of bounds and an IllegalStateException is thrown.
- The target index is in bounds, but refers to an unexpected Fragment.
I am surprised that this issue has been around for so long. It seems like a considerable oversight in the implementation of the target fragment API, and the simplest fix would be adding a note in the documentation and adding the missing FragmentManager instance comparison to the state saving code (which would prevent unexpected behavior and make the problem a lot more obvious when it happens).
I thought about also adding such a check to setTargetFragment(), but this would lead to incompatibility for any application that currently works when using a target from another manager, either because they don't use state persistence or because they manually remove the target fragment before it happens.
I am currently setting up my environment and will attempt to submit a patch ASAP.
lb...@gmail.com <lb...@gmail.com> #20
If anyone here wishes to use a DialogFragment and has issues with it, I've created a library that can avoid all of them, by avoiding using fragments :
https://github.com/AndroidDeveloperLB/DialogShard
jh...@themeetgroup.com <jh...@themeetgroup.com> #21
@#20, the way we fixed that issue is by explicitly removing the target fragment if the target does not exist on onSaveInstanceState():
@Override
public void onSaveInstanceState(Bundle outState) {
// safe guard against loss of target fragment
Fragment targetFragment = getTargetFragment();
if (targetFragment != null) {
FragmentManager fragmentManager = getFragmentManager();
if (!fragmentManager.getFragments().contains(targetFragment)) {
if (BuildConfig.DEBUG) Log.d(TAG, "while saving state, the target fragment has been removed " + targetFragment);
setTargetFragment(null, getTargetRequestCode());
}
}
super.onSaveInstanceState(outState);
That means you then need to restore the target when both fragments have been restored.
@Override
public void onSaveInstanceState(Bundle outState) {
// safe guard against loss of target fragment
Fragment targetFragment = getTargetFragment();
if (targetFragment != null) {
FragmentManager fragmentManager = getFragmentManager();
if (!fragmentManager.getFragments().contains(targetFragment)) {
if (BuildConfig.DEBUG) Log.d(TAG, "while saving state, the target fragment has been removed " + targetFragment);
setTargetFragment(null, getTargetRequestCode());
}
}
super.onSaveInstanceState(outState);
That means you then need to restore the target when both fragments have been restored.
sa...@google.com <sa...@google.com> #22
Thank you for your feedback. We have tried our best to address the issue reported, however our product team has shifted work priority which doesn't include this issue. For now, we will be closing the issue as "Won't Fix (Obsolete)". If this issue still currently exists, we request that you log a new issue along with the latest bug report here: https://goo.gl/TbMiIO and reference this bug for context.
Description
Setup:
-----
Having fragments of type SentenceFragment in a v4.app.FragmentStatePagerAdapter. This adapter serves pages in its parent fragment, HomeFragment. Now be in another fragment, SurroundingsFragment, which has another nested fragment, CompoundMapFragment showing a MapsV2 MapView. The first fragment, HomeFragment, is still managed in the FragmentManager.
Action:
------
Application is send to the background, destroyed, and recreated from state.
Observations:
------------
The application crashes with the below pasted exception when two fragments with child fragments are managed. If the second fragment, the SurroundingsFragment, is removed and another, plain, fragment is displayed instead, the crash does not occur.
04-18 23:58:08.183: E/AndroidRuntime(31955): FATAL EXCEPTION: main
04-18 23:58:08.183: E/AndroidRuntime(31955): java.lang.RuntimeException: Unable to start activity ComponentInfo{my.project.package/my.project.package.activities.RootActivity}: java.lang.IllegalStateException: Fragement no longer exists for key android:target_state: index 2
04-18 23:58:08.183: E/AndroidRuntime(31955): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2180)
04-18 23:58:08.183: E/AndroidRuntime(31955): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2230)
04-18 23:58:08.183: E/AndroidRuntime(31955): at android.app.ActivityThread.access$600(ActivityThread.java:141)
04-18 23:58:08.183: E/AndroidRuntime(31955): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1234)
04-18 23:58:08.183: E/AndroidRuntime(31955): at android.os.Handler.dispatchMessage(Handler.java:99)
04-18 23:58:08.183: E/AndroidRuntime(31955): at android.os.Looper.loop(Looper.java:137)
04-18 23:58:08.183: E/AndroidRuntime(31955): at android.app.ActivityThread.main(ActivityThread.java:5041)
04-18 23:58:08.183: E/AndroidRuntime(31955): at java.lang.reflect.Method.invokeNative(Native Method)
04-18 23:58:08.183: E/AndroidRuntime(31955): at java.lang.reflect.Method.invoke(Method.java:511)
04-18 23:58:08.183: E/AndroidRuntime(31955): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
04-18 23:58:08.183: E/AndroidRuntime(31955): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
04-18 23:58:08.183: E/AndroidRuntime(31955): at dalvik.system.NativeStart.main(Native Method)
04-18 23:58:08.183: E/AndroidRuntime(31955): Caused by: java.lang.IllegalStateException: Fragement no longer exists for key android:target_state: index 2
04-18 23:58:08.183: E/AndroidRuntime(31955): at android.support.v4.app.FragmentManagerImpl.getFragment(FragmentManager.java:559)
04-18 23:58:08.183: E/AndroidRuntime(31955): at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:847)
04-18 23:58:08.183: E/AndroidRuntime(31955): at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1088)
04-18 23:58:08.183: E/AndroidRuntime(31955): at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1070)
04-18 23:58:08.183: E/AndroidRuntime(31955): at android.support.v4.app.FragmentManagerImpl.dispatchCreate(FragmentManager.java:1856)
04-18 23:58:08.183: E/AndroidRuntime(31955): at android.support.v4.app.Fragment.performCreate(Fragment.java:1450)
04-18 23:58:08.183: E/AndroidRuntime(31955): at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:877)
04-18 23:58:08.183: E/AndroidRuntime(31955): at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1088)
04-18 23:58:08.183: E/AndroidRuntime(31955): at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1070)
04-18 23:58:08.183: E/AndroidRuntime(31955): at android.support.v4.app.FragmentManagerImpl.dispatchCreate(FragmentManager.java:1856)
04-18 23:58:08.183: E/AndroidRuntime(31955): at android.support.v4.app.FragmentActivity.onCreate(FragmentActivity.java:210)
04-18 23:58:08.183: E/AndroidRuntime(31955): at com.github.rtyley.android.sherlock.roboguice.activity.RoboSherlockFragmentActivity.onCreate(RoboSherlockFragmentActivity.java:34)
04-18 23:58:08.183: E/AndroidRuntime(31955): at my.project.package.activities.BaseActivity.onCreate(BaseActivity.java:60)
04-18 23:58:08.183: E/AndroidRuntime(31955): at my.project.package.activities.RootActivity.onCreate(RootActivity.java:103)
04-18 23:58:08.183: E/AndroidRuntime(31955): at android.app.Activity.performCreate(Activity.java:5104)
04-18 23:58:08.183: E/AndroidRuntime(31955): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1080)
04-18 23:58:08.183: E/AndroidRuntime(31955): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2144)
04-18 23:58:08.183: E/AndroidRuntime(31955): ... 11 more
The fragment manager also dumped its state, this is attached.