当 DialogFragment 打开屏幕旋转变化时,Observer 被多次调用

Observer is called multiple times when DialogFragment is opened with screen rotation changes

我有一个 activity,它在 OnCreate 中为 ViewModel 中的 MutableLiveData 变量实现了一个观察者。

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    ...
    subscribeDialogFragmentManualInput()
}

private fun subscribeDialogFragmentManualInput() {
    this.sharedViewModel.inputBarcode.observe(this) { inputValue ->
        postInput(inputValue)
    }
}

我的 activity 始终处于横向模式(Manifest 中的默认模式),当按下按钮时,会打开一个 DialogFragment 并将旋转更改为纵向,当它关闭时 activity returns 到横向模式。

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()
        }
    }
}

此流程使观察者多次触发,使我的应用出现不良行为,因为实时数据结果会打开一个对话框并多次显示。

我只想在 DialogFragment 关闭后调用 postInput 方法一次。

谢谢!

LiveData 默认设置为近循环逻辑。当视图更新 LiveData 时,这会引发一些问题,然后 LiveData 将再次更新视图,从而创建无限循环。尽管您是通过激发 Android Lifecycle 重新创建您的 Activity 来间接执行此操作,并且当它重新创建时它会附加另一个观察者,从而重新发出旧值。

您有 2 个选择:

保存已在 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)
    }
}

创建一个新的包装器 class 可以跟踪它是否已发出。 这是一个例子

接受的答案会让您将条形码包裹在一个事件中 class,这样您就可以判断它之前是否已经发出。