J'ai migré mon application vers Android O dans Android Studio 3
Fonctionnant sur un émulateur Android O, tous mes dialogFragments échouent maintenant avec: -
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)
Je n'ai apporté aucun changement de code.
Qu'est-ce qui a changé dans Android O, où DialogFragments qui fonctionnait auparavant ne fonctionne plus?
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'
}
J'ai eu le même problème, certainement un bug d'Android. Cela se produit lorsque vous montrez un fragment d’un autre fragment en l’utilisant comme cible. Pour contourner ce problème, vous pouvez utiliser:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
getActivity().getFragmentManager().beginTransaction().add(dialogFrag, "dialog").commit();
else
getChildFragmentManager().beginTransaction().add(dialogFrag,"dialog").commit();
Pour moi, ce n'était pas seulement un problème sur Android O, mais aussi sur des versions plus anciennes… .. La version la plus ancienne que j'ai testée était l'API Level 16.
J'instanciais mes fragments en utilisant ce code:
MyFragment myFragment = MyFragment.newInstance();
myFragment.setTargetFragment(ParentFragment.this, 0);
myFragment.show(getActivity().getSupportFragmentManager(), null);
Où ParentFragment.this
est une classe personnalisée qui étend Android.support.v4.app.Fragment
, MyFragment
étend également cette classe et est un fragment enfant du fragment ParentFragment
(d'où son nom).
Je pensais que je devais utiliser SupportFragmentManager (la méthode getSupportFragmentManager()
) parce que j'utilisais un fragment du package de support. J'ai donc essayé d'appeler getActivity().getSupportFragmentManager()
pour obtenir une référence d'activité prenant en charge cette méthode.
Cela ne semble pas être la bonne façon cependant… .. J'ai changé ces appels en:
MyFragment myFragment = MyFragment.newInstance();
myFragment.setTargetFragment(ParentFragment.this, 0);
myFragment.show(getFragmentManager(), null);
ainsi, le fragment décide lui-même quel FragmentManager utiliser et l'erreur a disparu.
J'espère que ça aide quelqu'un.
Je viens de faire face aux mêmes problèmes dans le projet sur lequel je travaille actuellement lorsque nous avons migré vers Android Studio 3 et mis à niveau la bibliothèque de support vers la version 26. Tout à coup, sans changer le code, nous avons eu des tonnes de cette exception. Finalement, j'ai découvert ce qui suit:
Google a ajouté un nouveau "contrôle d'intégrité" dans les sources du gestionnaire de fragments v4 en janvier de cette année (ne sachant pas dans quelle version se trouvait cette version) qui refuse d'ajouter un fragment à l'aide d'un gestionnaire de fragments, s'il contient un fragment de cible et la cible fragment ne peut pas être trouvé dans les fragments actifs du même gestionnaire de fragments. Le patch est décrit en détail ici
Les versions d'Ealier ne semblent pas s'en soucier. Il m'a fallu quelques jours pour mettre à jour toutes les zones de notre code où les fragments ajoutés à l'aide du gestionnaire de fragments de support utilisaient le gestionnaire de fragments d'enfant pour leurs sous-fragments avec le fragment parent comme cible. Eh bien, pénalité de retard pour avoir écrit un mauvais code.
J'avais le même cas que Markus Ressel mais j'utilisais getChildFragmentManager (). J'ai remplacé cela par getFragmentManager () et le problème a été résolu.
MISE À JOUR: Je travaille maintenant avec childFragmentManager et j'ai des commentaires.
Lorsque vous traitez avec des fragments internes hébergés par un fragment (donc un fragment dans un fragment), utilisez childFragmentManager. Ce gestionnaire de fragments diffère des activités getSupportFragmentManager. Les deux ne sont pas les mêmes. C'est une séparation des préoccupations.
J'ai donc défini comme une règle que les fragments hébergeant des fragments enfants utiliseront toujours childFragmentManager et que les éléments ne se trouvant pas à l'intérieur des fragments hôte peuvent utiliser getSupportfragmentManager.
mon application fonctionne bien jusqu’à la mise à niveau de la version cible à 27 puis j’ai le même problème lorsqu’appelle setTargetFragment (fragment de fragment, int requestCode)
exemple:
chooseRegionFragment.setTargetFragment(ParentFragment.this, REQUEST_CODE_CHOOSE_REGION);
changez simplement pour:
chooseRegionFragment.setTargetFragment(getRootParentFragment(this), REQUEST_CODE_CHOOSE_REGION);
getRootParentFragment(this)
cette méthode trouvera pour vous le parent racine de fragments
/**
* find root parent of fragment
*/
public static Fragment getRootParentFragment(Fragment fragment) {
Fragment parent = fragment.getParentFragment();
if(parent == null)
return fragment;
else
return getRootParentFragment(parent);
}
Utilisez la solution ci-dessous et vous n'avez pas à vous soucier des gestionnaires de fragments avec lesquels vous traitez,
En supposant que vous ayez utilisé un BaseFragment.
Commencez par créer une interface:
public interface OnRequestUpdateListener {
void onRequestUpdate(final int requestCode, final Intent data);
void setRequestFragment(final BaseFragment requestFragment, int requestCode);
BaseFragment getRequestFragment();
int getRequestCode();
}
Implémentez cette interface dans votre 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;
}
}
Ensuite, remplacez setTargetFragment par setRequestFragment et remplacez getTargetFragment par getRequestFragment.
Ici, vous pouvez également utiliser onRequestUpdate à la place de onActivityResult.
Ceci est une solution personnalisée sans se soucier du gestionnaire de fragments que vous utilisez.
Utiliser getFragmentManager () au lieu de getChildFragmentManager () fonctionnerait aussi, mais cela affecterait getParentFragment (). Si vous n'utilisez pas getChildFragmentManager () pour un fragment imbriqué, vous ne pourrez pas obtenir le fragment parent en utilisant getParentFragment () dans un fragment enfant/imbriqué.
Récemment, mon application a rencontré le même problème lorsque je l'ai ciblée pour Android O. Comme solution, utilisez:
myDialogFragment.show(SettingsFragment.this.getFragmentManager(), TAG);
au lieu de:
myDialogFragment.show(getFragmentManager(), TAG);
// or
myDialogFragment.show(getSupportFragmentManager(), TAG);
// or
myDialogFragment.show(getChildFragmentManager(), TAG);