Статьи

Сохранение состояния активности вашего приложения Android

Это последний пост в моей серии о сохранении данных в вашем Android-приложении. Предыдущие сообщения рассматривали различные способы сохранения данных в вашем приложении:

Введение: Как сохранить данные в вашем Android-приложении
Сохранение данных в файл в вашем приложении Android
Сохранение настроек в вашем приложении Android
Сохранение в базе данных SQLite в вашем приложении Android

В этом последнем посте объясняется, когда следует сохранять текущее состояние приложения, чтобы пользователи не теряли свои данные. Существует два типа состояний, которые можно сохранить: вы можете сохранить текущее состояние пользовательского интерфейса, если пользователь прерывает ввод данных, чтобы он мог возобновить ввод при повторном запуске приложения, или вы можете сохранить данные в более постоянном режиме. хранить данные для доступа к нему в любое время.

Сохранение текущего состояния интерфейса

Когда другое приложение запускается и скрывает ваше приложение, введенные вами данные могут быть не готовы для сохранения в постоянном хранилище данных, поскольку ввод еще не завершен. С другой стороны, вы все равно должны сохранять текущее состояние активности, чтобы пользователь не потерял всю свою работу, например, при поступлении телефонного звонка. Изменение конфигурации устройства Android, например поворот экрана, также будет иметь тот же эффект, так что это еще одна веская причина для сохранения состояния.

Когда происходит одно из этих событий или другое событие, которое требует сохранения состояния действия, Android SDK вызывает метод onSaveInstanceState для текущего действия, который получает объект android.os.Bundle в качестве параметра. Если вы используете стандартные представления из Android SDK и эти представления имеют уникальные идентификаторы, состояние этих элементов управления будет автоматически сохранено в комплекте. Но если вы используете несколько экземпляров представления, которые имеют один и тот же идентификатор, например, путем повторения представления с использованием ListView , значения, введенные в ваши элементы управления, не будут сохранены, поскольку идентификатор дублируется. Кроме того, если вы создадите свои собственные пользовательские элементы управления, вам придется сохранить состояние этих элементов управления.

Если вам нужно вручную сохранить состояние, вы должны переопределить метод onSaveInstanceState и добавить свою собственную информацию в полученный android.os.Bundle в качестве параметра с парами ключ / значение. Эта информация будет доступна позже, когда необходимо восстановить действие в методах onCreate и onRestoreInstanceState . Все типы примитивов или массивы значений этих типов могут быть сохранены в связке. Если вы хотите сохранить объекты или массив объектов в комплекте, они должны реализовать интерфейсы java.io.Serializable или android.os.Parcelable .

Чтобы продемонстрировать сохранение в состоянии, я буду использовать обновленную версию приложения, использованного в статье о сохранении в базе данных, которая доступна на GitHub по адресу http://github.com/CindyPotvin/RowCounter . Приложение управляет счетчиками строк, используемыми для вязания проекта, но у него не было возможности создать проект. В новой версии пользователь теперь может создавать новый проект, и состояние создаваемого проекта необходимо сохранить, если пользователь был прерван во время создания проекта. Для демонстрации числовые значения вводятся с использованием пользовательского элемента управления CounterView , который не обрабатывает сохранение состояния, поэтому мы должны сохранить состояние каждого счетчика вручную в связке.

01
02
03
04
05
06
07
08
09
10
11
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
   CounterView rowCounterAmountView = (CounterView)this.findViewById(R.id.row_counters_amount);
   savedInstanceState.putInt(ROW_COUNTERS_AMOUNT_STATE, rowCounterAmountView.getValue());
 
   CounterView rowAmountView = (CounterView)this.findViewById(R.id.rows_amount);
   savedInstanceState.putInt(ROWS_AMOUNT_STATE, rowAmountView.getValue());
 
   // Call the superclass to save the state of all the other controls in the view hierarchy
   super.onSaveInstanceState(savedInstanceState);
}

Когда пользователь возвращается обратно в приложение, Android SDK автоматически воссоздает действие из информации, которая была сохранена в пакете. На этом этапе вы также должны восстановить состояние пользовательского интерфейса для пользовательских элементов управления. Вы можете восстановить состояние пользовательского интерфейса, используя данные, которые вы сохранили в комплекте из двух методов вашей активности: метод onCreate, который вызывается первым при воссоздании действия, или метод onRestoreInstanceState, который вызывается после метода onStart . Вы можете восстановить состояние в одном или другом методе, и в большинстве случаев это не имеет значения, но оба они доступны, если требуется выполнить некоторую инициализацию после методов onCreate и onStart . Вот два возможных способа восстановления состояния из действия с использованием пакета, сохраненного в предыдущем примере:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
@Override
protected void onCreate(Bundle savedInstanceState) {
   [...Normal initialization of the activity...]
 
   // Check if a previously destroyed activity is being recreated.
   // If a new activity is created, the savedInstanceState will be empty
   if (savedInstanceState != null) {
      // Restore value of counters from saved state
      CounterView rowCounterAmountView;
      rowCounterAmountView  = (CounterView)this.findViewById(R.id.row_counters_amount);
      rowCounterAmountView.setValue(savedInstanceState.getInt(ROW_COUNTERS_AMOUNT_STATE));
 
      CounterView rowAmountView = (CounterView)this.findViewById(R.id.rows_amount);
      rowAmountView.setValue(savedInstanceState.getInt(ROWS_AMOUNT_STATE));
   }
}
01
02
03
04
05
06
07
08
09
10
11
12
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
   // Call the superclass to restore the state of all the other controls in the view hierarchy
   super.onRestoreInstanceState(savedInstanceState);
 
   // Restore value of counters from saved stat
   CounterView rowCounterAmountView = (CounterView)this.findViewById(R.id.row_counters_amount);
   rowCounterAmountView.setValue(savedInstanceState.getInt(ROW_COUNTERS_AMOUNT_STATE));
 
   CounterView rowAmountView = (CounterView)this.findViewById(R.id.rows_amount);
   rowAmountView.setValue(savedInstanceState.getInt(ROWS_AMOUNT_STATE));
}

