Je reçois un bogue très déroutant sur lequel je ne sais même pas comment commencer à travailler.
J'ai une application simple avec une activité, les vues sont implémentées avec des fragments. L'un des fragments contient un ViewPager; J'ai donc décidé d'utiliser la classe getChildFragmentManager de la bibliothèque de support v4. J'ai également dû utiliser ActionBarSherlock, ce qui a posé un problème, car il n'est pas livré avec la v11 de la bibliothèque v4.
J'ai résolu ce problème en remplaçant la bibliothèque de support v4 dans ABS par la bibliothèque v11. Tout ce qui était compilé et semblait fonctionner, y compris ViewPager.
Voici la partie étrange:
La première fois que le fragment avec ViewPager s'ouvre, il fonctionne correctement. mais la deuxième fois, l'application se bloque, ce qui donne une trace inutile de la pile. À partir du débogage, j'ai découvert que le problème venait du fragment FragmentManager renvoyé par getChildFragmentManager; il génère l'erreur Aucune activité.
Quelqu'un a-t-il une idée de ce qui pourrait causer cela?
Je posterai le code que vous jugez pertinent.
Merci, David
J'ai suivi le lien dans réponse jeremyvillalobos (ce qui était très utile) qui m'a conduit à cette solution de contournement .
public class CustomFragment extends Fragment {
private static final Field sChildFragmentManagerField;
static {
Field f = null;
try {
f = Fragment.class.getDeclaredField("mChildFragmentManager");
f.setAccessible(true);
} catch (NoSuchFieldException e) {
Log.e(LOGTAG, "Error getting mChildFragmentManager field", e);
}
sChildFragmentManagerField = f;
}
@Override
public void onDetach() {
super.onDetach();
if (sChildFragmentManagerField != null) {
try {
sChildFragmentManagerField.set(this, null);
} catch (Exception e) {
Log.e(LOGTAG, "Error setting mChildFragmentManager field", e);
}
}
}
...
}
Cela fonctionne bien pour moi, sans qu'il soit nécessaire de reconstituer le fragment.
Cela semble être un bug signalé à
https://code.google.com/p/Android/issues/detail?id=42601
La variable
FragmentManagerImpl mChildFragmentManager;
Dans Fragment.Java n'est pas défini sur null lors du détachement. Ainsi, lors du prochain chargement du fragment, la variable pointe toujours sur le dernier parent.
Comme discuté sur ce fil, une solution de contournement consiste à rétablir le fragment.
Dans mon cas, je basculais entre les fragments dans un onglet ActionBar. Le fragment en question a imbriqué de fragments et plantait l’application en revenant au fragment de chargeur de fichiers. Voici donc le code de contournement:
class MainTabsListener implements ActionBar.TabListener {
public Fragment fragment;
public int TabPosition;
public MainTabsListener(Fragment fragment, int tab_position) {
this.fragment = fragment;
TabPosition = tab_position;
}
@Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
}
@Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
CurrentFragment = fragment;
CurrentTabSelectedPos = TabPosition;
/**
* This is a work-around for Issue 42601
* https://code.google.com/p/Android/issues/detail?id=42601
*
* The method getChildFragmentManager() does not clear up
* when the Fragment is detached.
*/
if( fragment instanceof FileLoaderFragment ){
fragment = reinstatiateFileLoaderFragment();
}
ft.replace(R.id.fragment_container, fragment);
}
@Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
ft.remove(fragment);
}
}
malheureusement, c'est un bug de support v4, toujours là :(
Lorsque vous choisissez un autre fragment via le tiroir de navigation ou un objet similaire, le fragment contenant des sous-fragments est détaché. Ainsi, fragmentManager (getChildFragmentManager ()) de ces sous-fragments n'existe plus. alors que ces fragments reviennent, une erreur est survenue. Bombe!
Évidemment, le support technique v4 devrait nettoyer mChildFragmentManager dans onDetach (), mais ce n’est pas le cas, nous devons donc compter sur nous-mêmes. tels que les codes suivants dans le fragment qui a des sous-fragments:
@Override
public void onDetach() {
try {
Field childFragmentManager = Fragment.class.getDeclaredField("mChildFragmentManager");
childFragmentManager.setAccessible(true);
childFragmentManager.set(this, null);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
super.onDetach();
}
Tout ira bien, passez une bonne journée :)
J'ai le même problème.
Dans une activité, j'ai 3 boutons pour changer de fragment avec transaction.replace (...)
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.layout_tablet_paneau, mLigneMessageFragment);
L'un de ces fragments contient une ViewPage avec un FragmentPagerAdapter personnalisé. Par conséquent, je dois faire getChildFragmentManager (), pour autoriser les fragments imbriqués.
le constructeur est ici:
public LignePagerAdapter(Fragment ligneMessageTabletFragment) {
super(ligneMessageTabletFragment.getChildFragmentManager());
}
J'ai donc la même erreur: la première émission de ce fragment est corrigée, mais lorsque je montre un autre fragment et que je reviens sur celui-ci, j'obtiens cette exception:
02-26 11: 57: 50.798: D/ACRA (776): Attendre que Toast + soit terminé. Kill Application? true 02-26 11: 57: 50.798: E/AndroidRuntime (776): EXCEPTION FATALE: main 02-26 11: 57: 50.798: E/AndroidRuntime (776): Java.lang.IllegalStateException: Non activité 02-26 11: 57: 50.798: E/AndroidRuntime (776): sur Android.support.v4.app.FragmentManagerImpl.moveToState (FragmentManager.Java:1075) 02-26 11: 57: 50.798 : E/AndroidRuntime (776): sur Android.support.v4.app.FragmentManagerImpl.moveToState (FragmentManager.Java:1070) 02-26 11: 57: 50.798: E/AndroidRuntime (776): sur Android.support .v4.app.FragmentManagerImpl.dispatchActivityCreated (FragmentManager.Java:1861) 02-26 11: 57: 50.798: E/AndroidRuntime (776): sur Android.support.v4.app.Fragment.per.ActivityCreated (Fragment.Java) : 1474) 02-26 11: 57: 50.798: E/AndroidRuntime (776): sur Android.support.v4.app.FragmentManagerImpl.moveToState (FragmentManager.Java:931) 02-26 11:57. : 50.798: E/AndroidRuntime (776): sur Android.support.v4.app.FragmentManagerImpl.moveToState (FragmentManager.Java:1088) 02-26 11: 57: 50.798: E/AndroidRuntime ( 776): sur Android.support.v4.app.BackStackRecord.run (BackStackRecord.Java:682) 02-26 11: 57: 50.798: E/AndroidRuntime (776): sur Android.support.v4.app. FragmentManagerImpl.execPendingActions (FragmentManager.Java:1444) 02-26 11: 57: 50.798: E/AndroidRuntime (776): sur Android.support.v4.app.FragmentManagerImpl $ 1.run (FragmentManager.Java:429) 02-26 11: 57: 50.798: E/AndroidRuntime (776): sur Android.os.Handler.handleCallback (Gestionnaire.Java:587) 02-26 11: 57: 50.798: E/AndroidRuntime (776 ): sur Android.os.Handler.dispatchMessage (Handler.Java:92) 02-26 11: 57: 50.798: E/AndroidRuntime (776): sur Android.os.Looper.loop (Looper.Java:132 ) 02-26 11: 57: 50.798: E/AndroidRuntime (776): sur Android.app.ActivityThread.main (ActivityThread.Java:4126) 02-26 11: 57: 50.798: E/AndroidRuntime (776): sur Java.lang.reflect.Method.invokeNative (Méthode native) 02-26 11: 57: 50.798: E/AndroidRuntime (776): sur Java.lang.reflect.Method.invoke (Méthode Java: 491) 02-26 11: 57: 50.798: E/AndroidRuntime (776): sur com .Android.internal.os.ZygoteInit $ MethodAndArgsCaller.run (ZygoteInit.Java:844) 02-26 11: 57: 50.798: E/AndroidRuntime (776): sur le site com.Android.internal.os.ZygoteInit.main (ZygoteInit.Java:602) 02-26 11: 57: 50.798: E/AndroidRuntime (776): at dalvik.system.NativeStart.main (Méthode native) 02-26 11: 57: 52.818: I/dalvikvm (776): ID_fichier = 4: réagissant au signal 3 02-26 11: 57: 52.818: I/dalvikvm (776): Écrit les traces de pile dans '/data/anr/traces.txt'
Donc, au lieu de mettre la même instance de fragment, je peux recréer cela afin de résoudre le problème, mais je ne semble pas efficace.
transaction.replace(R.id.layout_tablet_paneau, LigneMessageTabletFragment.newInstance());
peut votre erreur est Android.view.InflateException?
si c'est le cas, vous devez gonfler Fragment de manière dynamique, n'utilisez pas de disposition XML.
et, vous ne devriez pas cibler le fragment défini entre XML Layout to Fragment Transaction.
Reportez-vous à la réponse @lopisan:
J'utilise sa solution depuis longtemps.
MAIS je pense avoir un meilleur moyen de le faire!
Si mChildFragmentManager.mActivity est null, définissez mChildFragmentManager sur null. Lorsque la méthode performActivityCreated.
@Override
void performActivityCreated(Bundle savedInstanceState) {
if (getFragmentManagerActivity(mChildFragmentManager) == null) {
setChildFragmentManager(this, null);
}
super.performActivityCreated(savedInstanceState);
}
public static FragmentActivity getFragmentManagerActivity(FragmentManager fragmentManager) {
FragmentManagerImpl fm = (FragmentManagerImpl) fragmentManager;
return fm.mActivity;
}
private static final Field sChildFragmentManagerField;
static {
/**
* BUG : causing a Java.IllegalStateException error, No Activity, only
* when navigating to Fragment for the SECOND time
* http://stackoverflow.com /questions/15207305/getting-the-error-Java-lang-illegalstateexception-activity-has-been-destroyed
* http://stackoverflow.com/questions/14929907/causing-a-Java-illegalstateexception-error-no-activity-only-when-navigating-to
*/
Field f = null;
try {
f = Fragment.class.getDeclaredField("mChildFragmentManager");
f.setAccessible(true);
} catch (NoSuchFieldException e) {
Log.e(TAG, "Error getting mChildFragmentManager field", e);
}
sChildFragmentManagerField = f;
}
public static void setChildFragmentManager(Fragment fragment, FragmentManager fragmentManager) {
if (sChildFragmentManagerField != null) {
try {
sChildFragmentManagerField.set(fragment, fragmentManager);
} catch (Exception e) {
Log.e(TAG, "Error setting mChildFragmentManager field", e);
}
}
}
Je suis coincé avec le même bogue en utilisant le commutateur BottomNavigationView et le remplacement du fragment provoquant le blocage de l'application. À la vitesse normale, il fonctionnait parfaitement, si l’utilisateur passait rapidement, il se plantait en disant
IllegalStateException Aucune activité
dans les journaux
Avant que j'ai
fragmentA = new FragmentA();
fragmentB = new FragmentB();
fragmentC = new FragmentC();
mBottomNavigationView.setOnNavigationItemSelectedListener(item -> {
switch (item.getItemId()) {
case R.id.fragmentA:
replaceFragment(fragmentA);
case R.id.fragmentB:
repalceFragment(fragmentB);
case R.id.fragmentC:
repalceFragment(fragmentC);
}
};
Je pense que cette pré-création de fragment et son utilisation étaient destinées à remplacer le problème, je l’ai maintenant créé pour le recréer à chaque fois. Cela a commencé à bien fonctionner
mBottomNavigationView.setOnNavigationItemSelectedListener(item -> {
switch (item.getItemId()) {
case R.id.fragmentA:
fragment = new FragmentA();
replaceFragment(fragment);
case R.id.fragmentB:
fragment = new FragmentB();
repalceFragment(fragment);
case R.id.fragmentC:
fragment = new FragmentC();
repalceFragment(fragment);
}
};
public void repalceFragment(Fragment fragment) {
if (!this.isFinishing()) {
if (!fragment.isAdded()) {
if (!this.mDisplayedFragment.isRemoving()) {
getFragmentManager().beginTransaction().replace(R.id.layout_fragment_container, fragment).commit();
this.mDisplayedFragment = fragment;
}
}
}
}
En espérant que cela soit utile pour certains, j'ai trop fait de codage défensif mais cela a résolu mon problème de commutation rapide de fragments