Получение ошибки «Действие Java.lang.IllegalStateException было уничтожено» при использовании вкладок с ViewPager


110

У меня есть приложение, которое состоит из использования ActionBarSherlock в режиме вкладок. У меня есть 5 вкладок, и содержимое каждой вкладки обрабатывается с помощью фрагментов. Однако для tab2 у меня есть фрагмент, XML-файл которого содержит элемент ViewPager, который, в свою очередь, имеет несколько страниц фрагментов. Когда я сначала запускаю приложение, я могу без проблем переключаться между вкладками, но когда я нажимаю вкладку 2 во второй раз, я получаю указанную выше ошибку. Основная деятельность заключается в следующем:

public class MainActivity extends SherlockFragmentActivity
{
    @Override
    protected void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ActionBar actionBar = getSupportActionBar();

        ActionBar.Tab tab1 = actionBar.newTab().setText("Tab1");
        ActionBar.Tab tab3 = actionBar.newTab().setText("Tab3");
        ActionBar.Tab tab2 = actionBar.newTab().setText("Tab2");
        ActionBar.Tab tab4 = actionBar.newTab().setText("Tab4");
        ActionBar.Tab tab5 = actionBar.newTab().setText("Tab5");

        Fragment fragment1 = new Tab1();
        Fragment fragment3 = new Tab3();
        Fragment fragment2 = new Tab2();
        Fragment fragment5 = new Tab5();
        Fragment fragment4 = new Tab4();

        tab1.setTabListener(new MyTabListener(fragment1));
        tab3.setTabListener(new MyTabListener(fragment3));
        tab2.setTabListener(new MyTabListener(fragment2));
        tab5.setTabListener(new MyTabListener(fragment5));
        tab4.setTabListener(new MyTabListener(fragment4));

        actionBar.addTab(tab1);
        actionBar.addTab(tab2);
        actionBar.addTab(tab3);
        actionBar.addTab(tab4);
        actionBar.addTab(tab5); 

        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
    }

    class MyTabListener implements ActionBar.TabListener
    {
        Fragment fragment;

        public MyTabListener(Fragment fragment)
        {
            this.fragment = fragment;
        }

        @Override
        public void onTabSelected(com.actionbarsherlock.app.ActionBar.Tab tab,FragmentTransaction ft) 
        {
            ft.replace(R.id.fragment_container,fragment);
        }

        @Override
        public void onTabUnselected(com.actionbarsherlock.app.ActionBar.Tab tab,FragmentTransaction ft) 
        {

        }

        @Override
        public void onTabReselected(com.actionbarsherlock.app.ActionBar.Tab tab,FragmentTransaction ft) 
        {

        }
    }
}

Класс фрагмента без ViewPager выглядит следующим образом:

public class Tab1 extends Fragment 
{
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState)
    {
        return inflater.inflate(R.layout.activity_tab1, container, false);
    }
}

Класс фрагмента с ViewPager выглядит следующим образом:

public class Tab2 extends Fragment 
{
    ViewPager mViewPager;
    private MyFragmentPagerAdapter mMyFragmentPagerAdapter;  
    private static int NUMBER_OF_PAGES = 5;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState)
    {
        View view =  inflater.inflate(R.layout.activity_tab2, container, false); 
        return view;
    }

    @Override
    public void onViewCreated(View view,Bundle savedInstanceState)
    {
        super.onViewCreated(view, savedInstanceState);
        mViewPager = (ViewPager) view.findViewById(R.id.viewpager);
        mMyFragmentPagerAdapter = new MyFragmentPagerAdapter(getChildFragmentManager());  
        mViewPager.setAdapter(mMyFragmentPagerAdapter);  
    }

    private static class MyFragmentPagerAdapter extends FragmentPagerAdapter 
    {    
        public MyFragmentPagerAdapter(FragmentManager fm) 
        {  
             super(fm);  
        }  

        @Override  
        public Fragment getItem(int index) 
        {  
             return PageFragment.newInstance("My Message " + index);
        }  

        @Override  
        public int getCount() 
        {  
             return NUMBER_OF_PAGES;  
        }  
   }
}

