I have an activity that implements an observer inside the OnCreate for a MutableLiveData variable that is in a ViewModel.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
subscribeDialogFragmentManualInput()
}
private fun subscribeDialogFragmentManualInput() {
this.sharedViewModel.inputBarcode.observe(this) { inputValue ->
postInput(inputValue)
}
}
My activity is always in landscape mode (default in Manifest) and when a button is pressed a DialogFragment is opened and it changes the rotation to Portrait, when it is closed the activity returns to Landscape mode.
private fun showInvoiceInputDialog() {
val inputDialog = InvoiceInputDialogFragment
val transaction = supportFragmentManager.beginTransaction()
inputDialog.show(transaction, InvoiceInputDialogFragment.TAG)
}
class InvoiceInputDialogFragment : DialogFragment() {
lateinit var binding: DialogFragmentInvoiceInputBinding
private val sharedViewModel by sharedViewModel<ManualInvoiceInputViewModel>()
private var invoiceInput: String = ""
...
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
setStyle(
STYLE_NORMAL,
R.style.FullScreenDialog
)
binding = DataBindingUtil.inflate(
inflater,
R.layout.dialog_fragment_invoice_input,
container,
false
)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
...
activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
setViews()
}
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
if(invoiceInput.isNotEmpty()) {
sharedViewModel.sendInputInvoice(invoiceInput)
}
}
private fun setViews() {
binding.btConfirmInput.setOnClickListener {
invoiceInput = binding.scannerManualInput.text
dismiss()
}
}
}
This flow makes the observer triggered multiple times making my app having and undesirable comportment because the livedata result open a dialog and it is displayed multiple times.
I want to call postInput
method only one time after DialogFragment is closed.
Thanks!
CodePudding user response:
LiveData
is by default setup in near circular logic. This raises some problems when the view updates LiveData
the LiveData
will then want to update the view again, creating an infinite loop. Although you are doing it indirectly by provoking the Android Lifecycle
to re-create your Activity
and when its re-created it attaches another observer thus re-emitting the old value.
You have 2 options:
Save the fact that the dialog was already called in the savedInstanceState Bundle
overide fun onSaveInstanceState(savedInstanceState : Bundle?) {
// Save the user's current game state
savedInstanceState.putBoolean("dialogCausedRecreate", dialogWasCreated);
super.onSaveInstanceState(savedInstanceState);
}
//in onCreate
this.sharedViewModel.inputBarcode.observe(this) { inputValue ->
if(!savedInstanceState.getBoolean("dialogCausedRecreate",false)){
postInput(inputValue)
}
}
OR
Create a new wrapper class that can keeps track if its been emitted. Here is an example LiveData prevent receive the last value when start observing
The accepted answer will have you wrap your barcode inside an Event class and that will allow you to tell if it already been emitted previously.