在使用fragment时如果需要和新的activity进行数据交互,通常会想到使用startActivityForResult方法,但是使用过程中发现有时候OnActivityResult方法不会调用。
第一种情况:
fragment.java 中
<span style="font-family:SimSun;font-size:18px;">getActivity().startActivityForResult(intent,1001);</span>
这种情况下fragmentActivity下的onActivityResult方法被调用。fragment中的onActivityResult不会被调用。
第二种情况:
fragment.java 中
<span style="font-family:SimSun;font-size:18px;">startActivityForResult(intent,1001);</span>
这种情况下fragmentActivity下的onActivityResult方法被调用,同时注意的是如果fragmentActivity下的onActivityResult方法调用了super.onActivityResult方法,fragment中的onActivityResult也会被调用。
一般情况下使用第二种情况,且不重载fragmentActivity下的onActivityResult方法,除非fragment和activity有互动。出现失效的原因大多是重载fragmentActivity下的onActivityResult方法但是没有调用super.onActivityResult方法
startActivityForResult
requestCode的可用区间:
1.Activity: [Integer.MIN_VALUE, Integer.MAX_VALUE]
(1)当requestCode取值在[Integer.MIN_VALUE, -1]区间中,效果和startActivity()一样,不会收到onActivityResult()回调
(2)内置的Fragment可用requestCode的区间和Activity相同
2.support库: Fragment,以及FragmentActivity:[-1, 65535]
(1)requestCode == -1,效果和startActivity()一样,不会收到onActivityResult()回调
(2)requestCode 在 [Integer.MIN_VALUE, -2]或者[65536, Integer.MAX_VALUE]之间,会抛出异常(requestCode只能使用低16比特)
建议: requestCode的取值统一限制在[-1, 65535]之间
嵌套Fragment
首先要说的是尽量不要使用嵌套Fragment.
当在嵌套Fragment中使用startActivityForResult()时,会遇到的问题:
所有的Fragment都收不到onActivityResult()
某个level 1 的Fragment收到了onActivityResult()
总之那个发起startActivityForResult()的嵌套Fragment是一定不会收到onActivityResult()回调的.
原因如下:(可参考上面说的requestCode)
FragmentActivity.startActivityFromFragment()会改动requestCode,用高16比特存储Fragment在FragmentManager中的index,而低16比特作为Fragment可用的requestCode.在FragmentActivity.onActivityResult()中,根据高16比特,从FragmentManager中找到对应的Fragment,然后将低16比特的值作为requestCode,调用Fragment.onActivityResult().
那么requestCode中只能存储一个index,即root FragmentManager中的Fragment index.因此就会出现上面所列出的情形:
- 当嵌套Fragment在childFragmentManager中的index,大于rootFragmentManager中的所有index时, rootFragmentManager将找不到与此index对应的Fragment,所以没有Fragment能收到onActivityResult()
- 当嵌套Fragment在childFragmentManager中的index,小于等于rootFragmentManager中的所有index时,那么隶属于rootFragmentManager的一个Fragment将会收到onActivityResult()
- 总之即使能有Fragment能收到onActivityResult(),那也是顶层的某个Fragment,而不是发起请求的嵌套Fragment
解决方案:
- 不使用嵌套Fragment :)
- 依然利用requestCode,将其低16位拆分,其中的高8位用来存储childFragmentManager中的index,低8位留给ChildFragment使用.(如果嵌套层级不深,那么此方案还是不错的,如果层级较深,那么留给Fragment的requestCode的可用值区间将非常局限)
- Android 4.2(Api 17)以后,可以使用内置的Fragment,以及ChildFragmentManager,内置Fragment不再需要借助requestCode的高16比特来记录它的index.而是由Framework收到Fragment.startActivityForResult()时,记录该Fragment的标识(android:fragment:${parentIndex}:${myIndex}),派发result时,就根据这个标识找到那个Fragment.因此就不会出现ChildFragment收不到onActivityResult()回调的问题了.可以参考Activity.dispatchActivityResult()
具体实现如下:
在Activity中:
@Override protected void onActivityResult(int requestCode, int ResultCode, Intent data) { super.onActivityResult(requestCode, ResultCode, data); // FragmentActivity.startActivityFromFragment()会改动requestCode, // 用高16比特存储Fragment在FragmentManager中的index, // 而低16比特作为Fragment可用的requestCode. // 在FragmentActivity.onActivityResult()中,根据高16比特, // 从FragmentManager中找到对应的Fragment,然后将低16比特的值作为requestCode, // 调用Fragment.onActivityResult().这里自己主动调用,将requestCode取低16bit数据 if (mFragment != null) { mFragment.onActivityResult(requestCode & 0xffff, ResultCode, data); }}
在一级fragment中
@Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (mChildFragment!= null) { mChildFragment.onActivityResult(requestCode,resultCode,data); } }
这样就会调用二级fragment的onActivityResult了。
至于更深的嵌套理论上用这个方法应该也能实现,但是如果嵌套了这么多层是否应该考虑下代码设计的不够合理。