Вызывая ошибку java.IllegalStateException, No Activity, только при навигации по фрагменту для SECOND time
Я получаю очень загадочную ошибку, о которой я понятия не имею, как начать работу.
У меня есть простое приложение с одним действием, представления реализованы с помощью фрагментов. Один из фрагментов имеет ViewPager внутри него; поэтому я решил, что хочу использовать класс getChildFragmentManager библиотеки поддержки v4. Мне также пришлось использовать ActionBarSherlock, что вызвало проблему, потому что оно не поставляется с v11 библиотеки v4.
Я исправил это, заменив библиотеку поддержки v4 в АБС библиотекой v11, и все было скомпилировано и, казалось, работало, включая ViewPager.
Вот странная часть:
При первом открытии фрагмента с ViewPager он работает правильно; но во второе время, к которому он подключен, приложение выходит из строя, давая бесполезную трассировку стека. От отладки я обнаружил, что проблема связана с FragmentManager, возвращаемым getChildFragmentManager; он выдает ошибку "Нет активности".
Кто-нибудь знает, что может быть причиной этого?
Я отправлю код, который, по вашему мнению, имеет значение.
Спасибо,
Дэвид
Ответы
Ответ 1
Я следил за ссылкой в jeremyvillalobos answer (что было очень полезно), что привело меня к это обходное решение.
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);
}
}
}
...
}
Он работает для меня хорошо, без необходимости повторного использования фрагмента.
Ответ 2
Это, по-видимому, ошибка, указанная в
https://code.google.com/p/android/issues/detail?id=42601
Переменная
FragmentManagerImpl mChildFragmentManager;
В Fragment.java не установлено значение null при отсоединении. Таким образом, в следующий раз, когда фрагмент загружен, переменная по-прежнему указывает на последнего родителя.
Как обсуждалось в этом потоке, обходной путь заключается в том, чтобы восстановить Фрагмент.
В моем случае я переключался между фрагментами на вкладке ActionBar. Проблемный фрагмент имеет вложенные фрагменты и сбой приложения, возвращаясь к файловому загрузчику Fragment. Итак, это код обхода:
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);
}
}
Ответ 3
может возникнуть ваша ошибка android.view.InflateException?
если это так, вы должны динамически раздувать фрагмент, не используйте XML-макет.
и вы не должны использовать целевой фрагмент, который определяется XML-макет для транзакции фрагментации.
Ответ 4
У меня та же проблема.
В действии у меня есть 3 кнопки для переключения фрагмента с помощью transaction.replace(...)
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.layout_tablet_paneau, mLigneMessageFragment);
Один из этих фрагментов содержит ViewPage с пользовательским FragmentPagerAdapter. Поэтому я должен сделать getChildFragmentManager(), чтобы охарактеризовать вложенные фрагменты.
здесь находится конструктор:
public LignePagerAdapter(Fragment ligneMessageTabletFragment) {
super(ligneMessageTabletFragment.getChildFragmentManager());
}
Итак, у меня такая же ошибка: первая демонстрация этого фрагмента нарушает, но когда я показываю другой фрагмент и возвращаюсь к этому, я получаю это исключение:
02-26 11:57:50.798: D/ACRA(776): Wait for Toast + worker ended. Kill Application ? true
02-26 11:57:50.798: E/AndroidRuntime(776): FATAL EXCEPTION: main
02-26 11:57:50.798: E/AndroidRuntime(776): java.lang.IllegalStateException: No activity
02-26 11:57:50.798: E/AndroidRuntime(776): at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1075)
02-26 11:57:50.798: E/AndroidRuntime(776): at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1070)
02-26 11:57:50.798: E/AndroidRuntime(776): at android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManager.java:1861)
02-26 11:57:50.798: E/AndroidRuntime(776): at android.support.v4.app.Fragment.performActivityCreated(Fragment.java:1474)
02-26 11:57:50.798: E/AndroidRuntime(776): at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:931)
02-26 11:57:50.798: E/AndroidRuntime(776): at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1088)
02-26 11:57:50.798: E/AndroidRuntime(776): at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:682)
02-26 11:57:50.798: E/AndroidRuntime(776): at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1444)
02-26 11:57:50.798: E/AndroidRuntime(776): at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:429)
02-26 11:57:50.798: E/AndroidRuntime(776): at android.os.Handler.handleCallback(Handler.java:587)
02-26 11:57:50.798: E/AndroidRuntime(776): at android.os.Handler.dispatchMessage(Handler.java:92)
02-26 11:57:50.798: E/AndroidRuntime(776): at android.os.Looper.loop(Looper.java:132)
02-26 11:57:50.798: E/AndroidRuntime(776): at android.app.ActivityThread.main(ActivityThread.java:4126)
02-26 11:57:50.798: E/AndroidRuntime(776): at java.lang.reflect.Method.invokeNative(Native Method)
02-26 11:57:50.798: E/AndroidRuntime(776): at java.lang.reflect.Method.invoke(Method.java:491)
02-26 11:57:50.798: E/AndroidRuntime(776): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:844)
02-26 11:57:50.798: E/AndroidRuntime(776): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
02-26 11:57:50.798: E/AndroidRuntime(776): at dalvik.system.NativeStart.main(Native Method)
02-26 11:57:52.818: I/dalvikvm(776): threadid=4: reacting to signal 3
02-26 11:57:52.818: I/dalvikvm(776): Wrote stack traces to '/data/anr/traces.txt'
Итак, вместо того, чтобы помещать один и тот же экземпляр фрагмента, я могу воссоздать его, чтобы он исправить проблему, но я выгляжу неэффективно.
transaction.replace(R.id.layout_tablet_paneau, LigneMessageTabletFragment.newInstance());
Ответ 5
К сожалению, это ошибка поддержки v4, все еще там: (
Когда вы выбираете другой фрагмент через навигационный ящик или другую вещь, подобную ему, фрагмент, который имеет суб-фрагменты, отделяется. Таким образом, фрагментарный фрагмент фрагментов (getChildFragmentManager()) больше не существует. в то время как эти фрагменты возвращаются, произошла ошибка. Бомба!
Очевидно, что поддержка v4 должна очищать mChildFragmentManager в onDetach(), но это не так, поэтому мы должны зависеть от нас самих. таких как следующие коды в фрагменте, который имеет суб-фрагменты:
@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();
}
Все будет хорошо, у вас будет хороший день:)
Ответ 6
Обратитесь к комментарию @lopisan:
Я использую его решение в течение длительного времени.
НО У меня есть лучший способ сделать это!
Если mChildFragmentManager.mActivity имеет значение NULL, установите для параметра mChildFragmentManager значение null. Когда метод 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);
}
}
}
Ответ 7
Я застрял с той же ошибкой, используя переключатель BottomNavigationView и замену фрагмента, вызывающего крах приложения. С нормальной скоростью он работал отлично, если пользователь быстро переключил его, чтобы сбой сказать
IllegalStateException Нет действий
в журналах
Прежде чем у меня
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);
}
};
Я думаю, что это предварительное создание фрагмента и использование их для замены - это создание проблемы, теперь я сделал это для повторного создания каждый раз. Он начал нормально работать
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;
}
}
}
}
Надеюсь, что это полезно для некоторых, я сделал слишком много защитного кодирования, но решил мою проблему быстрого переключения фрагментов
Ответ 8
Пожалуйста, проверьте номер 32 этой ссылки:
Child FragmentManager, сохраненный после отсоединения родительского фрагмента
Шаги: