•Save/restoreminimalstateacrossprocess
death
•Executedataloadingasynchronouslybasedon
state
–Shouldbe keptacrossconfigurationchanges
•Minimize„movingparts”
–Mutationsshouldbe controlled
–Mutationsshouldbe observed
Processdeath
(aka: howAndroid appsactuallywork)
Core App Quality Guidelines
From https://developer.android.com/docs/quality-guidelines/core-app-quality#fn
„When returning to the foreground, the app
must restore the preserved state and any
significant stateful transaction that was
pending, such as changes to editable fields,
game progress, menus, videos, and other
sections of the app.
How to induce process death?
•Step 1: put app in background with HOME
•Step 2: press „Terminate application”
•Step 3: restart app from launcher
For apps you don’t own
https://play.google.com/store/apps/details?id=me.empirical.android.application.fillme
mory&hl=en
What needs to be persistedacross
processdeath?
•Navigation state is already managed by the
system on Android out of thebox
–Empty ctor + using intent extras / fragment arguments
•Screen state is partially managed by the system
–Views with IDs have their state persisted
–Complex state (f.ex. RecyclerView selection) are not
persisted automatically
–Dynamically added views should be recreatable(!)
What SHOULDN’T be persisted?
•Data
–Bundle has a size limit
–Data should be fetched asynchronously, off the UI
thread
•Transient state
–„Loading” state: computed from progress of side-
effect („something is happening”, but is it really?)
Example for saving/restoringstate
(the„old way”)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (savedInstanceState != null) {
selectedSportId = savedInstanceState.getLong( "selectedSportId")
selectedPosition = savedInstanceState.getInt( "selectedPosition")
selectedTags.clear()
selectedTags.addAll(
savedInstanceState.getStringArrayList( "selectedTags"))
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putLong("selectedSportId", selectedSportId)
outState.putInt("selectedPosition", selectedPosition)
outState.putStringArrayList( "selectedTags", ArrayList(selectedTags))
}
Loading data
•Asynchronous loading should either begin on
initialization, or when observed
•Data can be loaded via a transformationchain
fromtheobservablesourcethatstores the
state –changes trigger new dataload
(switchMap, flatMapLatest)
ViewModel
•Storedacrossconfigurationchangesin a
ViewModelStore (ComponentActivity, Fragment,
and NavBackStackEntryareViewModelStoreOwner)
•Theyhavetheirownlifecycleforwhenthe
ViewModelStoreis destroyed(onCleared(),
viewModelScope)
•Stateand asynchronousoperationsgo here
Otherresources
•Understand Kotlin Coroutines on Android (Google
I/O'19)
•LiveDatawith Coroutines and Flow (Android Dev
Summit '19)
•Android Coroutines: How to manage async tasks in
Kotlin -Manuel Vivo
•Building Reactive UIs with LiveDataand
SavedStateHandle(or equivalent approaches like Rx)