https://qiita.com/FKbelm/items/f85949dff7a3c0e7781d で試した内容の改善版です。
上の記事では、メモリリークを起こしていたので、ViewModelの力を使って再実装を目指しました。
ViewModelの作成
class MainActivityViewModel() : ViewModel() {
val hogeDialogEvent = MutableLiveData<Unit>()
private val _hogeDialogChannel = Channel<Boolean>()
val hogeDialogChannel = _hogeDialogChannel as SendChannel<Boolean>
private suspend fun showHogeDialog(): Boolean {
// ダイアログを表示
hogeDialogEvent.postValue(Unit)
// suspendで結果を取得
// Channelに値が入るまで待つ
val result = _hogeDialogChannel.receive()
// 結果からなんかやって返す
return TODO(result)
}
}
showHogeDialog
はActivityにダイアログを開くよう通知するhogeDialogEvent
に通知を出して、_hogeDialogChannel.receive()
で結果が入ってくるのを待ちます。
Activityの作成
class MainActivity : AppCompatActivity() {
private val viewModel by lazy {
ViewModelProviders.of(this).get(MainActivityViewModel::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel.hogeDialogChannel.observe(this, Observer {
// ダイアログが存在しないときに開く処理
if (supportFragmentManager.findFragmentByTag(TAG_HOGE) == null) {
supportFragmentManager
.beginTransaction()
.add(AlertDialogFragment(), TAG_HOGE)
.commitAllowingStateLoss()
}
})
}
}
ActivityはDialogFragmentを開く処理のみを担っています。
DialogFragmentの作成
// 大元になるDialogFragment
abstract class ChannelDialogFragment<T> : DialogFragment() {
protected abstract val channel: SendChannel<T>?
}
SendChannel<T>
はcoroutinesのChannel<T>
でのうち、送信機能のみのインターフェイスです。
ここから結果を送信してあげます。
// 実際に使うDialogFragment
class AlertDialogFragment : ChannelDialogFragment<AlertDialogFragmentResult>() {
// ActivityのViewModelを取得し、そこからchannelを取得する
override val channel by lazy {
activity?.let {
ViewModelProviders.of(it).get(MainActivityViewModel::class.java).hogeDialogChannel
}
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val context = this.context ?: return super.onCreateDialog(savedInstanceState)
return AlertDialog.Builder(context).apply {
setTitle(R.string.hoge_title)
setMessage(R.string.hoge_message)
setPositiveButton(R.string.hoge_positive) { _, _ ->
channel?.offer(true) // ViewModelのreceiveが受け取る
}
setNegativeButton(R.string.hoge_negative) { _, _ ->
channel?.offer(false) // ViewModelのreceiveが受け取る
}
}.create()
}
}
ChannelをViewModelに持たせることでActivityの破棄,再生成後にもChannelを容易に再取得できるようになりました。
Channelを直接公開せずにViewModelにインターフェイスを実装させることでも実現できそうですが、複数のDialogFragmentを持ちたいといったことがあった場合はChannelを直接扱う方が楽かなと思いました。
ViewModelの力は大きいですね。
参考
Coroutine時代のDialogFragment
https://qiita.com/idaisuke/items/b4f3c2e0a872544b97d0
Prism 5とReactiveProperty
https://blog.okazuki.jp/entry/2014/05/09/083315
Android Architecture ComponentsのViewModelとDialogFragment
http://sys1yagi.hatenablog.com/entry/2017/08/24/125416