Из того, что я читал в разных местах (и, пожалуйста, поправьте меня, если я ошибаюсь), это происходит потому, что диспетчер фрагментов на втором проходе пытается повторно использовать фрагменты из активности, которой больше не существует, что приводит к ошибке. Но я не уверен, почему это происходит здесь, поскольку я не использую активность фрагментов. Согласно logcat, ошибка находится в классе Tab2, метод onViewCreated в строке, которая говорит mViewPager.setAdapter (mMyFragmentPagerAdapter). Любая помощь приветствуется ... Спасибо.

03-04 12:01:05.468: E/AndroidRuntime(2474): FATAL EXCEPTION: main
03-04 12:01:05.468: E/AndroidRuntime(2474): java.lang.IllegalStateException: Activity has been destroyed
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1342)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.BackStackRecord.commitAllowingStateLoss(BackStackRecord.java:578)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.FragmentPagerAdapter.finishUpdate(FragmentPagerAdapter.java:139)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.view.ViewPager.populate(ViewPager.java:1011)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.view.ViewPager.populate(ViewPager.java:880)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.view.ViewPager.setAdapter(ViewPager.java:433)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at com.example.tabs.Tab2.onViewCreated(Tab2.java:31)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:925)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1088)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:682)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1444)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:429)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.os.Handler.handleCallback(Handler.java:587)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.os.Handler.dispatchMessage(Handler.java:92)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.os.Looper.loop(Looper.java:123)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at android.app.ActivityThread.main(ActivityThread.java:3687)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at java.lang.reflect.Method.invokeNative(Native Method)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at java.lang.reflect.Method.invoke(Method.java:507)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:842)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)
03-04 12:01:05.468: E/AndroidRuntime(2474):     at dalvik.system.NativeStart.main(Native Method)

Так что я думаю, что нашел проблему. Посмотрев на переменную mMyFragmentPagerAdapter (класс Tab2) через отладчик eclipse, я увидел, что у него была переменная FragmentManager, которая при первом нажатии на Tab2 имела поле с именем mActivity, которое указывало на MainActivity. Но при переключении с tab2 на некоторые другая вкладка и снова взглянув на mActivity, она имела значение null, что, возможно, объясняет, почему она выдавала ошибку Activity была уничтожена.
— Юлрик Секейра

Ответы:


284

Похоже, это ошибка недавно добавленной поддержки вложенных фрагментов. В основном, когда ребенок FragmentManagerотрывается от деятельности, его внутреннее состояние нарушается. Кратковременный обходной путь, который исправил это для меня, - добавить следующее к onDetach()каждому, к Fragmentчему вы обращаетесь getChildFragmentManager():

@Override
public void onDetach() {
    super.onDetach();

    try {
        Field childFragmentManager = Fragment.class.getDeclaredField("mChildFragmentManager");
        childFragmentManager.setAccessible(true);
        childFragmentManager.set(this, null);

    } catch (NoSuchFieldException e) {
        throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
        throw new RuntimeException(e);
    }
}

21
Если вы посмотрите на реализацию фрагмента, вы увидите, что при переходе в отключенное состояние он сбрасывает свое внутреннее состояние. Однако он не сбрасывает mChildFragmentManager (это ошибка в текущей версии библиотеки поддержки). Это приводит к тому, что он не подключает повторно диспетчер дочерних фрагментов при повторном подключении фрагмента, что вызывает исключение, которое вы видели.
— Маркус Форселл Старе

14
Ты, должно быть, шутишь. Рад, что вы нашли это опубликованное, но доброе дело.
— secureboot 05

8
Эта ошибка отслеживается в системе отслеживания проблем с открытым исходным кодом Android: code.google.com/p/android/issues/detail?id=42601
— Кристофер Джонсон,

3
Немного скептически, но это не удается при использовании версии support-v4 или версии android.app. Так что ошибка возникает не только в библиотеке поддержки
— gbero

