20 марта 2014 г.

Задачи и обратный стек (Tasks and back stack). Часть 3

И так продолжаем перевод этой статьи из альма-матер. Остановились мы здесь. В свою очередь это ссылается на другую статью. А мой перевод начала этой статьи здесь. Все в лучших традициях Java.

Сегодняшняя тема 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().

TS0001

И логи

TS0002

Здесь все как обычно. Далее вводим текст в поле

TS0003

Затем нажимаем кнопку Home и смотрим логи

TS0004

Как видим метод onSaveInstanceState() был вызван перед методом onPause().

Теперь вернемся к приложению кликнув по его иконке в списке приложений

TS0005

Как видим введенный нами текст сохранился. Что собственно и описывалось выше в этой статье. Система, по умолчанию, сама сохраняет состояние Активностей в остановленном состоянии.

Посмотрим логи

TS0006

Активность просто перешла из остановленного состояние в восстановленное и система сама восстановила состояние Активности.

Теперь нажмем кнопку Start Activity B

TS0007


И посмотрим логи

TS0008

Видно, что перед тем как перейти в приостановленное состояние Активность А вызвала метод onSaveInstanceState().

Теперь введем текст в Активности В

TS0009

Нажмем кнопку Start Activity C и посмотрим логи

TS0010

Видно, что Активность В, так же вызвала метод onSaveInstanceState() перед методом onPause().

Теперь мы находясь в Активности C нажмем кнопку обратно, мы увидим так же что текст введённый нами в Активности В был восстановлен системой

TS0011

И посмотрим логи

TS0012

Активность В просто перешла из остановленного состояния в восстановленное.

Теперь еще раз нажмем кнопку обратно. И увидим, что Активность А, по прежнему сохранила введенный в ней текст, так как все это время была в остановленном состоянии.

TS0005

Но если мы сейчас снова нажмем кнопку чтобы запустить Активность В, то там уже введенного нами текста не будет, поскольку после нажатия кнопку ОБРАТНО, тот экземпляр Активности В был уничтожен методом onDestroy().

TS0013

Теперь опять вернемся к переводу текста из альма-матер.

Как уже упоминалось когда Активность приостановлена (onPause()) или остановлена (onStop()) ее состояние сохраняется системой. Это происходит потому, что экземпляр Активности в приостановленном и остановленном состояниях находится в памяти. Таким образом все изменения которые пользователь сделал в Активности сохраняются и восстанавливаются когда Активность переходит на передний план.

Однако, если система уничтожает Активность для высвобождения памяти, то экземпляр Активности уничтожается и поэтому система просто не может восстановить ее состояние каким оно было до этого. Вместо этого система создает новый экземпляр Активности. Пользователь же, не знает, что Активность была уничтожена и создана заново и может ожидать ее увидеть такой же, какой он ее оставил. В такой ситуации вы должны позаботиться чтобы важная информация из Активности была сохранена.

Комментариев нет:

Отправить комментарий