Сегодняшняя тема Saving activity state (сохранение состояния Активности).
Как уже обсуждалось выше, по умолчанию, система сохраняет состояние активности когда она остановлена. Таким образом, когда пользователь возвращается обратно, к предыдущей Активности, ее интерфейс отображается таким, как был оставлен пользователем. Однако вы можете, и вы должны, сохранять состояние вашей Активности на случай если она будет уничтожена, после чего естественно будет создана заново.
Когда система останавливает одну из ваших Активностей (например, в случае запуска новой Активности или перехода задачи в фоновый режим), то она может уничтожить вашу Активность, в случае если ей понадобятся какие либо ресурсы (к примеру, память). Когда такое происходит, информация о состоянии Активности теряется. Когда такое происходит, система все равно знает что Активность была помещена в обратный стек, но когда активность получает фокус, то есть помещается на вершину стека, то она создается заново системой, а не восстанавливается ей. Чтобы избежать потери пользовательских данных, вы должны сохранять их используя метод onSaveInstanceState().
И дальше эта статья отсылает нас сюда. И самое интересная фразочка в этом самом сюда следующая:
Нет ни какой гарантии что onSaveInstanceState() будет вызвана перед тем как ваша Активность будет уничтожена, поскольку есть случаи в которых сохранение состояния не является необходимым (например, когда пользователь покидает вашу активность используя кнопку ОБРАТНО, то это означает, что пользователь сам прекратил работу с Активностью). Если система вызывает onSaveInstanceState(), то делает это до onStop() и возможно перед onPause().
Вот так вот!
Рассмотрим это на примере. Я в очередной раз модифицировал свое мега приложение AP0003. Добавив в Разметки А и В поля ввода текста, а в Активности А и В метод onSaveInstanceState().
Текст layout_b.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#faf20d" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".ActivityB" > <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/strB" /> <Button android:id="@+id/buttonStartC" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/textView1" android:onClick="onClickStartC" android:text="Start Activity C" /> <TextView android:id="@+id/textStateActB" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@+id/buttonStartC" android:layout_below="@+id/buttonStartC" android:background="#fffcfc" android:text="Этот экземпляр АсtivityB запущен в первый раз" /> <TextView android:id="@+id/textActCountB" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@+id/textStateActB" android:layout_below="@+id/textStateActB" android:layout_marginTop="17dp" android:background="#fffcfc" android:text="TextView" /> <EditText android:id="@+id/editTextB" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignLeft="@+id/textActCountB" android:layout_below="@+id/textActCountB" android:layout_marginTop="17dp" android:background="#fcf5ab" android:ems="10" > <requestFocus /> </EditText> </RelativeLayout>
А теперь исходинк ActivityB.java
package com.example.ap0003; import java.util.List; import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.EditText; import android.widget.TextView; public class ActivityB extends Activity { final String TAG = "States"; TextView tvTextLife; List<ActivityManager.RunningTaskInfo> list; ActivityManager am; Integer TotalActCount; Boolean FirstStart; Boolean NextAct; TextView MyTextB; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.layout_b); Log.d(TAG, "ActivityB: onCreate()"); // флаг что активность запущена впервые FirstStart = true; // кнопка запуска следующей Активности не нажималась NextAct = false; } @Override protected void onStart() { super.onStart(); Log.d(TAG, "ActivityB: onStart()"); } @Override protected void onResume() { super.onResume(); Log.d(TAG, "ActivityB: onResume()"); // получаем список 10 последних задач am = (ActivityManager) getSystemService(ACTIVITY_SERVICE); list = am.getRunningTasks(10); // перебираем список задач и выбираем свою по имени пакета // com.example.ap0003 for (RunningTaskInfo task : list) { if (task.baseActivity.flattenToShortString().startsWith( "com.example.ap0003")) { // находим поле для вывода информации о количестве запущенных // Активностей tvTextLife = (TextView) findViewById(R.id.textActCountB); TotalActCount = task.numActivities; // коррекция счетчика для кнопки ОБРАТНО if (NextAct == true & FirstStart == false) TotalActCount = TotalActCount - 1; // выводим количество Активностей в задаче tvTextLife.setText("Activites in task " + TotalActCount); // коррекция счетчика для кнопки ДОМОЙ NextAct = false; } } } @Override protected void onPause() { super.onPause(); Log.d(TAG, "ActivityB: oPause()"); // флаг что активность уже была запущена FirstStart = false; // находим текстовое поле по его идентификатору tvTextLife = (TextView) findViewById(R.id.textStateActB); // присваиваем значение атрибуту Text для выбранного TextView tvTextLife.setText("Этот экземпляр ActivityB уже был запущен!"); } @Override protected void onStop() { super.onStop(); Log.d(TAG, "ActivityB: onStop()"); } @Override protected void onRestart() { super.onRestart(); Log.d(TAG, "ActivityB: onRestart()"); } @Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, "ActivityB: onDestroy()"); } public void onClickStartC(View v) { Intent intent = new Intent(ActivityB.this, ActivityC.class); startActivity(intent); // кнопка запуска следующей Активности была нажата NextAct = true; } @Override protected void onSaveInstanceState(Bundle savedInstanceState) { // получаем ссылку на поле ввода текста MyTextB = (EditText) findViewById(R.id.editTextB); Log.d(TAG, "onSaveInstanceState B text: " + MyTextB.getText().toString()); } }
Строки 110 и 111 выводят текст набранный в поле ввода в логах. Это сделано для наглядности работы метода onSaveInstanceState().
Теперь запустим прогу и поиграемся с ней чтобы понять как и когда срабатывает метод onSaveInstanceState().
И логи
Здесь все как обычно. Далее вводим текст в поле
Затем нажимаем кнопку Home и смотрим логи
Как видим метод onSaveInstanceState() был вызван перед методом onPause().
Теперь вернемся к приложению кликнув по его иконке в списке приложений
Как видим введенный нами текст сохранился. Что собственно и описывалось выше в этой статье. Система, по умолчанию, сама сохраняет состояние Активностей в остановленном состоянии.
Посмотрим логи
Активность просто перешла из остановленного состояние в восстановленное и система сама восстановила состояние Активности.
Теперь нажмем кнопку Start Activity B
И посмотрим логи
Видно, что перед тем как перейти в приостановленное состояние Активность А вызвала метод onSaveInstanceState().
Теперь введем текст в Активности В
Нажмем кнопку Start Activity C и посмотрим логи
Видно, что Активность В, так же вызвала метод onSaveInstanceState() перед методом onPause().
Теперь мы находясь в Активности C нажмем кнопку обратно, мы увидим так же что текст введённый нами в Активности В был восстановлен системой
И посмотрим логи
Активность В просто перешла из остановленного состояния в восстановленное.
Теперь еще раз нажмем кнопку обратно. И увидим, что Активность А, по прежнему сохранила введенный в ней текст, так как все это время была в остановленном состоянии.
Но если мы сейчас снова нажмем кнопку чтобы запустить Активность В, то там уже введенного нами текста не будет, поскольку после нажатия кнопку ОБРАТНО, тот экземпляр Активности В был уничтожен методом onDestroy().
Теперь опять вернемся к переводу текста из альма-матер.
Как уже упоминалось когда Активность приостановлена (onPause()) или остановлена (onStop()) ее состояние сохраняется системой. Это происходит потому, что экземпляр Активности в приостановленном и остановленном состояниях находится в памяти. Таким образом все изменения которые пользователь сделал в Активности сохраняются и восстанавливаются когда Активность переходит на передний план.
Однако, если система уничтожает Активность для высвобождения памяти, то экземпляр Активности уничтожается и поэтому система просто не может восстановить ее состояние каким оно было до этого. Вместо этого система создает новый экземпляр Активности. Пользователь же, не знает, что Активность была уничтожена и создана заново и может ожидать ее увидеть такой же, какой он ее оставил. В такой ситуации вы должны позаботиться чтобы важная информация из Активности была сохранена.
Комментариев нет:
Отправить комментарий