Reputation: 6444
Lifecycle v 2.3.1
Short Story: ViewModel
is not cleared when DialogFragment
onDestroy
is called. That's it.
In my MainActivity, I instantiate and show a DialogFragment on button press:
private fun showConfirmDialog(message: String) {
MyConfirmDialog.getInstance(message)
.show(supportFragmentManager, MyConfirmDialog.TAG)
}
my dialog fragment:
class MyConfirmDialog: DialogFragment() {
companion object {
val TAG:String = MyConfirmDialog::class.java.simpleName
const val ARGS_MESSAGE = "message"
fun getInstance(message:String): MyConfirmDialog {
val dialog = MyConfirmDialog()
dialog.message = message
val b = Bundle()
b.putString(ARGS_MESSAGE, message)
dialog.arguments = b
return dialog
}
}
private lateinit var message:String
private val vm: MyConfirmDialogViewModel by activityViewModels()
private lateinit var binding: MyConfirmDialogBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
message = requireArguments().getString(ARGS_MESSAGE, "")
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = MyConfirmDialogBinding.inflate(inflater, container, false)
with(binding) {
btnDone.setOnClickListener {
vm.doTheThing(message)
}
btnCancel.setOnClickListener {
dismiss()
}
}
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
/**
* Observer immediately fires on second fresh instance of this dialog.
* regardless of whether observed with this or viewLifecycleOwner. Thus before
* it can even be shown, this second unique dialog is immediately dismissed.
* The observer shouldn't receive an update until after btnDone is clicked.
*/
vm.doTheThing.observe(viewLifecycleOwner, {
Log.e(TAG, "observed doTheThing")
dismiss()
})
}
override fun onDestroy() {
super.onDestroy()
Log.e(TAG, "onDestroy")
//VM NOT CLEARED
}
}
I call showConfirmDialog
a second time from my MainActivity, a second dialog is instantiated, but the VM is still happily sitting resident and NOT cleared, despite the first dialog confirming onDestroy
having been called.
Thus is appears I can never use a DialogFragment
with a ViewModel
, if said dialog will be instantiated more than once.
This feels like a bug in Android lifecycle 2.3.1
Upvotes: 0
Views: 477
Reputation: 6444
I think I found my own answer. I was, in fact, instantiating the VM like this:
private val vm: MyConfirmDialogViewModel by activityViewModels()
Had to change to:
private val vm: MyConfirmDialogViewModel by viewModels()
Apparently, the choice of delegate is absolutely critical. Using the first delegate (by activityViewModels()
) was automatically scoping the VM lifetime to the lifetime of the Activity instead of the Dialog. Thus even when the dialog was destroyed the VM was not cleared.
I thought scoping the VM's lifecycle was handled when registering the observer (e.g. observe(this, {})
vs. observe(viewLifecycleOwner, {})
), but apparently the choice of these two targets has only to do with whether you intend to override onCreateDialog
. To me, it's a confusing mess.
It would certainly be a better world if I didn't have to know absolutely everything about lifecycles and all their intricacies just to use the SDK properly.
Upvotes: 2