Как правильно удалить фрагмент оставшегося экземпляра

В настоящее время я хочу сохранить дорогостоящую структуру данных во время изменений конфигурации. Я предпочитаю не использовать Bundle для его обработки, поскольку дорогая структура данных не является исчерпывающей.

Следовательно, я использую фрагмент без UI ( вызванный им RetainInstanceFragment ), с его setRetainInstance(true) для хранения структуры данных.

 public class RetainInstanceFragment extends Fragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Creating expensive data structure expensiveDataStructure = CreateExpensiveDataStructure(); // Tell the framework to try to keep this fragment around // during a configuration change. setRetainInstance(true); } public ExpensiveDataStructure expensiveDataStructure = null; } 

Фрагмент пользовательского интерфейса (называемый UIFragment ) получит дорогостоящую структуру данных из RetainInstanceFragment . Всякий раз, когда происходят изменения конфигурации в UIFragment , UIFragment всегда будет пытаться получить «кэшированный» RetainInstanceFragment из FragmentManager , прежде чем он решит создать новый RetainInstanceFragment .

Пример кода выглядит следующим образом.

 public class UIFragment extends SherlockListFragment @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); FragmentManager fm = getFragmentManager(); // Check to see if we have retained the worker fragment. retainInstanceFragment = (RetainInstanceFragment)fm.findFragmentByTag("data"); // If not retained (or first time running), we need to create it. if (retainInstanceFragment == null) { retainInstanceFragment = new RetainInstanceFragment(); fm.beginTransaction().add(watchlistArrayFragment, "data").commit(); } else { // We can re-use retainInstanceFragment.expensiveDataStructure even // after configuration change. } } } 

Однако есть проблема. Всякий раз, когда я уничтожаю свой старый UIFragment и заменяю его новым UIFragment , я ожидаю , что старый RetainInstanceFragment также будет уничтожен. Вот как я уничтожаю и создаю новый UIFragment

 public class MyFragmentActivity extends SlidingFragmentActivity // Being triggered when there is different menu item in sliding menu being // selected. public void selectActiveContent(Country country) { Fragment fragment = new UIFragment(country); getSupportFragmentManager().beginTransaction().replace(R.id.content, fragment).commitAllowingStateLoss(); } 

Но старый RetainInstanceFragment никогда не уничтожается.

Мое предположение, возможно, я забыл выполнить очистку в UIFragment . Следовательно, я добавляю следующий код

UIFragment

 @Override public void onDetach() { super.onDetach(); // To differentiate whether this is a configuration changes, or we are // removing away this fragment? if (this.isRemoving()) { FragmentManager fm = getFragmentManager(); fm.beginTransaction().remove(retainInstanceFragment).commit(); } } 

Однако он не работает все время. Я выполняю несколько скользящих щелчков меню.

 1. selectActiveContent() -> Create new UIFragment and new RetainInstanceFragment 2. selectActiveContent() -> Create new UIFragment, but re-use previous RetainInstanceFragment. (Wrong behavior) 3. selectActiveContent() -> Create new UIFragment, and new RetainInstanceFragment. 4. selectActiveContent() -> Create new UIFragment, but re-use previous RetainInstanceFragment. (Wrong behavior) 

Любая идея, как я могу правильно удалить сохранившийся фрагмент экземпляра?

Как предложил @Luksprog, работает следующий метод. Тем не менее, он по-прежнему не объясняет, почему предыдущая очистка, выполняемая через onDetach , не работает. Если кто-нибудь сможет объяснить, почему это решение работает, а предыдущее – нет, я был бы очень благодарен. 🙂

UIFragment

 @Override public void onDetach() { super.onDetach(); } public void cleanupRetainInstanceFragment() { FragmentManager fm = getFragmentManager(); fm.beginTransaction().remove(this.retainInstanceFragment).commit(); } 

MyFragmentActivity

 public class MyFragmentActivity extends SlidingFragmentActivity // Being triggered when there is different menu item in sliding menu being // selected. public void selectActiveContent(Country country) { // ******************************************* // Solution suggested by @Luksprog. It works! // But I have no idea why it works and previous doesn't work... // ******************************************* Fragment oldFragment = getSupportFragmentManager().findFragmentById(R.id.content); if (oldFragment instanceof UIFragment) { ((UIFragment)oldFragment).cleanupRetainInstanceFragment(); } Fragment fragment = new UIFragment(country); getSupportFragmentManager().beginTransaction().replace(R.id.content, fragment).commitAllowingStateLoss(); } 

(Отредактировано) Полезный комментарий от @Luksprog

Операции фрагмента не выполняются сразу. Мое предположение заключалось в том, что выполнение этой транзакции в обратном вызове onDetach () не приведет к удалению экземпляра остаточного фрагмента до завершения транзакции замены фрагмента пользовательского интерфейса, и поэтому ваш новый фрагмент пользовательского интерфейса все еще будет хранить экземпляр сохраняемого фрагмента, который все еще доступен, поэтому он не будет создавать новый один. Ваш предыдущий метод не в духе рамки фрагментов, где фрагменты не знают о других фрагментах, и активность управляет всеми ними, поскольку он знает больше об общем состоянии приложения.

Я думаю, вы можете просто удалить фрагмент из транзакции фрагмента.

  if (mWorkFragment != null) { fm.beginTransaction().remove(mWorkFragment).commitAllowingStateLoss(); }