使用RxPermissions时权限授予后回调未触发,需点击两次按钮跳转Activity
结合你描述的「部分Fragment功能正常、权限授予后无回调、必须点击两次按钮、添加线程切换逻辑无效」的特征,按照排查效率从高到低依次处理:
首先排查订阅链是否遗漏onError分支,导致内部异常中断回调
你使用的RxPermissions 0.12版本,内部是通过向宿主的FragmentManager添加一个隐藏的无UI权限Fragment,来承接系统的权限申请回调。当调用request()发起请求时,如果宿主页面已经触发了onSaveInstanceState状态保存(比如ViewPager懒加载页面未完全恢复、页面跳转过程中被用户快速点击),内部执行Fragment事务提交时就会抛出IllegalStateException。
你当前的subscribe方法只传入了处理授权结果的Consumer,完全没有捕获onError的逻辑,这个抛出的异常会直接中断整个订阅链,根本不会触发权限结果的回调。等用户第二次点击按钮时,页面已经恢复到可正常提交事务的状态,回调逻辑就可以正常执行,和你遇到的现象完全匹配。
验证方式很简单,把现有订阅代码改成带错误捕获的版本,打印异常日志即可快速确认:new RxPermissions((FragmentActivity) context) .request(Manifest.permission.CAMERA) .subscribe(granted -> { if (granted) { Intent packageReceiveIntent = new Intent(context, ReceivePackageActivity.class); context.startActivity(packageReceiveIntent); } }, throwable -> { Log.e("RxPermDebug", "权限申请流程异常", throwable); });你之前尝试添加的线程切换写法没有效果,本质就是因为这个问题和订阅线程完全无关,异常是在主线程执行Fragment事务时抛出的,切到计算线程根本不会修复这个错误。
其次排查RxPermissions绑定的页面实例是否为当前活跃状态
部分Fragment正常、部分异常的差异,大概率来自出问题的Fragment里,传入的context并不是当前活跃的FragmentActivity实例,而是已经被销毁的旧页面引用,导致RxPermissions绑定到了已经不可交互的旧页面上。系统返回权限结果时,因为旧页面已经销毁,对应的Consumer逻辑自然不会触发。第二次点击按钮时重新获取了当前活跃的Activity实例,才会正常收到回调。
修复方式:在Fragment内不要通过强转传入的context拿Activity,统一用requireActivity()获取当前宿主实例,避免上下文传递过程中拿到非活跃的页面引用。针对嵌套/ViewPager场景的Fragment,调整RxPermissions的绑定方式
如果出问题的Fragment是嵌套在ViewPager、或者通过childFragmentManager加载的多级嵌套Fragment,直接绑定到宿主Activity的FragmentManager时,很容易因为页面层级的状态同步问题,触发前面提到的Fragment事务提交异常。
这种情况不要把Activity实例传给RxPermissions,直接利用它支持传入FragmentManager的构造方法,把RxPermissions绑定到当前Fragment自己的childFragmentManager上:// 替换原来的new RxPermissions(requireActivity())写法 new RxPermissions(getChildFragmentManager()) .request(Manifest.permission.CAMERA) .subscribe(/* 原有业务逻辑 */);这种绑定方式可以避免和宿主Activity的Fragment状态保存逻辑产生冲突,从根源上减少事务提交失败的概率。
最后排查订阅是否缺少生命周期绑定
如果你在Fragment内没有管理订阅对应的Disposable,当Fragment因为配置变更(比如旋转屏幕)、页面重建时,之前建立的订阅会被直接销毁。第一次发起的权限请求返回结果时,因为已经没有对应的订阅者,不会触发Consumer逻辑,等你第二次点击按钮时重新建立新的订阅,才会正常收到结果。
修复方式:给所有的RxPermissions订阅绑定对应Fragment的生命周期事件,在页面销毁时主动切断订阅,避免页面重建导致的订阅丢失。
注意:不要随意添加无意义的线程切换逻辑,RxPermissions的权限回调本身就是在主线程派发的,切线程既不会解决内部Fragment事务的异常,还可能带来跨页面跳转的线程安全问题。
内容的提问来源于stack exchange,提问作者petrrr33

