IllegalStateException: Фрагмент уже добавлен - во время запуска приложения в ОС Android 4.3

В моем приложении для Android есть одно действие с контроллером вкладок, на котором размещены 4 вкладки — каждая вкладка представлена ​​​​фрагментом. Когда я запускаю приложение на своем тестовом устройстве под управлением ОС 4.03, приложение работает, и я могу переходить на разные вкладки и т. д. Когда приложение запускается на устройстве с ОС 4.3, приложение аварийно завершает работу во время запуска с IllegalStateException: Fragment уже добавлен : MyFirstTabFragment.

Вот трассировка стека:

Проблема, по-видимому, вызвана попыткой добавить фрагменты в FragmentTransaction в методе getItem() моего расширенного класса FragmentStatePagerAdapter следующим образом:

Причина, по которой я вызываю add(), заключается в том, что я могу связать тег с моими фрагментами, чтобы позже получить доступ к этим фрагментам, чтобы вызвать метод для этих экземпляров фрагментов, когда вкладки выбраны/не выбраны.

public class MyFragmentStatePagerAdapter extends android.support.v13.app.FragmentStatePagerAdapter {        

    @Override
    public Fragment getItem(int position) {
        if (position == 0) {
            Fragment fragment = new MyFirstTabFragment();
            getFragmentManager().beginTransaction().add(fragment, "MyFirstTabFragmentTag").commit();

         // getFragmentManager().beginTransaction().replace(R.id.pager, fragment, "MyFirstTabFragmentTag").commit();

            return fragment;  
        } else if (position == 1) {
            Fragment fragment = new MySecondTabFragment();
            getFragmentManager().beginTransaction().add(fragment, "MySecondTabFragmentTag").commit();

         // getFragmentManager().beginTransaction().replace(R.id.pager, fragment, "MySecondTabFragmentTag").commit();

            return fragment;  
        }
    }
.
.
.

}

Я просмотрел различные сообщения об исключении IllegalStateException и пробовал различные предложения, включая вызов replace() вместо add(), вызов remove(), затем add(), вызов MyFirstTabFragment.instantiate() вместо new MyFirstTabFragment(), и ни один из этих изменения исправили проблему.

Я подозреваю, что я могу выполнять эту FragmentTransaction слишком рано в процессе, поскольку ViewPager в настоящее время находится в процессе добавления фрагментов, когда я пытаюсь добавить или заменить фрагмент своим тегом. Есть ли у кого-нибудь лучшее понимание этого процесса? Есть ли лучшее место, где я могу сделать эти отдельные вызовы add(), чтобы пометить свои фрагменты?

Спасибо заранее за любые предложения!

Так что просто для завершения этой темы...


comment
Спасибо за ответ. Можете ли вы предложить другой способ доступа к моим фрагментам, кроме getFragmentManager().findFragmentByTag()? Мне нужно сделать это из onTabSelected(), а также из некоторых других пользовательских методов.   -  person Márcio Souza Júnior    schedule 16.12.2013
comment
вы можете использовать viewpager2 для решения этой проблемы см. подход   -  person JavaJoe    schedule 16.12.2013
comment
Спасибо за ответ. Согласен, что простой возврат новых экземпляров моих фрагментов в getItem() позволяет ViewPager добавлять их, однако мне нужен механизм для возможности извлечения экземпляра фрагмента, связанного с вкладкой, в какое-то более позднее время (обычно, когда вкладка становится доступной для просмотра или выходит из поля зрения), чтобы я мог вызвать метод класса фрагмента для выполнения некоторого желаемого поведения. Когда я исследовал, как это сделать, предложенный метод извлечения фрагментов состоял в том, чтобы добавить к ним тег, и единственный способ добавить тег — через add() или replace() внутри FragmentTransaction.   -  person javadroid    schedule 18.12.2019


Ответы (4)


Я удалил вызов add() или replace() в методе getItem() моего адаптера и просто создал новые фрагменты, чтобы избежать IllegalStateException, но не смог определить логическое место для перемещения вызова replace(), чтобы я мог пометить фрагменты. К сожалению, использование тега было бы наиболее надежным решением.

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

Я прибегал к поддержке собственной коллекции экземпляров фрагментов, которые я заполнял во время вызова getItem() и обновлял во время вызова адаптера destroyItem(). Я надеюсь, что на этом пути не будет никаких сюрпризов, из-за которых в моей коллекции будут экземпляры, отличные от тех, что хранит ViewPager/адаптер.

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

person JavaJoe    schedule 17.12.2013

Вы могли бы сделать что-то вроде этого:

