Issue
I am using View Model architecture component to handle the UI states and contain Business logic. It is known it survives a configuration change, but when Don't keep activities option is turned on in Developer options, a new instance of View Model is created. The activity onDestroy is called in both the cases then what is the difference between the two?
I am maintaining a state machine in the View Model and face issue in restoring it since View Model does not survive this. I cannot use Instance state since it has complex objects.
Following logs are from a sample project I tried. This has an Activity TestActivity
which contains a fragment TestFragment
, and TestFragment
contains two fragments TestStateAFragment
and TestStateBFragment
TestActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test)
if(savedInstanceState == null) {
supportFragmentManager.beginTransaction()
.add(R.id.container_fragment_test, TestFragment())
.commitAllowingStateLoss()
}
}
TestFragment.kt
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
Timber.i("onViewCreated")
super.onViewCreated(view, savedInstanceState)
if(savedInstanceState == null) {
childFragmentManager.beginTransaction()
.add(R.id.container_state_a, TestStateAFragment.newInstance())
.commitAllowingStateLoss()
childFragmentManager.beginTransaction()
.add(R.id.container_state_b, TestStateBFragment.newInstance())
.commitAllowingStateLoss()
}
}
TestStateAFragment.kt
private val compositeDisposable: CompositeDisposable = CompositeDisposable()
private lateinit var viewModel : TestStateAViewModel
override fun onCreate(savedInstanceState: Bundle?) {
Timber.i("onCreate")
super.onCreate(savedInstanceState)
viewModel = ViewModelProviders.of(this, TestStateAViewModelFactory())
.get(TestStateAViewModel::class.java)
Timber.i(viewModel.toString())
}
Screen rotation :
(TestStateAFragment.kt:101)#onPause: onPause
(TestStateBFragment.kt:66)#onPause: onPause
(TestFragment.kt:72)#onPause: onPause
(TestActivity.kt:45)#onPause: onPause
(TestStateAFragment.kt:105)#onStop: onStop
(TestStateBFragment.kt:70)#onStop: onStop
(TestFragment.kt:76)#onStop: onStop
(TestActivity.kt:50)#onStop: onStop
(TestActivity.kt:23)#onSaveInstanceState: onSaveInstanceState
(TestFragment.kt:55)#onSaveInstanceState: onSaveInstanceState
(TestStateAFragment.kt:55)#onSaveInstanceState: onSaveInstanceState
(TestActivity.kt:54)#onDestroy: onDestroy
(TestStateAFragment.kt:110)#onDestroyView: onDestroyView
(TestStateBFragment.kt:75)#onDestroyView: onDestroyView
(TestFragment.kt:81)#onDestroyView: onDestroyView
(TestStateAFragment.kt:115)#onDestroy: onDestroy
(TestStateBFragment.kt:80)#onDestroy: onDestroy
(TestFragment.kt:86)#onDestroy: onDestroy
(TestFragment.kt:17)#onAttach: onAttach
(TestFragment.kt:18)#onAttach: TestFragment{984b690 (625e084e-68d8-46d6-9527-dfe2694fa5c1) id=0x7f07004c}
(TestFragment.kt:23)#onCreate: onCreate
(TestStateAFragment.kt:30)#onAttach: onAttach
(TestStateAFragment.kt:31)#onAttach: TestStateAFragment{1ff9e9a (f73d9a4a-4fb0-4e1c-b6b0-220941525ae2) id=0x7f07004d}
(TestStateAFragment.kt:39)#onCreate: onCreate
(TestStateAFragment.kt:44)#onCreate: com.example.lifecycleviewmodel.fragment.states.a.TestStateAViewModel@afc805d
(TestStateBFragment.kt:27)#onAttach: onAttach
(TestStateBFragment.kt:28)#onAttach: TestStateBFragment{908b1a7 (a5fa2bce-9b3b-4474-ae2a-0a891f289f65) id=0x7f07004e}
(TestStateBFragment.kt:33)#onCreate: onCreate
(TestActivity.kt:34)#onStart: onStart
(TestFragment.kt:34)#onCreateView: onCreateView
(TestFragment.kt:40)#onViewCreated: onViewCreated
(TestStateAFragment.kt:67)#onCreateView: onCreateView
(TestStateAFragment.kt:73)#onViewCreated: onViewCreated
(TestStateBFragment.kt:44)#onCreateView: onCreateView
(TestStateBFragment.kt:50)#onViewCreated: onViewCreated
(TestFragment.kt:61)#onStart: onStart
(TestStateAFragment.kt:90)#onStart: onStart
(TestStateBFragment.kt:55)#onStart: onStart
(TestActivity.kt:40)#onResume: onResume
(TestFragment.kt:66)#onResume: onResume
(TestStateAFragment.kt:95)#onResume: onResume
(TestStateBFragment.kt:60)#onResume: onResume
Don't keep activities off :
(TestStateAFragment.kt:101)#onPause: onPause
(TestStateBFragment.kt:66)#onPause: onPause
(TestFragment.kt:72)#onPause: onPause
(TestActivity.kt:45)#onPause: onPause
(TestStateAFragment.kt:105)#onStop: onStop
(TestStateBFragment.kt:70)#onStop: onStop
(TestFragment.kt:76)#onStop: onStop
(TestActivity.kt:50)#onStop: onStop
(TestActivity.kt:23)#onSaveInstanceState: onSaveInstanceState
(TestFragment.kt:55)#onSaveInstanceState: onSaveInstanceState
(TestStateAFragment.kt:55)#onSaveInstanceState: onSaveInstanceState
(TestActivity.kt:30)#onRestart: onRestart
(TestActivity.kt:34)#onStart: onStart
(TestFragment.kt:61)#onStart: onStart
(TestStateAFragment.kt:90)#onStart: onStart
(TestStateBFragment.kt:55)#onStart: onStart
(TestActivity.kt:40)#onResume: onResume
(TestFragment.kt:66)#onResume: onResume
(TestStateAFragment.kt:95)#onResume: onResume
(TestStateBFragment.kt:60)#onResume: onResume
(TestStateAFragment.kt:101)#onPause: onPause
(TestStateBFragment.kt:66)#onPause: onPause
(TestFragment.kt:72)#onPause: onPause
(TestActivity.kt:45)#onPause: onPause
(TestStateAFragment.kt:105)#onStop: onStop
(TestStateBFragment.kt:70)#onStop: onStop
(TestFragment.kt:76)#onStop: onStop
(TestActivity.kt:50)#onStop: onStop
(TestActivity.kt:23)#onSaveInstanceState: onSaveInstanceState
(TestFragment.kt:55)#onSaveInstanceState: onSaveInstanceState
(TestStateAFragment.kt:55)#onSaveInstanceState: onSaveInstanceState
Don't keep activities ON :
(TestStateAFragment.kt:101)#onPause: onPause
(TestStateBFragment.kt:66)#onPause: onPause
(TestFragment.kt:72)#onPause: onPause
(TestActivity.kt:45)#onPause: onPause
(TestStateAFragment.kt:105)#onStop: onStop
(TestStateBFragment.kt:70)#onStop: onStop
(TestFragment.kt:76)#onStop: onStop
(TestActivity.kt:50)#onStop: onStop
(TestActivity.kt:23)#onSaveInstanceState: onSaveInstanceState
(TestFragment.kt:55)#onSaveInstanceState: onSaveInstanceState
(TestStateAFragment.kt:55)#onSaveInstanceState: onSaveInstanceState
(TestActivity.kt:54)#onDestroy: onDestroy
(TestStateAFragment.kt:110)#onDestroyView: onDestroyView
(TestStateBFragment.kt:75)#onDestroyView: onDestroyView
(TestFragment.kt:81)#onDestroyView: onDestroyView
(TestStateAFragment.kt:115)#onDestroy: onDestroy
(TestStateBFragment.kt:80)#onDestroy: onDestroy
(TestFragment.kt:86)#onDestroy: onDestroy
2019-09-18 20:30:10.503 31473-31473/com.example.lifecycleviewmodel W/ActivityThread: handleWindowVisibility: no activity for token android.os.BinderProxy@d382041
(TestFragment.kt:17)#onAttach: onAttach
(TestFragment.kt:18)#onAttach: TestFragment{544b1c5 (625e084e-68d8-46d6-9527-dfe2694fa5c1) id=0x7f07004c}
(TestFragment.kt:23)#onCreate: onCreate
(TestStateAFragment.kt:30)#onAttach: onAttach
(TestStateAFragment.kt:31)#onAttach: TestStateAFragment{ea5f827 (f73d9a4a-4fb0-4e1c-b6b0-220941525ae2) id=0x7f07004d}
(TestStateAFragment.kt:39)#onCreate: onCreate
(TestStateAFragment.kt:44)#onCreate: com.example.lifecycleviewmodel.fragment.states.a.TestStateAViewModel@fa43f72
(TestStateBFragment.kt:27)#onAttach: onAttach
(TestStateBFragment.kt:28)#onAttach: TestStateBFragment{f66bb79 (a5fa2bce-9b3b-4474-ae2a-0a891f289f65) id=0x7f07004e}
(TestStateBFragment.kt:33)#onCreate: onCreate
(TestActivity.kt:34)#onStart: onStart
(TestFragment.kt:34)#onCreateView: onCreateView
(TestFragment.kt:40)#onViewCreated: onViewCreated
(TestStateAFragment.kt:67)#onCreateView: onCreateView
(TestStateAFragment.kt:73)#onViewCreated: onViewCreated
(TestStateBFragment.kt:44)#onCreateView: onCreateView
(TestStateBFragment.kt:50)#onViewCreated: onViewCreated
(TestFragment.kt:61)#onStart: onStart
(TestStateAFragment.kt:90)#onStart: onStart
(TestStateBFragment.kt:55)#onStart: onStart
(TestActivity.kt:40)#onResume: onResume
(TestFragment.kt:66)#onResume: onResume
(TestStateAFragment.kt:95)#onResume: onResume
(TestStateBFragment.kt:60)#onResume: onResume
So where do these two scenarios differ exactly and is there a way to determine which one took place?
Solution
When a configuration change occurs, the Android framework will call onRetainNonConfigurationInstance()
on your Activity
. You can return any object you want to. Then Android will destroy your Activity
and immediate create a new instance. In onCreate()
of the new instance you can call getLastNonConfigurationInstance()
. If the Activity
was recreated due to a configuration change, the object that you returned from onRetainNonConfigurationInstance()
will be returned here. Otherwise the call returns null
. In this case you can tell when your Activity
was recreated due to a configuration change, and when it was (re)started for other reasons.
The developer option "Don't keep activities" is not something that you usually have to deal with, as normal users should never have it enabled. You can use it for testing purposes to ensure that your Activity
properly recovers in case Android decides to kill it off.
In reality, Android usually doesn't kill off individual activities. If Android needs to recover resources from background applications it usually just kills off the entire OS process. However, I have recently seen some situations on some devices where Android does indeed kill off individual activities when an app is in the background. When the user then returns to the app, Android will recreate the Activity
. In this case you will get null
returned from getLastNonConfigurationInstance()
, but you would get a non-null Bundle
in the onCreate()
call and you would also get a non-null Bundle
in the onRestoreInstanceState()
. So you should be able to tell the difference between:
Activity
created for the first timeActivity
instance created after configuration changeActivity
instance recreated after Android killed it off and user returned to it
I'm not exactly sure of your requriements, but this information should help.
Answered By - David Wasser
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.