KunMinX/MVI-Dispatcher

BaseFragment的addOnBackPressed()存在问题

Closed this issue · 6 comments

##场景
新建一个fragment继承自BaseFragment,覆写onBackPressed,返回false,程序会死循环
##原因:
如果fragment中的onBackPressed返回false,那么事件会重新分发给该fragment,就死循环crash了
##目标:
当onBackPressed return false时,再次分发时过滤该OnBackPressedCallback

如果不存在enable的OnBackPressedCallback的话,Dispatcher会走ComponentActivity.super.onBackPressed(),事件交给Activity里的逻辑处理。我们在fragment中是否有办法走到那里?

image
这里是否可以这么处理,先将callback置为false,再分发,结束分发后重新置为true

感谢你的反馈,

如今想了想,之前 OnBackPressed 提供返回值的写法,其实并不好,不如另外提供一个 allowBackPress 的开关,默认是 true,如果 false,就不监听 backPress 事件,如此使用时更贴近原先 activity 栈返回的方式,也更符合用户预期,

具体见最新代码,

假设backPressed的逻辑:举个简单的例子,1.比如点击第一下的话弹出吐司,3秒内点击第二次的话转交activity处理; 2.比如在执行某些逻辑(弹窗/下载/计算/动画等等)的时候fragment拦截掉,当前fragment空闲的时候收到back事件转交activity处理
从源码上来看的话,
fun1:allowBackPress=false 时将 mBackPressedCallback 置为unable

fun2: 当onBackPressed return false时将 mBackPressedCallback 置为unable
从结果上看一样,逻辑上感觉fun2更符合响应思维,也可以省一些监听;但是fun1的分发效率(o1)上会高于fun2(o2),且fun2在响应时会做一些逻辑,理论上fragment栈数据量越大,两者的时间差越大~
不过两种方式都存在同一个问题。正常预期感觉是当前fragment不做逻辑时,交由activity做back逻辑。 但是只要使用了BackPressedDispatcher,那back事件就会优先在mOnBackPressedCallbacks(由于在每个fragment的base里创建了callback,这里和我们的fragment栈对应)里便利寻找enable的callback。所以当前activity存在多个fragment且当前fragment return false时,会执行栈里上一个fragment的backPressedCallback。比如AFragment转跳Bfragment,AFragment的back事件是finishActivity,而BFragment的back事件return false,那么会转而执行AFragment的finishActivity事件,而不是ComponentActivity.super.onBackPressed (即Activity的backPressed,popBackStackImmediate)
感觉要实现activity的backPress有点麻烦,原因是本身androidx的OnBackPressedDispatcher源码里就只有分发没有考虑消费的情况,真要实现的话
1.模仿自己通过baseActivity维护一个onBackPressedCallback的ArrayDeque,每个我们添加到OnBackPressedDispatcher的callback都添加到这个队列中。当pop的不可用时,将整个队列都置为unable,然后再次分发让activity去完成逻辑,完成后再把队列置为enable
2.我们的每个activity都实现完整的fragmentPop,activityFinish 以及别的相关逻辑。。。感觉这个方法不太ok,每个activity都要去实现那些理应统一处理的逻辑,但是如果这些message的key和value在base里写死的话,又耦合性太强了。。。

感谢你的反馈,

如今想了想,之前 OnBackPressed 提供返回值的写法,其实并不好,不如另外提供一个 allowBackPress 的开关,默认是 true,如果 false,就不监听 backPress 事件,如此使用时更贴近原先 activity 栈返回的方式,也更符合用户预期,

具体见最新代码,

如果有A,B,C顺序转跳的3个fragment,C的allowBackPress置为false的话,backPressed事件还是会分发到BFragment的。只有OnBackPressedDispatcher的mOnBackPressedCallbacks不存在enable的callback,才会走Activity的backPressed(ComponentActivity的onBackPressed实质上是调用Dispatcher分发事件,只有call全部无效才能走到)。
image

是的,这几天也在想,到底怎么设计比较好一点,

在 activity 作为页面的 App 中,当触发虚拟返回键时,OnBackPressed 会被执行,super.OnBackPressed 中隐含 finish 的效果,
如果我们想在 OnBackPressed 节点做些操作、并且禁止返回,通常是会重写 OnBackPressed 并且不调用 super,

然后目前环境转变为,fragment 作为页面的 app 中,activity 只是最终的容器,因而 fragment 的 OnBackPressed 也是类似写法,例如 super.OnBackPressed 默认提供 nav.navigateUp,如果禁止返回,可以重写 OnBackPressed 并且不调用 super,

由于在单 activity app 中,activity 通常只存在一个,或为数不多个,因而如果需要 activity 本身来 finish,那么可以通过当前 fragment 的 OnBackPressed 中发送消息给 activity,由 activity 在观察者中自行决定关闭逻辑,

于是去掉 allowBackPress 设计,和 activity 作为页面的情况保持一致,