Android: дублирование фрагментов при использовании Fragment Manager
У меня очень странная проблема с фрагментами. Я использую новейшую библиотеку поддержки. Я также использую аналогичный код, который используется Google в проекте IOSCHED
Но у меня проблема с воссозданием активности после ротации. После того, как действие будет уничтожено и создано снова, я вызываю методы управления транзакцией фрагмента (в методе onStart). Эта строка вызывается только один раз, но она создает фрагмент TWICE!
Это мой метод активности:
public abstract class SinglePaneActivity extends FragmentActivity
{
@Override
protected void onStart()
{
super.onStart();
if(mFragment == null)
{
mFragment = onCreatePane();
mFragment.setArguments(Utils.intentToFragmentArguments(getIntent()));
Log.w(TAG, "Fragment creation counter = " + createCounter);
createCounter++;
getSupportFragmentManager()
.beginTransaction()
.add(R.id.root_container,mFragment)
.commit();
}
}
@Override
protected void onStop()
{
Log.i(TAG, "onStop");
if(mFragment != null)
{
getSupportFragmentManager()
.beginTransaction()
.remove(mFragment)
.commit();
mFragment = null;
}
super.onStop();
}
}
и мои журналы:
--Start of application--
11-18 13:26:37.050: I/SinglePaneActivity(19040): onCreate
11-18 13:26:37.050: I/SinglePaneActivity(19040): onStart
11-18 13:26:37.055: W/SinglePaneActivity(19040): replacing fragment, counter = 1
11-18 13:26:37.075: I/MyFragment(19040): onCreate
11-18 13:26:37.110: I/MyFragment(19040): onActivityCreated
--Rotating the device--
11-18 13:26:39.600: I/SinglePaneActivity(19040): onStop
11-18 13:26:39.600: I/SinglePaneActivity(19040): onDestroy
11-18 13:26:39.605: I/MyFragment(19040): onDestroy
11-18 13:26:39.755: I/MyFragment(19040): onCreate
11-18 13:26:39.755: I/SinglePaneActivity(19040): onCreate
11-18 13:26:39.790: I/MyFragment(19040): onActivityCreated
11-18 13:26:39.800: I/SinglePaneActivity(19040): onStart
11-18 13:26:39.800: W/SinglePaneActivity(19040): replacing fragment, counter = 2
11-18 13:26:39.810: I/MyFragment(19040): onCreate
11-18 13:26:39.815: I/MyFragment(19040): onActivityCreated
--Rotating the device back--
11-18 13:36:47.060: I/SinglePaneActivity(19040): onStop
11-18 13:36:47.060: I/SinglePaneActivity(19040): onDestroy
11-18 13:36:47.060: I/MyFragment(19040): onDestroy
11-18 13:36:47.065: I/MyFragment(19040): onDestroy
11-18 13:36:47.130: I/MyFragment(19040): onCreate
11-18 13:36:47.130: I/MyFragment(19040): onCreate
11-18 13:36:47.130: I/SinglePaneActivity(19040): onCreate
11-18 13:36:47.140: I/MyFragment(19040): onActivityCreated
11-18 13:36:47.150: I/MyFragment(19040): onActivityCreated
11-18 13:36:47.150: I/SinglePaneActivity(19040): onStart
11-18 13:36:47.150: W/SinglePaneActivity(19040): replacing fragment, counter = 3
11-18 13:36:47.160: I/MyFragment(19040): onCreate
11-18 13:36:47.160: I/MyFragment(19040): onActivityCreated
--Exiting the app--
11-18 13:36:48.880: I/SinglePaneActivity(19040): onStop
11-18 13:36:48.885: I/SinglePaneActivity(19040): onDestroy
11-18 13:36:48.885: I/MyFragment(19040): onDestroy
11-18 13:36:48.890: I/MyFragment(19040): onDestroy
11-18 13:36:48.890: I/MyFragment(19040): onDestroy
Таким образом, количество фрагментов растет после каждого поворота.
После ротации он восстанавливает фрагмент до того, как он перейдет к моему методу onStart, и мой метод onStart создает второй такой же фрагмент в одном контейнере макета. Но где он восстанавливает первый фрагмент? Я хочу его разблокировать. Или мне нужно изменить "if", чтобы проверить, если он уже создан? Но я не знаю, как это определить. Похоже, что нулевой тест бесполезен.
Я также сделал небольшое обходное решение, заменив метод add()
на replace()
. После этого количество фрагментов не растет, и каждый фрагмент уничтожается до того, как новый будет создан в onStart
. Но это создает серьезную проблему в некоторых фрагментах, где я запускаю некоторые фоновые процессы в своем методе onCreate...
Пожалуйста, помогите мне... Я действительно не знаю, что с этим делать. Большое спасибо за любые советы или идеи!
Ответы
Ответ 1
Ну, я нашел решение. Мне пришлось переместить код с менеджером фрагментов из onStart в onCreate и проверить null
значение savedInstanceState
. Ну, я не знал, что он сохраняется и восстанавливается с savedInstanceState
автоматически. Полезно знать!
EDIT:
И сделать это полностью правильно, то есть восстановить фрагмент, если savedInstanceState не имеет значения null, должно быть:
if(savedInstanceState == null)
{
mFragment = onCreatePane();
mFragment.setArguments(Utils.intentToFragmentArguments(getIntent()));
getSupportFragmentManager()
.beginTransaction()
.add(R.id.root_container,mFragment)
.commit();
}
else
{
mFragment = getSupportFragmentManager().findFragmentById(R.id.root_container);
}
Ответ 2
У меня была аналогичная проблема, но я добавлял дочерний фрагмент из родительского фрагмента и видел дубликат дочернего фрагмента. Я пытался добавить его в onStart
родительского фрагмента, чтобы я мог получить доступ к родительскому представлению, которое недоступно в onCreate
. Поэтому вместо того, чтобы переместить код на onCreate
, я переместил его на onActivityCreated
; этот метод имеет доступ к родительскому представлению и также имеет доступ к переменной savedInstanceState
, чтобы проверить, добавлен ли этот фрагмент автоматически. Это не проблема при добавлении фрагмента из активности, потому что представление активности доступно в onCreate
.
Если по какой-то причине вам нужно добавить фрагмент в onStart
или в другое место, где savedInstanceState
недоступно, вот альтернатива. Используя исходный код OnStart из вопроса...
if(mFragment == null)
{
mFragment = onCreatePane();
mFragment.setArguments(Utils.intentToFragmentArguments(getIntent()));
Log.w(TAG, "Fragment creation counter = " + createCounter);
createCounter++;
getSupportFragmentManager()
.beginTransaction()
.add(R.id.root_container,mFragment)
.commit();
}
... вы могли бы replace
, а не add
фрагмент:
if(mFragment == null)
{
mFragment = onCreatePane();
mFragment.setArguments(Utils.intentToFragmentArguments(getIntent()));
Log.w(TAG, "Fragment creation counter = " + createCounter);
createCounter++;
}
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.root_container,mFragment)
.commit();
Затем, если фрагмент уже был добавлен автоматически, ваш новый фрагмент просто заменит его в представлении.
Ответ 3
Или если ваш фрагмент не находится в фоновом стеке: setRetainInstance(true);