6
Кажется, исправлено в версии библиотеки поддержки 24.0.0. Метод performDetach () из android.support.v4.app.Fragment выполняет mChildFragmentManager = null;
— Emerson Dallagnol,

13

У меня точно такая же проблема. Единственный обходной путь, который я нашел, - это заменять фрагменты новым экземпляром каждый раз, когда вкладки меняются.

ft.replace(R.id.fragment_container, Fragment.instantiate(PlayerMainActivity.this, fragment.getClass().getName()));

Не настоящее решение, но я не нашел способа повторно использовать предыдущий экземпляр фрагмента ...


1
Вау, большое спасибо ... это устранило проблему. Можно ли объяснить, почему это работает? Я ломал себе голову, пытаясь исправить ошибку с помощью FragmentManager.
— Юлрик Секейра

1
Вы проанализировали свой объем памяти? Поскольку этот обходной путь может увеличить его, по крайней мере, я подозреваю
— nmxprime

не знаю, почему это работает .. не знаю, как это работает, но это работает лол

7

Я столкнулся с той же проблемой при вызове super.onCreate()в конце моего метода. Причина: attachActivity()вызывается onCreate () FragmentActivity. При переопределении onCreate()и, например, создании вкладок, диспетчер вкладок попытается переключиться на фрагмент, не привязав активность к FragmentManager.

Простое решение: переместите вызов super.onCreate()в заголовок тела функции.

В общем, похоже, что эта проблема может возникнуть по множеству причин. Это просто еще один ...

Матиас


