无法使用观察者模式在 DialogFragment 和 Activity 之间进行通信?

Can't communication between DialogFragment and Activity using Observer pattern?

当你按下按钮打开一个单独的输入window,有一个功能来显示结果toast。

class MainActivity : AppCompatActivity() {

val disposable = CompositeDisposable()

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    button.setOnClickListener {
        val f = TestPopup()
        usingRxJava(f)
        //usingLiveData(f)
    }
}

private fun usingRxJava(f: TestPopup) {
    val subject = SingleSubject.create<String>()
    f.show(supportFragmentManager, "TAG")
    button.post {
        f.dialog.setOnDismissListener {
            val str = f.arguments?.getString(TestPopup.TEST_KEY) ?: ""
            subject.onSuccess(str)
        }
    }
    subject.subscribe({
        Toast.makeText(this, "Accept : $it", Toast.LENGTH_SHORT).show()
    }, {

    }).addTo(disposable)
}

private fun usingLiveData(f: TestPopup) {
    val liveData = MutableLiveData<String>()
    f.show(supportFragmentManager, "TAG")
    button.post {
        f.dialog.setOnDismissListener {
            val str = f.arguments?.getString(TestPopup.TEST_KEY) ?: ""
            liveData.postValue(str)
        }
    }
    liveData.observe(this, Observer {
        Toast.makeText(this, "Accept : $it", Toast.LENGTH_SHORT).show()
    })
}

override fun onDestroy() {
    disposable.dispose()
    super.onDestroy()
}
}

DialogFragment

class TestPopup : DialogFragment() {

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    return inflater.inflate(R.layout.dialog_test, container, false)
}

override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)
    button_test.setOnClickListener {
        val arg = Bundle()
        arg.putString(TEST_KEY, edit_test.text.toString())
        arguments = arg
        dismiss()
    }
}

companion object {
    const val TEST_KEY = "KEY"
}
}

(示例项目 Url : https://github.com/heukhyeon/DialogObserverPattern

此示例代码适用于正常情况。但是,经过以下程序后吐司并没有漂浮起来。

  1. 开发者选项 - 不要保持活动启用
  2. 打开 TestPopup,然后输入您的文本。 (不要按确定按钮)
  3. 按主页按钮将应用移至后台
  4. 应用被系统杀掉
  5. 重新激活应用程序(如点击应用程序列表中的应用程序)

在这种情况下,我输入的文本保留在屏幕上,但当我按下 OK 按钮时没有任何反应。

我当然知道会发生这种情况,因为在 activity 结束时 activity 和对话框之间的观察关系结束了。

大多数代码使用 Activity 中该对话框的回调接口的实现来处理这种情况。

override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)
    button_test.setOnClickListener {
        val input = edit_test.text.toString()
        (activity as MyListener).inputComplete(input)
        dismiss()
    }
}

class MainActivity : AppCompatActivity(), TestPopup.MyListener {

override fun inputComplete(input: String) {
    Toast.makeText(this, "Accept : $input", Toast.LENGTH_SHORT).show()
}
}

但我认为这是一种不符合Observer模式的方式,我想尽可能使用Observer模式来实现。

我正在考虑从 FragmentManager 获取 Fragment 并在 onCreate 再次订阅,但我认为有更好的方法。

有人可以帮助我吗?

您对问题的理解是正确的,除了任何配置更改都会出现问题,包括屏幕旋转。您可以在不使用开发人员模式的情况下重现问题。例如试试这个:

  1. 打开 TestPopup,然后输入您的文本。 (不要按确定按钮)
  2. 旋转屏幕
  3. 看到 toast 消息没有弹出。

另请注意,您的 "observer pattern" 实施不是适当的观察者模式。观察者模式有一个 subject 和一个 observer。在您的实现中,activity 同时充当 subjectobserver。对话框不参与此观察者模式,使用 .setOnDismissListener 只是另一种形式的侦听器模式。

为了在 Fragment(主题)和 Activity(观察者)之间实现观察者模式,Activity 需要按照您的建议使用 FragmentManager 获取 Fragment 的引用.我建议使用视图模型并在视图层和视图模型层之间建立观察者模式。

RxJava 示例:

//MainViewModel.kt
class MainViewModel: ViewModel() {

    val dialogText = PublishProcessor.create<String>()

    fun postNewDialogText(text: String) {
        dialogText.onNext(text)
    }
}
// Activity
val disposable = CompositeDisposable()

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    val viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)

    viewModel.dialogText.subscribe {
        Toast.makeText(this, "Accept : $it", Toast.LENGTH_SHORT).show()
    }.addTo(disposable)

    button.setOnClickListener {
        TestPopup().show(supportFragmentManager, "TAG")
        // usingRxJava(f)
        // usingLiveData(f)
    }
}

override fun onDestroy() {
    disposable.dispose()
    super.onDestroy()
}
// Dialog Fragment
override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)

    // Important!! use activity when getting the viewmodel.
    val viewModel = ViewModelProviders.of(requireActivity()).get(MainViewModel::class.java)

    button_test.setOnClickListener {
        viewModel.postNewDialogText(edit_test.text.toString())
        dismiss()
    }
}