Сегодняшняя тема 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()) ее состояние сохраняется системой. Это происходит потому, что экземпляр Активности в приостановленном и остановленном состояниях находится в памяти. Таким образом все изменения которые пользователь сделал в Активности сохраняются и восстанавливаются когда Активность переходит на передний план.
Однако, если система уничтожает Активность для высвобождения памяти, то экземпляр Активности уничтожается и поэтому система просто не может восстановить ее состояние каким оно было до этого. Вместо этого система создает новый экземпляр Активности. Пользователь же, не знает, что Активность была уничтожена и создана заново и может ожидать ее увидеть такой же, какой он ее оставил. В такой ситуации вы должны позаботиться чтобы важная информация из Активности была сохранена.
Комментариев нет:
Отправить комментарий