Android fragment already added

java.lang.IllegalStateException: Fragment already added

I have a Problem with a Android App compiled and running with target SDK 4.3. The App has two Activities, a MainActivity which is also the Launcher Activity and a SecondActivity. Both are using Fragments. To Support older Devices also, the support lib is used.

In the following scenario it comes to the «IllegalStateException: Fragment already added» Error.

1) Start the app -> MainActivity is shown
2) switch to the SecondActivity with an Intent
3) Press the Home Button
4) Wait a longer time (tested with 24 hours)
5) press again the app icon -> Exception. If the time is shorter, the SecondActivity is shown like expected.

I have read a lot of the IllegalStateExceptions during Fragment handling, but all all of them pointed to a problem with a replace() method. In the Stacktrace, my own code is never called.

The Fragement is added in the Activies onCreate() Method:

Answers

Okay, solved in on my own.

I put all Fragments out in onPause() and store the state in some booleans. Depending on that booleans the Fragments are put in in onResume(). Than the launch is stable regardless how long the Activity was in the background.

Maybe that helps someone with the same problem

This happens when we try to add same fragment or DialogFragment twice before dismissing,

Having said that, I don’t see any reason why to remove old fragment and add the same fragment again since we can update the UI/data by simply passing parameters to the method inside the fragment

FragmentTransactions are committed asynchronously. Therefore, you need to call

before you call

That way, you can make sure that everything is up to date.

I’m not sure what the specific problem is, but maybe this will help.

From the Android documentation on Fragment s:

You should design each fragment as a modular and reusable activity component. That is, because each fragment defines its own layout and its own behavior with its own lifecycle callbacks, you can include one fragment in multiple activities, so you should design for reuse and avoid directly manipulating one fragment from another fragment.

That is, you should never manipulate a fragment from another fragment; rather, this should be done through the underlying Activity. Read the «Creating event callbacks to the activity» section in this article for more information (it’s important stuff!!).

On the other hand, if you want the button to perform an action within the Fragment itself (i.e. if you wanted a Button click to change the text of a TextView within the Fragment), you should implement this in the Fragment , not the Activity (this is because the resulting behavior is contained within the Fragment and has nothing to do with the parent Activity ).

Leave a comment and I can clarify if my post is confusing. I only recently began to understand Fragment ‘s myself :).

Читайте также:  Android get process name

This is a really good question!

I think one can safely rule out the possibility of the Service probing for the current configuration to decide what activity to target with the PendingIntent.

I’d agree that it would be preferable to keep UI decisions at the UI layer, but having the service make the decision would certainly be an expedient choice. You might use a static method on a UI layer class to keep the decision code technically outside of the service (e.g., a static createArticlePendingIntent() method on NewsReaderActivity that the service uses to build its Notification ).

So, how does one decide what activity to launch from a notification, if the activity to launch depends on screen configuration?

Use a getActivity() PendingIntent for NewsReaderActivity in your Notification , with enough extras that NewsReaderActivity knows that it is in this «show the article» scenario. Before it calls setContentView() , have it determine if ArticleActivity is the right answer. If so, NewsReaderActivity calls startActivity() to launch ArticleActivity , then calls finish() to get rid of itself (or not, if you want BACK from the article to go to NewsReaderActivity ).

Or, use a getActivity() PendingIntent for ICanHazArticleActivity in your Notification . ICanHazArticleActivity has Theme.NoDisplay , so it will not have a UI. It makes the decision of whether to launch NewsReaderActivity or ArticleActivity , calls startActivity() on the right answer, and then calls finish() . The advantage over the previous solution is that there is no brief flash of NewsReaderActivity if the end destination is ArticleActivity .

Or, use the createArticlePendingIntent() option I mentioned in the first paragraph of my answer.

There may be other options as well, but those are what come to mind.

Источник

MichaЕ‚ ЕЃuszczuk

Personal thoughts about code, Android and more

Android Fragments restoration mechanism

In my last post, where I described a problem of incorrect usage of Fragments instantiation inside FragmentPageAdapter & ViewPager , I wrote:

After orientation change all fragments currently added to FragmentManager are automatically restored and instantiated so there is no need to create them once again.

Today, I want to focus more specifically on an issue how this automatic restoration works under the hood.

Beginning

The simplest way to use a fragment:

A custom fragment instance is created and added to FragmentManager with help of FragmentTransaction .
We could add that our Fragment will be added to container identified by android.R.id.content .

This is the easiest case. I want to point out that we are not using setRetainInstance(true) inside our custom fragment implementation, and our activity is not protected in manifest agains any type of configuration changes.

The Question

Now, what will happen with our fragment if suddenly our device configuration will change, i.e. orientation?

Android Source Code (especially FragmentActivity and FragmentManager / FragmentManagerImpl ) is a place where we should look for the answer.

1. Saving the state

Before Activity will be destroyed its onSaveInstanceState method will be called. Take a look what this method is doing internally.

It takes mFragments (reference to FragmentManager held in this Activity) and calls FragmentManager.saveAllState() method which will return a parcelable object ready to be saved inside the bundle which, you can already guess… later will be used to restore Fragments.

Читайте также:  Car driving multiplayer android

In reality result of saveAllState method call is an object of type FragmentManagerState which consists of information about all active fragments and back stack entries

Last quick look at FragmentState .

It consists of all data which describes a specific fragment object instance, like container id, tag, arguments but also savedFragmentState.

It looks sufficient to create fragments from scratch, and set them like they were before.