@Override
public Fragment getItem(int position) {
    if (position == 0) {
        Fragment fragment = new MyFirstTabFragment();

        return fragment;  
    } else if (position == 1) {
        Fragment fragment = new MySecondTabFragment();

        return fragment;  
    }
}
person petey    schedule 16.12.2013
comment
пожалуйста, проверьте stackoverflow.com/q/8785221/794088 (там есть несколько ответов, которые помогут вам в этом). - person JavaJoe; 16.12.2013
comment
Спасибо за ссылку - я прочитал предложения, и в предлагаемых решениях есть дыры/опасности... 1) Поддержание моей собственной коллекции ссылок на фрагменты подвержено ошибкам, поскольку это требует полного понимания поведения ViewPager, чтобы моя коллекция всегда была в порядке. синхронизировать с коллекцией фрагментов ViewPager. 2) Вызов findFragmentByTag() с использованием внутреннего именования фрагментов ViewPager (android:switcher:viewID:position) опасен, поскольку он опирается на недокументированные детали внутренней реализации ViewPager. - person petey; 16.12.2013
comment
Это сводится к попытке найти безопасное и надежное решение для решения проблемы отсутствия метода getFragment() в ViewPager. Чтобы не пытаться реализовать какой-то хак для достижения этой цели, может показаться, что существуют методы add() или replace() для назначения пользовательского тега моим фрагментам для последующего поиска с помощью findFragmentByTag(). Вопрос в том, когда безопасно выполнять эти вызовы во время процесса запуска? Другими словами, где/при вызове я вызываю replace() и убеждаюсь, что фрагменты уже были заполнены в ViewPager, чтобы я мог избежать этого исключения IllegalStateException? - person JavaJoe; 17.12.2013
comment
java.lang.IllegalStateException: невозможно изменить тег полученного фрагмента - person JavaJoe; 17.12.2013

Таким образом, вы можете получить доступ к экземплярам фрагмента адаптера, просто позвонив:

private Fragment[] pages = new Fragment[2];

@Override
public Fragment getItem(int position) {

     if(pages[position] == null){

        switch(position){

        case 0:
           pages[position] = new MyFirstTabFragment();
        break;

        case 1:
            pages[position] = new MySecondTabFragment();
        break:

        default:
        //you have to determine a default position
        break;
    }
  }

  return pages[position];
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    super.destroyItem(container, position, object);
    pages[position] = null;
}

Это не создаст другой экземпляр для информированной позиции.

MyFirstTabFragment firstFragment = (MyFirstTabFragment)adapter.getItem(0);

Это сработало для нас:

person Cícero Moura    schedule 09.10.2017
comment
java.lang.IllegalStateException: Фрагмент уже добавлен: MyFirstTabFragment{41866dc0 #0 id=0x7f080000 MyFirstTabFragment} в android.app.FragmentManagerImpl.addFragment(FragmentManager.java:1128) в android.app.BackStackRecord.run(BackStackRecord.java:616) в android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1435) в android.app.FragmentManagerImpl.executePendingTransactions(FragmentManager.java:474) в android.support.v13.app.FragmentStatePagerAdapter.finishUpdate(FragmentStatePagerAdapter.java:167) в android.support.v4.view.ViewPager.populate(ViewPager.java:1068) в android.support.v4.view.ViewPager.populate(ViewPager.java:914) в android.support.v4.view.ViewPager.onMeasure( ViewPager.java:1436) в android.view.View.measure(View.java:15848) в android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5012) в android.widget.FrameLayout.onMeasure(FrameLayout.java:310) ) на android.view.View .measure(View.java:15848) в android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5012) в com.android.internal.widget.ActionBarOverlayLayout.onMeasure(ActionBarOverlayLayout.java:302) в android.view.View. мера(View.java:15848) в android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5012) в android.widget.FrameLayout.onMeasure(FrameLayout.java:310) в com.android.internal.policy.impl.PhoneWindow $DecorView.onMeasure(PhoneWindow.java:2189) в android.view.View.measure(View.java:15848) в android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:1905) в android.view.ViewRootImpl.measureHierarchy( ViewRootImpl.java:1104) в android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1284) в android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1004) в android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java) :5481) на android.view.Choreographer$CallbackRecord.run(Choreographer. java:749) в android.view.Choreographer.doCallbacks(Choreographer.java:562) в android.view.Choreographer.doFrame(Choreographer.java:532) в android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:735) ) в android.os.Handler.handleCallback(Handler.java:730) в android.os.Handler.dispatchMessage(Handler.java:92) в android.os.Looper.loop(Looper.java:137) в android.app .ActivityThread.main(ActivityThread.java:5103) на java.lang.reflect. Method.invokeNative(собственный метод) на java.lang.reflect.Method.invoke(Method.java:525) на com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737) на com.android. internal.os.ZygoteInit.main(ZygoteInit.java:553) в dalvik.system.NativeStart.main(собственный метод) - person Rushi Ayyappa; 28.11.2017

Не вызывайте add() и попробуйте другой способ доступа к этим фрагментам.

private FragmentManager fm;
private Map<Integer, Fragment> map;

@Override
public Fragment getItem(int position) {
    fm.executePendingTransactions();
    if (map == null)
        map = new HashMap<>();

    if (map.containsKey(position))
        return map.get(position);

    Fragment fragment = new Fragment();
    ...
    map.put(position, fragment);
    return fragment;
}
person doctorram    schedule 15.06.2021