Я нигде не нашел хорошего окончательного ответа на этот вопрос (может быть, я плохо ищу?)
Жизненный цикл активности Android имеет различные события, которые заставят пользователя потерять содержимое EditText, если вы не сохраните и не восстановите его во время событий жизненного цикла — как правило, с использованием общих настроек. Звучит просто, но я не могу заставить это работать на практике; несмотря на то, что содержимое EditText регистрируется при каждом событии, оно не обновляется на моем устройстве, когда я перехожу к другому приложению, а затем обратно.
У меня есть пользователь, вводящий серийный номер в поле EditText. Вот что я делаю в своей деятельности:
private lateinit var sharedPref: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// (boilerplate databinding code omitted here)
sharedPref = getPreferences(Context.MODE_PRIVATE)
}
private fun loadSN() {
if (sharedPref.contains(PREFERENCE_SN)) {
binding.serialNumberEditText.setText(sharedPref.getString(PREFERENCE_SN, ""))
Log.d(TAG, "serialNumberEditText is now ${binding.serialNumberEditText.text.toString()}")
}
}
private fun saveSN() {
Log.d(TAG, "saveSN: saving ${binding.serialNumberEditText.text.toString()}")
with (sharedPref.edit()) {
putString(PREFERENCE_SN, binding.serialNumberEditText.text.toString())
apply()
}
}
override fun onStart() {
super.onStart()
loadSN()
}
override fun onPause() {
super.onPause()
saveSN()
}
override fun onStop() {
super.onStop()
saveSN()
}
override fun onResume() {
super.onResume()
loadSN()
}
override fun onRestart() {
super.onRestart()
loadSN()
}
Пример вывода журнала при переходе к другому приложению и обратно:
[navigate away from app]
D/FooActivity: saveSN: saving ABCDE
[^^ return to app here]
D/FooActivity: serialNumberEditText is now ABCDE
D/FooActivity: savePreference was called, restoring ABCDE
D/FooActivity: serialNumberEditText is now ABCDE
D/FooActivity: savePreference was called, restoring ABCDE
D/FooActivity: serialNumberEditText is now ABCDE
D/FooActivity: savePreference was called, restoring ABCDE
Но это не восстановление ABCDE в поле EditText. Почему?
Вы можете использовать viewModel и модуль сохранения состояния, который сделает это за вас. Добавьте это в gradle (уровень приложения):
dependencies {
// Java language implementation
implementation "androidx.savedstate:savedstate:1.0.0"
// Kotlin
implementation "androidx.savedstate:savedstate-ktx:1.1.0-rc01"
}
если вы не хотите использовать модуль, вы можете сохранить состояние через:
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
outState.putString("edit_text", yourEditTextView.getText().toString());
super.onSaveInstanceState(outState);
}
и получить это значение
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState!=null){
if (savedInstanceState.containsKey("edit_text")){
String valueForEditText = savedInstanceState.getString("edit_text");
yourEditTextView.setText(valueForEditText);
}
}
Вы не должны сохранять состояние при остановке и паузе, просто определите свой текст редактирования при создании, и Android сделает это за вас.
Если бы у вас был действительно сложный пользовательский интерфейс, я могу понять, почему это может представлять интерес. Тем не менее, я чувствую, что не должна требоваться отдельная ViewModel для поддержки содержимого одного EditText, если пользователь уходит и возвращается обратно. Есть ли более простой способ?
Простым решением было бы @Override onSaveInstanceState() и onRestoreInstanceState внутри вашей активности. Достаточно обрабатывать вызовы onPause и onStop вашей активности. Вызов OnDestroy может поддерживаться таким образом, только если он вызван поворотом экрана. Вы должны использовать общие настройки, только если хотите восстановить состояние EditText после того, как приложение было закрыто пользователем.
На самом деле, насколько я помню, достаточно определить ваш текст редактирования как переменную внутри вашего класса активности, и текст внутри него будет автоматически восстановлен после вызовов onPause, onStop системой Android. Но я не могу проверить это прямо сейчас.
Фреймворк автоматически сохранит состояние View
s (включая EditText
s) в текущем макете, но только пока приложение находится в «фоновом режиме». Это включает в себя, когда приложение уничтожается системой для освобождения памяти, представления будут восстановлены в том виде, в котором они были, но не в том случае, если пользователь смахивает приложение, чтобы явно закрыть его, или если вы удаляете макет, например. переходим к другому Activity
. Если вы хотите, чтобы те же данные отображались в следующий раз, когда пользователь откроет этот макет, вам нужно будет сохранить состояние самостоятельно, и это означает что-то вроде использования SharedPreferences
.
Судя по вашим логам, восстановление идет нормально? Вы сохраняете ABCDE
, вы устанавливаете его на ABCDE
в следующий раз, когда вы вернетесь, затем вы проверяете содержимое EditText
, и он установлен на ABCDE
? Что вы видите вместо этого, и есть ли что-то еще, что устанавливает данные в этом представлении, например привязка данных в XML? Система восстанавливает состояние просмотра до onStart
, поэтому ваше восстановление происходит после этого, но если есть какое-либо сохраненное состояние, это должны быть те же данные, которые вы все равно сохранили.
Да, это странно; ясно, несмотря на то, что говорят журналы, я делаю это неправильно, поскольку ABCDE
не восстанавливается до EditText
на экране. Я использую привязку данных, чтобы упростить обращение к элементам TextView
, которые отображают метаданные базы данных. На TextChangedListener
есть EditText
, но его отключение не влияет на поведение.
Вы можете попробовать добавить точку останова в метод setText
EditText (например, в классе EditText
нажмите ctrl+щелчок по имени метода, чтобы попасть туда), а затем отлаживать приложение — надеюсь, оно будет останавливаться каждый раз, когда что-то изменяет содержимое, и вы можете посмотреть на стек вызовов слева, чтобы увидеть, что установлено не так. Ваш код, похоже, работает нормально, поэтому должно быть что-то еще, появившееся позже и снова изменившее его?
В документации прямо указано, что состояние пользовательского интерфейса не сохраняется автоматически, когда приложение переводится в фоновый режим или уничтожается для освобождения памяти: developer.android.com/topic/libraries/architecture/…
Это для View
s: developer.android.com/guide/components/activities/… «По умолчанию система использует состояние экземпляра Bundle для сохранения информации о каждом объекте View в вашем макете активности (например, текстовое значение введен в виджет EditText). Таким образом, если ваш экземпляр активности уничтожается и создается заново, состояние макета восстанавливается до его предыдущего состояния без необходимости кода». Вы можете попробовать это с включенным Don't keep activities
и EditText
в макете. Документация Android дикая, я все еще видел скриншоты Holo
@cactustictacs Запускать отладчик странно. Он работает с точкой останова отладчика, которая вызывает loadSN 3 раза (onRestart, onStart, onResume), но когда я выключаю отладчик, он больше не работает. Он работает последовательно, когда я использую Handler
, чтобы применить задержку к вызову setText
внутри loadSN
. Очевидно, что в моем коде происходит какое-то странное состояние гонки, поэтому я приму ваш ответ как в целом правильный способ сделать это. Спасибо!
Я подозреваю, что это как-то связано с кодом привязки данных, который вы пропустили выше. Кстати, ваши перегрузки
onStart
,onStop
иonRestart
избыточны, посколькуonPause
иonResume
всегда вызываются, когда активность теряет/приобретает фокус.