As mentioned in the previous article, although the viewModel is simpler than onSaveInstanceState, the viewModel can only rebuild the page to maintain data after screen rotation and language switch (i.e. configuration change), and cannot recover the data when the page is accidentally destroyed (the viewModel will also rebuild). And that’s what onSaveInstanceState does. In terms of accidental destruction, we temporarily understand it as destruction reconstruction caused by non-configuration changes, such as out of memory and other scenarios.
Jetpack note code
The source of this article is based on SDK 29
Problem of repetition
Introducing dependencies:
def lifecycle_version = "2.2.0"
Extensions include Lifecycles, LiveData, ViewModel
implementation "android.arch.lifecycle:extensions:$lifecycle_version"
Copy the code
Create a ViewModel,
class CommonViewModel extends ViewModel {
public MutableLiveData<String> text = new MutableLiveData<>();
}
Copy the code
Used in layout files,
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="com.holiday.jetpackstudy.viewmodel_livedata.CommonViewModel" />
<variable
name="commonViewModel"
type="CommonViewModel" />
</data>
<TextView
android:id="@+id/tv_text"
android:text="@{commonViewModel.text}"
android:textSize="@dimen/tv_text_size" />
</layout>
Copy the code
Used in act,
class ViewModelActivity extends BaseActivity {
CommonViewModel mCommonViewModel;
String mTime;
void onCreate(Bundle savedInstanceState) {
// Pass this, create viewModel based on act
mCommonViewModel = ViewModelProviders.of(this).get(CommonViewModel.class);
mBinding.setCommonViewModel(mCommonViewModel);
// Observe data changes
mCommonViewModel.text.observe(this.new Observer<String>() {
@Override
public void onChanged(String s) {
/ / update the UImBinding.tvText.setText(s); }});// After the page is accidentally destroyed, the ViewModel is rebuilt
QrLog.e(String.valueOf(mCommonViewModel.hashCode()));
if (null == savedInstanceState) {
mTime = String.valueOf(System.currentTimeMillis() / 1000);
QrLog.e("OnCreate get current time =" + mTime);
} else {
mTime = savedInstanceState.getString("test");
QrLog.e("OnCreate restore last time ="+ mTime); }}
void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Store the creation time of the act when the page was accidentally destroyed
outState.putString("test", mTime); }}Copy the code
In the onCreate method, we added savedInstanceState and rewrote the onSaveInstanceState method to store the time. So how to simulate the accidental destruction of the page? We can select the developer option not to keep the activity – destroy each activity after the user leaves. Run the app and press the home button to cause the page to be accidentally destroyed. Then go back to the page and view the log:
As you can see, the viewModel does not hold data very well when the page is accidentally destroyed.
To solve
If you want the ViewModel to hold data in the event of an accidental page destruction, you can use it in combination with SavedStateHandle. Create a new ViewModel,
class SavedStateViewModel extends ViewModel {
// SavedStateHandle needs to be referenced
private SavedStateHandle mHandle;
public SavedStateViewModel(SavedStateHandle handle) {
mHandle = handle;
Object text = mHandle.get("text");
if (null == text) {
String time = String.valueOf(System.currentTimeMillis() / 1000);
mHandle.set("text", time);
QrLog.e("SavedStateViewModel initializes data =" + time);
} else {
QrLog.e("SavedStateViewModel restore data ="+ text); }}}Copy the code
Then add to act:
//ViewModelActivity.java
class ViewModelActivity extends BaseActivity {
SavedStateViewModel mSavedStateViewModel;
void onCreate(Bundle savedInstanceState) {
/ / this way create incoming SavedStateViewModelFactory
mSavedStateViewModel = ViewModelProviders
.of(this.new SavedStateViewModelFactory(getApplication(), this))
.get(SavedStateViewModel.class);
QrLog.e("mSavedStateViewModel hashCode = "+ mSavedStateViewModel.hashCode()); }}Copy the code
Run to the page, hit the home button to trigger accidental destruction, and then go back to the page to view the logs,
It turns out that although mSavedStateViewModel is no longer the same instance, the data is recoverable.
As for the principle, general idea is in SavedStateViewModelFactory,
//SavedStateViewModelFactory.java
<T extends ViewModel> T create(String key, Class<T> modelClass) {
/ / with the aid of SavedStateHandleController SavedStateHandle storage
SavedStateHandleController controller = SavedStateHandleController.create(
mSavedStateRegistry, mLifecycle, key, mDefaultArgs);
// Pass SavedStateHandle when creating the ViewModel
T viewmodel = constructor.newInstance(controller.getHandle());
}
Copy the code
In SavedStateHandleController,
//SavedStateHandleController.java
static SavedStateHandleController create(SavedStateRegistry registry, Lifecycle lifecycle, String key, Bundle defaultArgs) {
// Find the Bundle using the key generated by the act class name
Bundle restoredState = registry.consumeRestoredStateForKey(key);
// Restore data through the Bundle
SavedStateHandle handle = SavedStateHandle.createHandle(restoredState, defaultArgs);
/ / packaged into SavedStateHandleController returns
SavedStateHandleController controller = new SavedStateHandleController(key, handle);
return controller;
}
//SavedStateHandle.java
static SavedStateHandle createHandle(Bundle restoredState,Bundle defaultState) {
if (restoredState == null && defaultState == null) {
return new SavedStateHandle();
}
// Data recovery
Map<String, Object> state = new HashMap<>();
ArrayList keys = restoredState.getParcelableArrayList(KEYS);
ArrayList values = restoredState.getParcelableArrayList(VALUES);
for (int i = 0; i < keys.size(); i++) {
state.put((String) keys.get(i), values.get(i));
}
// Although SavedStateHandle is no longer the same instance, the data has been recovered
return new SavedStateHandle(state);
}
Copy the code
Essentially, data is recovered through serialization and deserialization of bundles.
reference
- B – Jetpack course ViewModelSavedState
This article is formatted using MDNICE