实现一个 Coroutine 版 DialogFragment
-
Activity 临终前会在
onSaveInstanceState()中保存DialogFragment的状态FragmentManagerState; -
重建后的 Activity,在
onCreate()中根据savedInstanceState所给予的FragmentManagerState自动重建DialogFragment并且show()出来
总结起来流程如下:
旋转屏幕 --> Activity.onSaveInstanceState() --> Activity.onCreate() --> DialogFragment.show()
重建后的 FragmentDialog 其成员变量 _cont 尚未初始化,此时对其访问自然发生 crash。
那么如果不使用 lateinit 就没问题了呢? 我们尝试引入 RxJava 对其进行改造
3. 二次改造: RxJava + Coroutine
============================
通过 RxJava 的 Subject 避免了 lateinit 的出现,防止 crash :
//build.gradle
implementation “io.reactivex.rxjava2:rxjava:2.2.8”
新的 AlertDialogFragment 代码如下:
class AlertDialogFragment : DialogFragment() {
private val subject = SingleSubject.create()
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val listener = DialogInterface.OnClickListener { _: DialogInterface, which: Int ->
subject.onSuccess(which)
}
return AlertDialog.Builder(requireContext())
.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) }
}
}
显示 dialog 时,通过订阅 SingleSubject 响应 listener 的回调。
经过修改,旋转屏幕后点击 Dialog 按钮时没有再发生 crash 的现象,但是仍然存在问题:屏幕旋转后我们无法接收到 Dialog 的返回值,即没有按预期的那样显示下面的日志
Log.d(“AlertDialogFragment”, “$result Clicked”)
当 DialogFragment 重建后, Subject 也跟随重建,但是丢失了之前的 Subscriber ,所以点击按钮后,Rx 的下游无法响应。
有没有办法让 Subject 重建时能够恢复之前的 Subscriber 呢? 此时想到了借助 onSaveInstanceState 。
想要 subject 作为 Fragment 的 arguments 保存到 savedInstanceState,必须是一个 Serializable 或者 Parcelable,
4. 三次改造: SerializableSingleSubject
===================================
令人高兴的是,查阅 SingleSubject 源码后发现其成员变量全是 Serializable 的子类,也就是只要 SingleSubject 实现 Serializable 接口就可以存入 savedInstanceState 了, 但可惜它不是,而且它是一个 final 类,只好拷贝源码出来,自己实现一个 SerializableSingleSubject :
/**
- 实现 Serializable 接口并增加 serialVersionUID
*/
public final class SerializableSingleSubject extends Single implements SingleObserver, Serializable {
private static final long serialVersionUID = 1L;
final AtomicReference<SerializableSingleSubject.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;
// 以下代码同 SingleSubject,省略
基于 SerializableSingleSubject 重写 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.OnClickListener { _: DialogInterface, which: Int ->
subject.onSuccess(which)
}
return AlertDialog.Builder(requireContext())
.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 恢复之前的 Subscriber ,下游顺利收到消息,日志正常输出。
需要注意的是,此时仍然存在隐患:屏幕旋转后,点击 dialog 虽然可以正常获得返回值,但是此时协程恢复的上下文是前一次 launch { ... } 的闭包
GlobalScope.launch {
val frag = AlertDialogFragment()
val result = frag.showAsSuspendable(supportFragmentManager)
Log.d(“AlertDialogFragment”, “$result Clicked on $frag”)
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。





既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
最后
为了方便有学习需要的朋友,我把资料都整理成了视频教程(实际上比预期多花了不少精力)
当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。
- 无论你现在水平怎么样一定要 持续学习 没有鸡汤,别人看起来的毫不费力,其实费了很大力,这四个字就是我的建议!!
- 我希望每一个努力生活的IT工程师,都会得到自己想要的,因为我们很辛苦,我们应得的。
当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。
无论你现在水平怎么样一定要 持续学习 没有鸡汤,别人看起来的毫不费力,其实费了很大力,没有人能随随便便成功。
加油,共勉。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
我希望每一个努力生活的IT工程师,都会得到自己想要的,因为我们很辛苦,我们应得的。
当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。
无论你现在水平怎么样一定要 持续学习 没有鸡汤,别人看起来的毫不费力,其实费了很大力,没有人能随随便便成功。
加油,共勉。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
更多推荐


所有评论(0)