У меня есть довольно простое приложение с фиктивным Activity
и фиктивным Android Lifecycle ViewModel ViewModel
.
Фрагментальная активность
class FragmentActivity: AppCompatActivity() {
companion object {
private const val TAG = "FragmentActivity"
private const val KEY = "key_key"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_fragment)
Log.d(TAG, "Activity ${hashCode()}, onCreate: orientation ${resources.configuration.orientation}")
if (savedInstanceState != null) {
Log.d(TAG, "Activity ${hashCode()}, onCreate: saved string from savedInstanceState ${savedInstanceState.getString(KEY)}")
} else {
Log.d(TAG, "Activity ${hashCode()}, onCreate: no savedInstanceState")
}
val myViewModel: MyViewModel = ViewModelProviders
.of(this, VmFactory())
.get(MyViewModel::class.java)
}
override fun onResume() {
super.onResume()
Log.d(TAG, "Activity ${hashCode()}, onResume: orientation ${resources.configuration.orientation}")
}
override fun onStop() {
super.onStop()
Log.d(TAG, "Activity ${hashCode()}, onStop: orientation ${resources.configuration.orientation}")
}
override fun onDestroy() {
super.onDestroy()
Log.d(TAG, "Activity ${hashCode()}, onDestroy: orientation ${resources.configuration.orientation}")
}
override fun onSaveInstanceState(outState: Bundle?) {
val savedString = "SAVED_STATE_" + hashCode()
outState?.putString(KEY, savedString)
Log.d(TAG, "Activity ${hashCode()}, onSaveInstanceState: $savedString")
super.onSaveInstanceState(outState)
}
}
Модель представления
class MyViewModel: ViewModel() {
companion object {
private const val TAG = "MyViewModel"
}
init {
Log.d(TAG, "MyViewModel ${hashCode()}: created")
}
override fun onCleared() {
Log.d(TAG, "MyViewModel ${hashCode()}: onCleared")
super.onCleared()
}
}
Фабрика моделей модели
class VmFactory: ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if (modelClass == MyViewModel::class.java) {
return MyViewModel() as T
} else {
throw IllegalArgumentException()
}
}
}
Манифест
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.dkarmazi.unknownmemorysampleapp">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".WebViewActivity">
</activity>
<activity android:name=".FragmentActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Шаги по уничтожению ViewModel
- Переведите приложение в ландшафтный режим
- Заблокировать экран
- Разблокируйте экран и обратите внимание, что ViewModel исчезла, Activity уничтожена и создана два раза.
Вывод журнала
02-20 16:30:14.159 8296-8296/com.dkarmazi.viewmodelscoping D/FragmentActivity: Activity 244798673, onCreate: orientation 2
02-20 16:30:14.159 8296-8296/com.dkarmazi.viewmodelscoping D/FragmentActivity: Activity 244798673, onCreate: no savedInstanceState
02-20 16:30:14.169 8296-8296/com.dkarmazi.viewmodelscoping D/MyViewModel: MyViewModel 55090662: created
02-20 16:30:14.183 8296-8296/com.dkarmazi.viewmodelscoping D/FragmentActivity: Activity 244798673, onResume: orientation 2
### LOCKED IN LANDSCAPE MODE
02-20 16:30:22.978 8296-8296/com.dkarmazi.viewmodelscoping D/FragmentActivity: Activity 244798673, onSaveInstanceState: SAVED_STATE_244798673
02-20 16:30:22.996 8296-8296/com.dkarmazi.viewmodelscoping D/FragmentActivity: Activity 244798673, onStop: orientation 2
### UNLOCKED IN LANDSCAPE MODE
02-20 16:30:33.177 8296-8296/com.dkarmazi.viewmodelscoping D/FragmentActivity: Activity 244798673, onStop: orientation 2
02-20 16:30:33.178 8296-8296/com.dkarmazi.viewmodelscoping D/MyViewModel: MyViewModel 55090662: onCleared
02-20 16:30:33.179 8296-8296/com.dkarmazi.viewmodelscoping D/FragmentActivity: Activity 244798673, onDestroy: orientation 2
02-20 16:30:33.241 8296-8296/com.dkarmazi.viewmodelscoping D/FragmentActivity: Activity 218111434, onCreate: orientation 1
02-20 16:30:33.241 8296-8296/com.dkarmazi.viewmodelscoping D/FragmentActivity: Activity 218111434, onCreate: saved string from savedInstanceState SAVED_STATE_244798673
02-20 16:30:33.242 8296-8296/com.dkarmazi.viewmodelscoping D/MyViewModel: MyViewModel 113479034: created
02-20 16:30:33.248 8296-8296/com.dkarmazi.viewmodelscoping D/FragmentActivity: Activity 218111434, onResume: orientation 1
02-20 16:30:33.705 8296-8296/com.dkarmazi.viewmodelscoping D/FragmentActivity: Activity 218111434, onSaveInstanceState: SAVED_STATE_218111434
02-20 16:30:33.710 8296-8296/com.dkarmazi.viewmodelscoping D/FragmentActivity: Activity 218111434, onStop: orientation 1
02-20 16:30:33.712 8296-8296/com.dkarmazi.viewmodelscoping D/FragmentActivity: Activity 218111434, onDestroy: orientation 1
02-20 16:30:33.815 8296-8296/com.dkarmazi.viewmodelscoping D/FragmentActivity: Activity 158140230, onCreate: orientation 2
02-20 16:30:33.815 8296-8296/com.dkarmazi.viewmodelscoping D/FragmentActivity: Activity 158140230, onCreate: saved string from savedInstanceState SAVED_STATE_218111434
02-20 16:30:33.822 8296-8296/com.dkarmazi.viewmodelscoping D/FragmentActivity: Activity 158140230, onResume: orientation 2
Такое поведение согласуется с выводами здесь, однако я ожидаю библиотека Arch для обработки этого случая, поскольку она довольно стандартна, а блокировка и разблокировка в портретном режиме работает, как и ожидалось.
Есть хорошие идеи по предотвращению уничтожения этого ViewModel
в этом конкретном сценарии?
Протестировано на Nexus 5X, API 27.
РЕДАКТИРОВАТЬ 1: после добавления строки для сохранения в onSaveInstanceState
и проверки, сохраняется ли эта строка во всех действиях, уничтожающих и создающих, я почти уверен, что это ошибка библиотеки.
EDIT 2: Почему это проблема?
Проблема 1: в случае блокировки ландшафта пакет каким-то образом правильно перенаправляется из действия 244798673 в действие 218111434 по адресу 02-20 16:30:33.241
, однако ViewModel
не может сохраняться в этой последовательности действий. Это несовместимо с поведением пакетов, поскольку технически мы все еще находимся в одной и той же области действия.
Проблема 2: Вывод лога блокировки и разблокировки в портретном режиме:
02-20 16:38:10.283 8567-8567/? D/FragmentActivity: Activity 244798673, onCreate: orientation 1
02-20 16:38:10.283 8567-8567/? D/FragmentActivity: Activity 244798673, onCreate: no savedInstanceState
02-20 16:38:10.293 8567-8567/? D/MyViewModel: MyViewModel 55090662: created
02-20 16:38:10.301 8567-8567/? D/FragmentActivity: Activity 244798673, onResume: orientation 1
02-20 16:38:13.459 8567-8567/com.dkarmazi.viewmodelscoping D/FragmentActivity: Activity 244798673, onSaveInstanceState: SAVED_STATE_244798673
02-20 16:38:13.480 8567-8567/com.dkarmazi.viewmodelscoping D/FragmentActivity: Activity 244798673, onStop: orientation 1
02-20 16:38:17.704 8567-8567/com.dkarmazi.viewmodelscoping D/FragmentActivity: Activity 244798673, onResume: orientation 1
ViewModel
сохраняется при блокировке и разблокировке портретной ориентации, что несовместимо с ландшафтным сценарием.