2. Destroying fragments and activities.

After state of fragments is saved via FragmentManager activity object is destroyed (removed from memory) with all its fragments (those which are not retained with setRetainInstance(true) ).

3. Creating activity and recreating fragments

Final point is a recreation of the activity and recreation of the fragments. It starts within the first Activity lifecycle callback method onCreate(Bundle savedInstanceState)

The previously saved fragment manager state bundle now is used inside FragmentManager.restoreAllState method. This metod declaration is quite long but I want to focus on the most important part.

Array with all FragmentState objects is iterated. And every FragmentState object is used to recreate (create new instance with state like before) specific Fragments.

A new instance of a fragment is created by platform with usage of reflection and default constructor – that’s why you must remember to always ensure the existence of public non-argument fragment constructor and initialize your fragment through arguments bundle (not with usage of fragment object setters from strange places).

This is short story how restoration of Fragments works.

Conclusion

Points to remember:

  • Already created fragments are restored automatically after orientation change
  • Magic behind this is just code written in Activity together with FragmentManager logic/implementation
  • Avoid setter methods and parametrized constructors to modify fragment state, because platform uses only default (0 parameter) constructor, arguments bundle and saved state bundle to restore it later
  • Fragments are not so bad

Источник

Полный список

— динамически работаем с фрагментами

Размещать статические фрагменты мы уже умеем. Но, ясно дело, что гораздо интереснее работать с ними динамически. Система позволяет нам добавлять, удалять и заменять фрагменты друг другом. При этом мы можем сохранять все эти манипуляции в BackStack и кнопкой Назад отменять. В общем, все удобно и красиво.

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

— добавлять первый фрагмент
— удалять первый фрагмент
— заменять первый фрагмент вторым фрагментом
— переключать режим сохранения в BackStack операций с фрагментами

Project name: P1051_FragmentDynamic
Build Target: Android 4.1
Application name: FragmentDynamic
Package name: ru.startandroid.develop.p1051fragmentdynamic
Create Activity: MainActivity

В strings.xml добавим строки:

Создаем фрагменты. Как мы помним из прошлого урока, для этого нам нужны будут layout-файлы и классы, наследующие android.app.Fragment

Все почти аналогично прошлому уроку, только убрали вызовы кучи lifecycle методов с логами.

Рисуем основное Activity.

Три кнопки для добавления, удаления и замены фрагментов. Чекбокс для включения использования BackStack. И FrameLayout – это контейнер, в котором будет происходить вся работа с фрагментами. Он должен быть типа ViewGroup. А элементы Fragment, которые мы использовали на прошлом уроке для размещения фрагментов, нам не нужны для динамической работы.

Читайте также:  Пазл квест для андроида

В onCreate создаем пару фрагментов и находим чекбокс.

В onClick мы получаем менеджер фрагментов с помощью метода getFragmentManager. Этот объект является основным для работы с фрагментами. Далее, чтобы добавить/удалить/заменить фрагмент, нам необходимо использовать транзакции. Они аналогичны транзакциям в БД, где мы открываем транзакцию, производим операции с БД, выполняем commit. Здесь мы открываем транзакцию, производим операции с фрагментами (добавляем, удаляем, заменяем), выполняем commit.

Итак, мы получили FragmentManager и открыли транзакцию методом beginTransaction. Далее определяем, какая кнопка была нажата:

если Add, то вызываем метод add, в который передаем id контейнера (тот самый FrameLayout из main.xml) и объект фрагмента. В итоге, в контейнер будет помещен Fragment1

если Remove, то вызываем метод remove, в который передаем объект фрагмента, который хотим убрать. В итоге, фрагмент удалится с экрана.

если Replace, то вызываем метод replace, в который передаем id контейнера и объект фрагмента. В итоге, из контейнера удалится его текущий фрагмент (если он там есть) и добавится фрагмент, указанный нами.

Далее проверяем чекбокс. Если он включен, то добавляем транзакцию в BackStack. Для этого используем метод addToBackStack. На вход можно подать строку-тэг. Я передаю null.

Ну и вызываем commit, транзакция завершена.

Давайте смотреть, что получилось. Все сохраняем, запускаем приложение.

появился первый фрагмент.

Еще раз добавим первый фрагмент – жмем Add. И жмем Replace

первый фрагмент заменился вторым.

Жмем кнопку Назад. Приложение закрылось, т.к. все эти операции с фрагментами не сохранялись в BackStack. Давайте используем эту возможность.

Снова запускаем приложение и включаем чекбокс add to Back Stack

Выполняем те же операции: Add, Remove, Add, Replace. У нас добавится первый фрагмент, удалится первый фрагмент, добавится первый фрагмент, заменится вторым. В итоге мы снова видим второй фрагмент. Теперь жмем несколько раз кнопку Назад и наблюдаем, как выполняются операции, обратные тем, что мы делали. Когда транзакции, сохраненные в стеке закончатся, кнопка Назад закроет приложение.

Т.е. все достаточно просто и понятно. Скажу еще про пару интересных моментов.

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

Когда мы удаляем фрагмент и не добавляем транзакцию в BackStack, то фрагмент уничтожается. Если же транзакция добавляется в BackStack, то, при удалении, фрагмент не уничтожается (onDestroy не вызывается), а останавливается (onStop).

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

На следующем уроке:

— рассмотрим взаимодействие между Activity и ее фрагментами

Присоединяйтесь к нам в Telegram:

— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.

— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование

— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня

— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме

Источник

Оцените статью