Отримання помилки "Java.lang.IllegalStateException Activity було знищено" під час використання вкладок з ViewPager


110

У мене є програма, яка складається з використання ActionBarSherlock в режимі вкладки. У мене є 5 вкладок, і вміст кожної вкладки обробляється за допомогою фрагментів. Для tab2, однак, у мене є фрагмент, у файлі xml якого міститься елемент ViewPager, який, у свою чергу, містить деякі сторінки фрагментів. Коли я спочатку запускаю програму, я можу переходити між вкладками без проблем, але коли я вдруге натискаю на tab2, я отримую згадану вище помилку. Основний вид діяльності:

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, методом onViewCreate у рядку, що говорить 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.But при переході з tab2 на деякий інша вкладка і знову дивлячись на mActivity, вона мала значення null, що, можливо, пояснює, чому її знищення Activity Activity було знищено.
— Yulric Sequeira

Відповіді:


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 (це помилка в поточній версії бібліотеки підтримки). Це призводить до того, що він не приєднає дочірнього менеджера фрагментів, коли фрагмент повторно закріплений, що спричинить виняток, який ви бачили.
— Marcus Forsell Stahre

14
Ти мусиш жартувати мене. Радий, що ви знайшли повідомлення про це, але добре горе.
— secureboot

8
Ця помилка відслідковується у відстежувачі проблем Android Open Source: code.google.com/p/android/isissue/detail?id=42601
— Крістофер Джонсон

3
Щіпка солі, але це не вдається використовувати версію support-v4 або версію android.app. Тож помилка виникає не лише у бібліотеці підтримки
— gbero

6
Це здається зафіксованим у бібліотеці підтримки версії 24.0.0. Метод 'performDetach ()', 'android.support.v4.app.Fragment', виконайте 'mChildFragmentManager = null;'.
— Емерсон Даллагнол

13

У мене точно така ж проблема. Єдине вирішення, яке я знайшов, - це замінити фрагменти на новий екземпляр, щоразу змінюючи вкладки.

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

Це не справжнє рішення, але я не знайшов способу повторного використання попереднього фрагмента фрагмента ...


1
Дякую велике ... що вирішило проблему. Чи можна дати пояснення, чому це працює? Я ламав голову, намагаючись виправити помилку за допомогою FragmentManager.
— Yulric Sequeira

1
Ви проаналізували свій слід пам’яті? Оскільки цей спосіб вирішення може його збільшити, принаймні, я підозрюю
— nmxprime

не знаю, чому це працює .. не знаю, як це працює, але це працює lol

7

З тією ж проблемою я стикався, коли дзвонив super.onCreate()наприкінці свого методу. Причина: attachActivity()викликається в onCreate () фрагментаActivity. При переопределенні onCreate()та, наприклад, створенні вкладок, менеджер вкладок намагатиметься переключитися на фрагмент, не маючи активності, прикріпленої до FragmentManager.

Просте рішення: Перенесіть заклик до super.onCreate()голови функції функції.

Взагалі, здається, є безліч причин, через які ця проблема може виникнути. Це просто ще один ...

Маттіас


Дуже дякую, ти врятував мені день! У мене була така ж проблема, і я все ще не міг її вирішити через тиждень; (. Я дуже радий, що ти написав цю відповідь !!!
— JonasPTFL

4

Хотів додати, що моя проблема полягала в тій діяльності, де я спробував зробити « FragmentTransactiononCreate», перш ніж я подзвонив super.onCreate(). Я просто перейшов super.onCreate()до вершини функції і працював чудово.


2

У мене виникла помилка, оскільки я використовував LocalBroadcastManager, і я зробив:

unregisterReceiver(intentReloadFragmentReceiver);

замість:

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

1
це не вирішує мою проблему. Але дякую, це допомогло мені виправити мої помилки щодо використання BroadcastReceivers
— nmxprime

Що робити, якщо ваш контекст діяльності недійсний. Тоді "це" не було б дійсним.
— ІгорГанапольський

коли контекст діяльності може бути нульовим? Я думаю, що ніколи.
— Малахіаш

2

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


1

Я отримав ту саму помилку, коли намагався отримати доступ до дитини FragmentManagerдо того, як фрагмент був повністю ініціалізований (тобто приєднаний до Діяльності або, принаймні, onCreateView()викликаний). В іншому випадку FragmentManagerініціалізація над nullактивністю спричиняє вищезгаданий виняток.


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 ()", але я використовую теми, і я хотів переконатися, що всі потоки будуть знищені перед початком наступної діяльності , і схоже, що "закінчення ()" не завжди є негайним. Мені потрібно обробити / зменшити багато «растрових зображень» та відобразити великі «растрові карти», і я працюю над покращенням використання пам’яті у своєму додатку

Тепер я буду вбивати нитки, використовуючи інший метод, і виконую цей метод з "onDestroy ();" і коли я думаю, що мені потрібно вбити всі нитки.

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

0

Я мав це питання і зрозумів , що це було , тому що я дзвонив setContentView(int id)двічі в моєму ActivityхonCreate


0

Цей звів мене з розуму за Ксамарін.

Я зіткнувся з цим із реалізацією 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.