Большое спасибо, вы спасли мне день! У меня была такая же проблема, и я все еще не мог ее решить через неделю; (. Я очень рад, что вы написали этот ответ !!!
— JonasPTFL

4

Хотел добавить, что моя проблема была FragmentTransactionв действии, где я пытался создать onCreate ДО того, как позвонил super.onCreate(). Я просто перешел super.onCreate()в верхнюю часть функции и работал нормально.


2

У меня была эта ошибка, потому что я использовал LocalBroadcastManager, и я сделал:

unregisterReceiver(intentReloadFragmentReceiver);

вместо того:

LocalBroadcastManager.getInstance(this).unregisterReceiver(intentReloadFragmentReceiver);

1
это не решает мою проблему. Но спасибо, это помогло мне исправить мои ошибки при использовании BroadcastReceivers
— nmxprime

Что делать, если ваш контекст Activity равен нулю. Тогда «это» не будет действительным.
— Игорь Ганапольский

когда контекст действий может быть нулевым? Думаю, никогда.
— Malachiasz

2

Я столкнулся с теми же проблемами и Lateron узнал, что я пропустил звонок super.onCreate( savedInstanceState );в onCreate()из FragmentActivity.


1

Я получил ту же ошибку при попытке доступа к дочернему элементу FragmentManagerдо того, как фрагмент был полностью инициализирован (то есть прикреплен к Activity или, по крайней мере, onCreateView()вызван). В противном случае FragmentManagerинициализируется nullActivity, вызывающее вышеупомянутое исключение.


1

Я принудительно устанавливаю для фрагмента, содержащего дочерний фрагмент, значение NULL в onPause, и это решает мою проблему.

fragment = null;

1

Я знаю, что это старый пост, но предложенные ответы с моей стороны не сработали. Я хочу оставить это здесь на всякий случай, если кому-то это пригодится.

Что я сделал:

@Override
public void onResume() {
    super.onResume();
    // add all fragments
    FragmentTransaction fragmentTransaction = getChildFragmentManager().beginTransaction();
    for(Fragment fragment : fragmentPages){
        String tag = fragment.getClass().getSimpleName();
        fragmentTransaction.add(R.id.contentPanel, fragment, tag);
        if(fragmentPages.indexOf(fragment) != currentPosition){
            fragmentTransaction.hide(fragment);
        } else {
            lastTag = tag;
        }
    }
    fragmentTransaction.commit();
}

Затем в:

@Override
public void onPause() {
    super.onPause();
    // remove all attached fragments
    for(Fragment fragment: fragmentPages){
        getChildFragmentManager().beginTransaction().remove(fragment).commit();
    }
}

0

У меня была эта проблема, и я не смог найти здесь решение, поэтому я хочу поделиться своим решением на случай, если у кого-то снова возникнет эта проблема.

У меня был такой код:

public void finishAction() {
  onDestroy();
  finish();
}

и решил проблему, удалив строку "onDestroy ();"

public void finishAction() {
  finish();
}

Причина, по которой я написал исходный код: я знаю, что когда вы выполняете "finish ()", действие вызывает "onDestroy ()", но я использую потоки, и я хотел убедиться, что все потоки будут уничтожены перед запуском следующего действия. , и похоже, что "finish ()" не всегда выполняется немедленно. Мне нужно обработать / уменьшить количество «растровых изображений» и отобразить большие «растровые изображения», и я работаю над улучшением использования памяти в моем приложении.

Теперь я убью потоки, используя другой метод, и я выполню этот метод из "onDestroy ();" и когда я думаю, мне нужно убить все потоки.

public void finishAction() {
  onDestroyThreads();
  finish();
}

0

Я имел этот вопрос и понял , что это было , потому что я звонил setContentView(int id)дважды в моем Activity-хonCreate


0

Этот сводил меня с ума по Xamarin.

Я столкнулся с этим с реализацией ViewPager для TabLayout ВНУТРИ фрагмента, который сам реализован в DrawerLayout:

 - DrawerLayout
   - DrawerFragment
     - TabLayout
     - TabViewPager
       - TabViewPagerFragments

Итак, вам нужно реализовать следующий код в своем DrawerFragment . Не забывайте выбирать правильный FragmentManager-Path. Потому что у вас может быть две разные ссылки FragmentManager:

  1. Android.Support.V4.App.FragmentManager
  2. Android.App.FragmentManager

-> Выберите тот, который вы используете. Если вы хотите использовать ChildFragmentManager, вам нужно было использовать объявление класса Android.App.FragmentManager для вашего ViewPager!

Android.Support.V4.App.FragmentManager

Реализуйте следующий метод в своем « основном » фрагменте - в этом примере: DrawerFragment

public override void OnDetach() {
    base.OnDetach();
    try {
        Fragment x = this;
        var classRefProp = typeof(Fragment).GetProperty("class_ref", BindingFlags.NonPublic | BindingFlags.Static);
        IntPtr classRef = (IntPtr)classRefProp.GetValue(x);
        var field = JNIEnv.GetFieldID(classRef, "mChildFragmentManager", "Landroid/support/v4/app/FragmentManagerImpl;");
        JNIEnv.SetField(base.Handle, field, IntPtr.Zero);
    }
    catch (Exception e) {
        Log.Debug("Error", e+"");
    }
}

Android.App.FragmentManager

public class TabViewPager: Android.Support.V13.App.FragmentPagerAdapter {}

Это означает, что вам нужно было запустить ViewPager с Android.App.FragmentManager.

Реализуйте следующий метод в своем « основном » фрагменте - в этом примере: DrawerFragment

public override void OnDetach() {
    base.OnDetach();
    try {
        Fragment x = this;
        var classRefProp = typeof(Fragment).GetProperty("class_ref", BindingFlags.NonPublic | BindingFlags.Static);
        IntPtr classRef = (IntPtr)classRefProp.GetValue(x);
        var field = JNIEnv.GetFieldID(classRef, "mChildFragmentManager", "Landroid/app/FragmentManagerImpl;");
        JNIEnv.SetField(base.Handle, field, IntPtr.Zero);
    }
    catch (Exception e) {
        Log.Debug("Error", e+"");
    }
}

0

Ошибка исправлена ​​в последней версии androidx. И знаменитый обходной путь теперь вызовет сбой. так что нам это не нужно сейчас.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.