不能使用Observer模式在DialogFragment和Activity之间进行通信?

时间:2019-08-20 08:40:13

标签: dialog rx-java android-dialogfragment observer-pattern android-livedata

按下按钮打开一个单独的输入窗口时,将显示结果吐司。

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"
}
}

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

此示例代码在正常情况下有效。但是,按照以下步骤操作,烤面包不会漂浮。

  1. 开发人员选项-不保留活动启用
  2. 打开TestPopup,然后输入您的文本。 (请勿按“确定”按钮)
  3. 按下主屏幕按钮将应用移至后台
  4. 该应用已被系统杀死。
  5. 重新激活应用程序(就像在应用程序列表中单击一个应用程序一样)

在这种情况下,我输入的文本仍保留在屏幕上,但是当我按“确定”按钮时什么也没有发生。

我当然知道发生这种情况是因为在活动结束时,活动与对话框之间的观察关系已经结束。

大多数代码使用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 个答案:

答案 0 :(得分:1)

您对问题的理解是正确的,除了问题是任何配置更改(包括屏幕旋转)都会发生。您无需使用开发人员模式即可重现问题。尝试以下示例:

  1. 打开TestPopup,然后输入您的文本。 (请勿按“确定”按钮)
  2. 旋转屏幕
  3. 看到吐司消息没有弹出。

还要注意,您的“观察者模式”实现不是正确的观察者模式。观察者模式有一个主题和一个观察者。在您的实现中,活动同时充当主题观察者。对话框不参与此观察者模式,而使用.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()
    }
}