Kotlin|【Kotlin协程】当DialogFragment遇上Coroutine
文章图片
Android对话框有多种实现方法,目前比较推荐的是DialogFragment
,相对于直接使用AlertDialog
来说,可以避免屏幕旋转会的消失。但是其建立在回调基础上的API使用起来并不友好。好在有RxJava、Coroutine等优秀的工具,我们可以对其进行一番改造。
基于Coroutine+RxJava的改造 build.gradle
dependencies {
// 省略implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$latest_version"
implementation 'io.reactivex.rxjava2:rxjava:$latest_version'
implementation 'io.reactivex.rxjava2:rxkotlin:$latest_version'
implementation 'io.reactivex.rxjava2:rxandroid:$latest_version'// 省略
}kotlin {
experimental {
coroutines "enable"
}
}
继承DialogFragment
class AlertDialogFragment : DialogFragment() {private val subject = SingleSubject.create()override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val listener = { _: DialogInterface, which: Int ->
subject.onSuccess(which)
}return AlertDialog.Builder(activity)
.setTitle("Title")
.setMessage("Message")
.setPositiveButton("Ok", listener)
.setNegativeButton("Cancel", listener)
.create()
}suspend fun showAsSuspendable(fm: FragmentManager, tag: String? = null) = suspendCoroutine { cont ->
show(fm, tag)
subject.subscribe { it -> cont.resume(it) }
}
}
使用
button.setOnClickListener {
launch(UI) {
val result = AlertDialogFragment().showAsSuspendable(supportFragmentManager)
Log.d("AlertDialogFragment", "$result Clicked")
}
}
屏幕旋转问题 当屏幕旋转时,会返现上述设置的Listener失效,只要理解了Fragment 和 Activity 生命周期就会知道问题的原因:
- 旋转屏幕时,Activity将会被重新创建。
- Activity临终前会在
onSaveInstanceState()
中保存 DialogFragment的状态FragmentManagerState
; - 重建后的Activity,在
onCreate()
中会根据savedInstanceState
所给予的FragmentManagerState
自动实例化DialogFragment,并且show()
出来
旋转屏幕-->-Activity.onSaveInstanceState()-->-Activity.onCreate()-->- DialogFragment.show()
协程的改造让
DialogFragment
结果变成同步读取,但其本质上是把suspend后面的代码在编译期变成了回调,可以理解为设置了一个Listener。那问题来了,由于横竖屏导致Fragment的重建,造成Listener丢失,此时点击按钮无法再出现预期log:Log.d("AlertDialogFragment", "$result Clicked")
对于这种情况,一般有两种解决办法:
- 重新设置Listener。特别是对于Listener包含对宿主Activity引用的情况(匿名内部类或者被Activity实现)情况下,由于Activity也重建导致闭包过期,需要更新Listener
- 将Listener通过
arguments
或savedInstanceState
进行保存后恢复
Subject
实现第二种方法SerializableSingleSubject 序列化的
SingleSubject
,可以将其Subscriber
可以内部状态进行保存,并恢复使用/**
* implements Serializable并增加serialVersionUID
*/
public final class SerializableSingleSubject extends Single implements SingleObserver, Serializable {
private static final long serialVersionUID = 1L;
final AtomicReference.SingleDisposable[]> observers;
@SuppressWarnings("rawtypes")
static final SerializableSingleSubject.SingleDisposable[] EMPTY = new SerializableSingleSubject.SingleDisposable[0];
@SuppressWarnings("rawtypes")
static final SerializableSingleSubject.SingleDisposable[] TERMINATED = new SerializableSingleSubject.SingleDisposable[0];
final AtomicBoolean once;
T value;
Throwable error;
// 省略
AlertDialogFragment 基于
SerialzableSingleSubject
,重新实现AlertDialogFragment :class AlertDialogFragment : DialogFragment() {private var subject = SerializableSingleSubject.create()override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
savedInstanceState?.let {
subject = it["subject"] as SerializableSingleSubject
}
}override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val listener = { _: DialogInterface, which: Int ->
subject.onSuccess(which)
}return AlertDialog.Builder(activity)
.setTitle("Title")
.setMessage("Message")
.setPositiveButton("Ok", listener)
.setNegativeButton("Cancel", listener)
.create()
}override fun onSaveInstanceState(outState: Bundle?) {
super.onSaveInstanceState(outState)
outState?.putSerializable("subject", subject);
}suspend fun showAsSuspendable(fm: FragmentManager, tag: String? = null) = suspendCoroutine { cont ->
show(fm, tag)
subject.subscribe { it -> cont.resume(it) }
}
}
重建后通过
savedInstanceState
恢复之前的Subject
/Subscriber
,从而保证点击有效。RxJava版本 当然,也可以脱离协程,仅使用RxJava
fun showAsSingle(fm: FragmentManager, tag: String? = null): Single {
show(fm, tag)
return subject.hide()
}
【Kotlin|【Kotlin协程】当DialogFragment遇上Coroutine】使用时,由
subscribe()
替代挂起函数的调用button.setOnClickListener {
AlertDialogFragment().showAsSingle(supportFragmentManager).subscribe { result ->
Log.d("AlertDialogFragment", "$result Clicked")
}
}
推荐阅读
- 宽容谁
- 我要做大厨
- 增长黑客的海盗法则
- 画画吗()
- 2019-02-13——今天谈梦想()
- 远去的风筝
- 三十年后的广场舞大爷
- 叙述作文
- 20190302|20190302 复盘翻盘
- 学无止境,人生还很长