startActivityResult
这个方法其实做Android开发的都应该很熟悉了,但是可能还是有人不知道Fragment里也有这个方法。虽然之前在Fragment里写代码的时候看到过这个方法,也能猜出来能干啥,但是最近才第一次用到,所以总结一下。首先在使用上就不多说了,在Fragment中和在Activity中使用startActivityForResult
的方式是一样的,Fragment也有onActivityResult
方法可供重写,但是记住不要调用getActivity().startActivityForResult
方法,这就是两回事了。
表现
首先看在使用上会有什么问题。我们遇到的比较典型的就是在一个Activity中嵌套一个Fragment,在两个页面里都有startActivityForResult
的调用,也都重写了onActivityResult
,然后调用方法并收到返回的结果。这时候我们会发现在各自页面里都收到返回的数据,也能对上对应的requestCode,但是,在Activity里也能收到在Fragment里的请求结果,但是requestCode对不上。从这个表现我们能大概猜测出来,Fragment的startActivityForResult
和对应的onActivityResult
也是借助Activity对应的方法来实现,只是做了一点处理来避免冲突。
源码
猜测当然是不严谨的,还是看源码来的实在。先看Fragment里的源码:
1 | /** |
源码很简单,注释也很清楚。首先对是否由Fragment发起请求做一个判断,然后如果requestCode为-1则直接正常调用父Activity的startActivityForResult
方法,因为requestCode为-1代表调用的是startActivity
方法。然后对requestCode做一个过滤,,必须小于0xffff0000。接着因为一个Activity可能有多个Fragment,所以还要拿到请求的序号。然后就是调用ActivityCompat.startActivityForResult
了:
1 |
|
这里就没啥好说的了。那么关键就在调用之前对requestCode的处理那里了,那里并没有直接传入requestCode,而是传入((requestIndex + 1) << 16) + (requestCode & 0xffff)
。requestIndex是Fragment的请求序号,这一个转换比较明确的表达就是:
1 |
|
ok,这里我们也能猜到一点东西了,就是用这个公式来对requestCode做区分,使对应的Activity和Fragment们都能得到正确的回调而不会发生冲突。那么我们再看Activity
的onActivityResult
方法来验证一下:
1 |
|
看得出来基本与猜测的一致,在onActivityResult
里会拿着requestCode推出requestIndex,再推出对应的Fragment和对应的requestCode,再调用Fragment的onActivityResult
方法完成回调。整个流程结束。
结论
根据源码的分析,基本能解释了开始提到的表现都是什么原因。Fragment的startActivityForResult
这一整套流程都要借助父Activity对应的方法来实现,所以在Activity里自然也能拦截到返回的数据,但是因为requestCode被做了处理,所以无法映射到正确的requestCode。看了这个源码只能说Fragment给Google挖的坑太大了···
startActivityForResult在Adapter里如何解耦
在搜索今天这篇东西相关的资料的时候看到了这个问题,觉得还挺有意思的。现在的页面越来越复杂,Adapter也越来越复杂,虽然现在几乎Adapter里都会传入Activity,但是理论上来说Adapter只是一个适配器,应该只负责View和data的绑定,而不应该做太多的逻辑处理。所以我目前想到的比较合理的解耦方式应该是Adapter向外界暴露对应的startActivityForResult
和onActivityResult
两个方法,类似今天分析的源码Fragment所做的那样,这样做也可以避免处理Fragment和Activity不同的问题,无论Adapter在哪里使用都不受影响。如果Adapter更复杂,具体的逻辑已经被拆成了单个的item来实现,那么对应的item也暴露这两个方法,然后交给Adapter来代理,requestCode也交给Adapter来管理。这应该是比较合理的解耦方式。