Помните, что сохранение данных в пакете не означает, что оно является постоянным хранилищем данных, поскольку оно хранит только текущее состояние представления: оно не является частью жизненного цикла действия и вызывается только тогда, когда необходимо восстановить действие или оно отправлено на задний план. Это означает, что метод onSaveInstanceState не вызывается, когда приложение уничтожается, поскольку состояние активности никогда не может быть восстановлено. Чтобы сохранить данные, которые никогда не должны быть потеряны, вы должны сохранить данные в одном из постоянных хранилищ данных, описанных ранее в этой серии. Но когда эти данные должны храниться?

Сохранение ваших данных в постоянном хранилище данных

Если вам необходимо сохранить данные в постоянном хранилище данных, когда действие отправлено в фоновый режим или уничтожено по какой-либо причине, необходимо сохранить данные в методе onPause действия. Метод onStop вызывается только в том случае, если пользовательский интерфейс полностью скрыт, поэтому нельзя полагаться на то, что он вызывается постоянно. Все необходимые данные должны быть сохранены на этом этапе, потому что вы не можете контролировать, что произойдет после: пользователь может, например, закрыть приложение, и данные будут потеряны.

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

01
02
03
04
05
06
07
08
09
10
11
@Override
public void onPause() {
   super.onPause(); 
         
   ProjectsDatabaseHelper database = new ProjectsDatabaseHelper(this);
         
   // Update the value of all the counters of the project in the database since the activity is
   // destroyed or send to the background
   for (RowCounter rowCounter: mRowCounters) {
      database.updateRowCounterCurrentAmount(rowCounter);
   }

Позже, если ваше приложение не было уничтожено и пользователь снова получает доступ к действию, возможны два возможных процесса в зависимости от того, как ОС Android обработала ваше действие. Если действие все еще находится в памяти , например, если пользователь открыл другое приложение и сразу же вернулся, сначала вызывается метод onRestart , затем вызывается метод onStart и, наконец, вызывается метод OnResume, и действие отображается в пользователь. Но если действие было повторно использовано и воссоздается , например, если пользователь повернул устройство, чтобы воссоздать макет, процесс такой же, как и для нового действия: сначала вызывается метод onCreate , а затем вызывается метод вызывается метод onStart и, наконец, метод onResume, и действие показывается пользователю.

Итак, если вы хотите использовать данные, которые были сохранены в постоянном хранилище данных, для инициализации элементов управления в вашей деятельности, которые теряют свое состояние, вы должны поместить свой код в метод onResume, поскольку он всегда вызывается, независимо от того, было ли действие воссоздано или нет. В предыдущем примере нет необходимости явно восстанавливать данные, поскольку никакие пользовательские элементы управления не использовались: если действие было повторно использовано, оно воссоздается заново, а метод onCreate инициализирует элементы управления из данных в базе данных. Если действие все еще находится в памяти, делать больше нечего: Android SDK обрабатывает отображение значений в том виде, в котором они были впервые отображены, как объяснено ранее в разделе о сохранении состояний пользовательского интерфейса. Вот напоминание о том, что происходит в методе onCreate :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.project_activity);
         
   Intent intent = getIntent();
   long projectId = intent.getLongExtra("project_id", -1);
         
   // Gets the database helper to access the database for the application
   ProjectsDatabaseHelper database = new ProjectsDatabaseHelper(this);
   // Use the helper to get the current project
   Project currentProject = database.getProject(projectId);
 
   TextView projectNameView = (TextView)findViewById(R.id.project_name);
   projectNameView.setText(currentProject.getName());
           
        // Initialize the listview to show the row counters for the project from
        // the database
   ListView rowCounterList = (ListView)findViewById(R.id.row_counter_list);
   mRowCounters = currentProject.getRowCounters();
   ListAdapter rowCounterListAdapter = new RowCounterAdapter(this,
                                                             R.layout.rowcounter_row,
                                                             currentProject.getRowCounters());
   rowCounterList.setAdapter(rowCounterListAdapter);
   }

На этом мы завершаем серию о сохранении данных в вашем Android-приложении. Теперь вы знаете о различных типах хранилищ данных, доступных для приложений Android, и о том, когда они должны использоваться, чтобы ваши пользователи никогда не теряли свои данные и имели наилучший пользовательский опыт.