Да какая разница... Пациент быстрее жив, чем мертв.
Код фрагмента. Тут просто будем выводить в консоль его жизненный цикл
[syntax=java5]
public class MyFragment extends Fragment {
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
Log.d("sample", "Fragment onAttach "+this);
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("sample", "Frag onCreate "+this);
}
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View fragment = inflater.inflate(R.layout.fragment, null);
Log.d("sample", "Frag onCreateView " + this);
return fragment;
}
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Log.d("sample", "Frag onActivityCreated "+this);
}
public void onStart() {
super.onStart();
Log.d("sample", "Frag onStart "+this);
}
public void onResume() {
super.onResume();
Log.d("sample", "Frag onResume "+this);
}
public void onPause() {
super.onPause();
Log.d("sample", "Frag onPause "+this);
}
public void onStop() {
super.onStop();
Log.d("sample", "Frag onStop "+this);
}
public void onDestroyView() {
super.onDestroyView();
Log.d("sample", "Frag onDestroyView "+this);
}
public void onDestroy() {
super.onDestroy();
Log.d("sample", "Frag onDestroy "+this);
}
public void onDetach() {
super.onDetach();
Log.d("sample", "Frag onDetach "+this);
}
}
[/syntax]
Допустим наше желание НЕ сохранять фрагмент при повороте. Когда такое может понадобиться. К примеру, если у вас есть ViewPager, который является фрагментом, а в нем еще несколько фрагментов. А теперь добавьте сюда логики, к примеру, в ландшафте располагать 2 фрагмента, а в портрете один. Тут сохранение идет лесом. Закончим лирическое отступление.
Код активности. Будем его по мере необходимости изменять
[syntax=java5]
public class MainActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d("sample", "MainActivity onStart");
MyFragment myFragment = new MyFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.add(R.id.main_frame, myFragment);
transaction.commit();
}
protected void onStart() {
super.onStart();
Log.d("sample", "MainActivity onStart");
}
protected void onResume() {
super.onResume();
Log.d("sample", "MainActivity onResume");
}
protected void onPause() {
super.onPause();
Log.d("sample", "MainActivity onPause");
}
protected void onStop() {
super.onStop();
Log.d("sample", "MainActivity onStop");
}
protected void onDestroy() {
super.onDestroy();
Log.d("sample", "MainActivity onDestroy");
}
}[/syntax]
Я добавил специально transaction.add, дабы показать разницу. Итак, консоль
Всегда создаем фрагмент в onCreate. Жизненный цикл до onResume.
Кстати замечу, что в фигурных скобках информация об объекте фрагмента {адрес памяти, номер фрагмента в стеке, на сколько понял, id контейнера}MainActivity onStart
Fragment onAttach MyFragment{416a46e8 #0 id=0x7f080000}
Frag onCreate MyFragment{416a46e8 #0 id=0x7f080000}
Frag onCreateView MyFragment{416a46e8 #0 id=0x7f080000}
Frag onActivityCreated MyFragment{416a46e8 #0 id=0x7f080000}
Frag onStart MyFragment{416a46e8 #0 id=0x7f080000}
MainActivity onStart
MainActivity onResume
Frag onResume MyFragment{416a46e8 #0 id=0x7f080000}
Frag onPause MyFragment{416a46e8 #0 id=0x7f080000}
MainActivity onPause
MainActivity onResume
Frag onResume MyFragment{416a46e8 #0 id=0x7f080000}
Всё красиво, всё правильно. Поворачиваем экран.
Особо интересное я выделил жирным. До onCreate вылез наш фрагмент. А дальше вы можете лицезреть ДВА фрагмента с одной РАЗНЫМИ областями памяти, 416f9e48 и 41703da8.11-13 01:57:05.944: D/sample(19366): Frag onPause MyFragment{416a46e8 #0 id=0x7f080000}
11-13 01:57:05.944: D/sample(19366): MainActivity onPause
11-13 01:57:05.944: D/sample(19366): Frag onStop MyFragment{416a46e8 #0 id=0x7f080000}
11-13 01:57:05.944: D/sample(19366): MainActivity onStop
11-13 01:57:05.944: D/sample(19366): Frag onDestroyView MyFragment{416a46e8 #0 id=0x7f080000}
11-13 01:57:05.954: D/sample(19366): Frag onDestroy MyFragment{416a46e8 #0 id=0x7f080000}
11-13 01:57:05.954: D/sample(19366): Frag onDetach MyFragment{416a46e8 #0 id=0x7f080000}
11-13 01:57:05.954: D/sample(19366): MainActivity onDestroy
11-13 01:57:06.084: D/sample(19366): Fragment onAttach MyFragment{416f9e48 #0 id=0x7f080000}
11-13 01:57:06.084: D/sample(19366): Frag onCreate MyFragment{416f9e48 #0 id=0x7f080000}
11-13 01:57:06.154: D/sample(19366): MainActivity onStart
11-13 01:57:06.154: D/sample(19366): Frag onCreateView MyFragment{416f9e48 #0 id=0x7f080000}
11-13 01:57:06.164: D/sample(19366): Frag onActivityCreated MyFragment{416f9e48 #0 id=0x7f080000}
11-13 01:57:06.164: D/sample(19366): Fragment onAttach MyFragment{41703da8 #1 id=0x7f080000}
11-13 01:57:06.164: D/sample(19366): Frag onCreate MyFragment{41703da8 #1 id=0x7f080000}
11-13 01:57:06.174: D/sample(19366): Frag onCreateView MyFragment{41703da8 #1 id=0x7f080000}
11-13 01:57:06.174: D/sample(19366): Frag onActivityCreated MyFragment{41703da8 #1 id=0x7f080000}
11-13 01:57:06.174: D/sample(19366): Frag onStart MyFragment{416f9e48 #0 id=0x7f080000}
11-13 01:57:06.174: D/sample(19366): Frag onStart MyFragment{41703da8 #1 id=0x7f080000}
11-13 01:57:06.174: D/sample(19366): MainActivity onStart
11-13 01:57:06.194: D/sample(19366): MainActivity onResume
11-13 01:57:06.194: D/sample(19366): Frag onResume MyFragment{416f9e48 #0 id=0x7f080000}
11-13 01:57:06.194: D/sample(19366): Frag onResume MyFragment{41703da8 #1 id=0x7f080000}
Ну хорошо, давайте теперь поставим replace вместо add.
[syntax=java5]
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d("sample", "MainActivity onStart");
MyFragment myFragment = new MyFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.main_frame, myFragment);
transaction.commit();
}
[/syntax]
При старте всё нормально. Буду показывать только при повороте
Жирным я выделил область памяти сохранившегося фрагмента. Посмотрите, какие он проходит жизненные циклы.11-13 02:02:19.694: D/sample(20012): Frag onPause MyFragment{416a4dd8 #0 id=0x7f080000}
11-13 02:02:19.694: D/sample(20012): MainActivity onPause
11-13 02:02:19.704: D/sample(20012): Frag onStop MyFragment{416a4dd8 #0 id=0x7f080000}
11-13 02:02:19.704: D/sample(20012): MainActivity onStop
11-13 02:02:19.704: D/sample(20012): Frag onDestroyView MyFragment{416a4dd8 #0 id=0x7f080000}
11-13 02:02:19.704: D/sample(20012): Frag onDestroy MyFragment{416a4dd8 #0 id=0x7f080000}
11-13 02:02:19.704: D/sample(20012): Frag onDetach MyFragment{416a4dd8 #0 id=0x7f080000}
11-13 02:02:19.704: D/sample(20012): MainActivity onDestroy
11-13 02:02:19.774: D/sample(20012): Fragment onAttach MyFragment{416b7530 #0 id=0x7f080000}
11-13 02:02:19.784: D/sample(20012): Frag onCreate MyFragment{416b7530 #0 id=0x7f080000}
11-13 02:02:19.844: D/sample(20012): MainActivity onStart
11-13 02:02:19.854: D/sample(20012): Frag onCreateView MyFragment{416b7530 #0 id=0x7f080000}
11-13 02:02:19.854: D/sample(20012): Frag onActivityCreated MyFragment{416b7530 #0 id=0x7f080000}
11-13 02:02:19.854: D/sample(20012): Frag onDestroyView MyFragment{416b7530 #0 id=0x7f080000}
11-13 02:02:19.864: D/sample(20012): Frag onDestroy MyFragment{416b7530 #0 id=0x7f080000}
11-13 02:02:19.864: D/sample(20012): Frag onDetach MyFragment{416b7530 #0 id=0x7f080000}
11-13 02:02:19.864: D/sample(20012): Fragment onAttach MyFragment{416c1490 #0 id=0x7f080000}
11-13 02:02:19.864: D/sample(20012): Frag onCreate MyFragment{416c1490 #0 id=0x7f080000}
11-13 02:02:19.864: D/sample(20012): Frag onCreateView MyFragment{416c1490 #0 id=0x7f080000}
11-13 02:02:19.874: D/sample(20012): Frag onActivityCreated MyFragment{416c1490 #0 id=0x7f080000}
11-13 02:02:19.874: D/sample(20012): Frag onStart MyFragment{416c1490 #0 id=0x7f080000}
11-13 02:02:19.874: D/sample(20012): MainActivity onStart
11-13 02:02:19.874: D/sample(20012): MainActivity onResume
11-13 02:02:19.884: D/sample(20012): Frag onResume MyFragment{416c1490 #0 id=0x7f080000}
onAttach
onCreate
onCreateView
onActivityCreated
onDestroyView
onDestroy
Более того, если я создам фрагмент к примеру в жизненном цикле активности onStart или onReume, то данный фрагмент пройдет еще больше жизненных циклов.
Попробуем удалить. Добавим метод удаления фрагмента по ID контейнера в onCreate активити.
[syntax=java5]
public class MainActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d("sample", "MainActivity onStart");
MyFragment myFragment = (MyFragment) getSupportFragmentManager().findFragmentById(R.id.main_frame);
if (myFragment!=null) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.remove(myFragment);
myFragment = null;
ft.commit();
Log.d("sample", "Removed in onCreate");
}
myFragment = new MyFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.main_frame, myFragment);
transaction.commit();
}
protected void onStart() {
super.onStart();
Log.d("sample", "MainActivity onStart");
}
protected void onResume() {
super.onResume();
Log.d("sample", "MainActivity onResume");
}
protected void onPause() {
super.onPause();
Log.d("sample", "MainActivity onPause");
}
protected void onStop() {
super.onStop();
Log.d("sample", "MainActivity onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
}
}
[/syntax]
Получим
И получаем, что фрагмент не может удалиться. Он прошел свои жизненные циклы, умер и после создан был новый. Замечу, замечу, что до поворота и после поворота, все экземпляры (до поворота один и после поворота два) - разные объекты. Так что не думайте, что область памяти у этого глючного фрагмента, что появляется после поворота, будет того, что до поворота.11-13 02:11:26.684: D/sample(20780): Frag onPause MyFragment{416a1df0 #0 id=0x7f080000}
11-13 02:11:26.684: D/sample(20780): MainActivity onPause
11-13 02:11:26.694: D/sample(20780): Frag onStop MyFragment{416a1df0 #0 id=0x7f080000}
11-13 02:11:26.694: D/sample(20780): MainActivity onStop
11-13 02:11:26.694: D/sample(20780): Frag onDestroyView MyFragment{416a1df0 #0 id=0x7f080000}
11-13 02:11:26.694: D/sample(20780): Frag onDestroy MyFragment{416a1df0 #0 id=0x7f080000}
11-13 02:11:26.694: D/sample(20780): Frag onDetach MyFragment{416a1df0 #0 id=0x7f080000}
11-13 02:11:26.744: D/sample(20780): Fragment onAttach MyFragment{416b5538 #0 id=0x7f080000}
11-13 02:11:26.744: D/sample(20780): Frag onCreate MyFragment{416b5538 #0 id=0x7f080000}
11-13 02:11:26.784: D/sample(20780): MainActivity onStart
11-13 02:11:26.784: D/sample(20780): Removed in onCreate
11-13 02:11:26.794: D/sample(20780): Frag onCreateView MyFragment{416b5538 #0 id=0x7f080000}
11-13 02:11:26.794: D/sample(20780): Frag onActivityCreated MyFragment{416b5538 #0 id=0x7f080000}
11-13 02:11:26.794: D/sample(20780): Frag onDestroyView MyFragment{416b5538 #0 id=0x7f080000}
11-13 02:11:26.794: D/sample(20780): Frag onDestroy MyFragment{416b5538 #0 id=0x7f080000}
11-13 02:11:26.794: D/sample(20780): Frag onDetach MyFragment{416b5538 #0 id=0x7f080000}
11-13 02:11:26.794: D/sample(20780): Fragment onAttach MyFragment{416bf5e0 #0 id=0x7f080000}
11-13 02:11:26.794: D/sample(20780): Frag onCreate MyFragment{416bf5e0 #0 id=0x7f080000}
11-13 02:11:26.794: D/sample(20780): Frag onCreateView MyFragment{416bf5e0 #0 id=0x7f080000}
11-13 02:11:26.794: D/sample(20780): Frag onActivityCreated MyFragment{416bf5e0 #0 id=0x7f080000}
11-13 02:11:26.794: D/sample(20780): Frag onStart MyFragment{416bf5e0 #0 id=0x7f080000}
11-13 02:11:26.794: D/sample(20780): MainActivity onStart
11-13 02:11:26.804: D/sample(20780): MainActivity onResume
11-13 02:11:26.804: D/sample(20780): Frag onResume MyFragment{416bf5e0 #0 id=0x7f080000}
Решить проблему я смог, очень стандартно, но не так, как хотелось бы
[syntax=java5]
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myFragment = (MyFragment) getSupportFragmentManager().findFragmentById(R.id.main_frame);
if (myFragment==null) {
myFragment = new MyFragment();
}
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.main_frame, myFragment);
transaction.commit();
}
[/syntax]
Т.е. ищите фрагмент всегда, даже если вы ничего не сохраняете.
Если вкратце, то при повороте экрана, фрагменты всегда сохраняются, так что вы можете даже с transaction.replace получить memory leak. Речь идёт про динамически добавленные фрагменты. Статические итак не удаляются11-13 02:21:26.044: D/sample(21645): Frag onPause MyFragment{416a7c78 #0 id=0x7f080000}
11-13 02:21:26.044: D/sample(21645): MainActivity onPause
11-13 02:21:26.044: D/sample(21645): Frag onStop MyFragment{416a7c78 #0 id=0x7f080000}
11-13 02:21:26.044: D/sample(21645): MainActivity onStop
11-13 02:21:26.064: D/sample(21645): Frag onDestroyView MyFragment{416a7c78 #0 id=0x7f080000}
11-13 02:21:26.064: D/sample(21645): Frag onDestroy MyFragment{416a7c78 #0 id=0x7f080000}
11-13 02:21:26.064: D/sample(21645): Frag onDetach MyFragment{416a7c78 #0 id=0x7f080000}
11-13 02:21:26.154: D/sample(21645): Fragment onAttach MyFragment{416b8e98 #0 id=0x7f080000}
11-13 02:21:26.154: D/sample(21645): Frag onCreate MyFragment{416b8e98 #0 id=0x7f080000}
11-13 02:21:26.194: D/sample(21645): Frag onCreateView MyFragment{416b8e98 #0 id=0x7f080000}
11-13 02:21:26.194: D/sample(21645): Frag onActivityCreated MyFragment{416b8e98 #0 id=0x7f080000}
11-13 02:21:26.204: D/sample(21645): Frag onStart MyFragment{416b8e98 #0 id=0x7f080000}
11-13 02:21:26.204: D/sample(21645): MainActivity onStart
11-13 02:21:26.204: D/sample(21645): MainActivity onResume
11-13 02:21:26.204: D/sample(21645): Frag onResume MyFragment{416b8e98 #0 id=0x7f080000}
Материалы по теме:
Вырезка с книги Professional Android 4 Application Development (Wrox Professional Guides)
Dynamically added fragments are not removed when the container is removed"to ensure a consistent user experience, Android persists the Fragment layout and associated back stack when an Activity is restarted due to a configuration change." (p. 124)
Android Fragment lifecycle over orientation changes
Fragment/Activity Lifecycles and Orientation Change
Weird fragment lifecycle error