fragment和activity的startActivityForResult的区别
直接的表现
fragment和activity都有startActivityForResult,都能触发onActivityResult的回调。
但是他们的差异是:
- 由Activity发起的跳转请求,fragment是完全收不到结果的
- 由fragment发起的请求,activity是可以接收到的,但是回调的requestCode完全不对,是很奇怪的数字,完全匹配不上
源码分析
Fragment#startActivityForResult
public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
if (mHost == null) {
throw new IllegalStateException("Fragment " + this + " not attached to Activity");
}
mHost.onStartActivityFromFragment(this /*fragment*/, intent, requestCode, options);
}
mHost就是FragmentHostCallback,它的具体实现是在FragmentActivity中。
而mHost是直接将请求转发到FragmentActivity上了。
FragmentHostCallback#onStartActivityFromFragment
@Override
public void onStartActivityFromFragment(
Fragment fragment, Intent intent, int requestCode, @Nullable Bundle options) {
FragmentActivity.this.startActivityFromFragment(fragment, intent, requestCode, options);
}
FragmentActivity#startActivityFromFragment
public void startActivityFromFragment(Fragment fragment, Intent intent,
int requestCode, @Nullable Bundle options) {
mStartedActivityFromFragment = true;
try {
if (requestCode == -1) {
ActivityCompat.startActivityForResult(this, intent, -1, options);
return;
}
checkForValidRequestCode(requestCode);
int requestIndex = allocateRequestIndex(fragment);
ActivityCompat.startActivityForResult(
this, intent, ((requestIndex + 1) << 16) + (requestCode & 0xffff), options);
} finally {
mStartedActivityFromFragment = false;
}
}
1、requestCode合法性检查
requestCode的值必须是小于0XFFFF的。
static void checkForValidRequestCode(int requestCode) {
if ((requestCode & 0xffff0000) != 0) {
throw new IllegalArgumentException("Can only use lower 16 bits for requestCode");
}
}
2、计算requestIndex,即请求的序号
序号是能超过0xffff - 1的,并且需要是顺序递增的。
private int allocateRequestIndex(Fragment fragment) {
if (mPendingFragmentActivityResults.size() >= MAX_NUM_PENDING_FRAGMENT_ACTIVITY_RESULTS) {
throw new IllegalStateException("Too many pending Fragment activity results.");
}
// Find an unallocated request index in the mPendingFragmentActivityResults map.
while (mPendingFragmentActivityResults.indexOfKey(mNextCandidateRequestIndex) >= 0) {
mNextCandidateRequestIndex =
(mNextCandidateRequestIndex + 1) % MAX_NUM_PENDING_FRAGMENT_ACTIVITY_RESULTS;
}
int requestIndex = mNextCandidateRequestIndex;
mPendingFragmentActivityResults.put(requestIndex, fragment.mWho);
mNextCandidateRequestIndex =
(mNextCandidateRequestIndex + 1) % MAX_NUM_PENDING_FRAGMENT_ACTIVITY_RESULTS;
return requestIndex;
}
3、调用activity的startActivityXXX
这里的核心是requestCode的重新计算:
(requestIndex + 1) << 16) + (requestCode & 0xffff)
它的简化意思是:
(requestIndex+1)*65536+requestCode
因此这个的code肯定是大于65536的。
FragmentActivity#startActivityFromFragment
这个是从activity发起跳转的。是和从fragment过来有差异的。
@Override
public void startActivityForResult(Intent intent, int requestCode) {
// If this was started from a Fragment we've already checked the upper 16 bits were not in
// use, and then repurposed them for the Fragment's index.
if (!mStartedActivityFromFragment) {
if (requestCode != -1) {
checkForValidRequestCode(requestCode);
}
}
super.startActivityForResult(intent, requestCode);
}
如果请求是来自与fragment,那么则不需要再对requestCode做检查;如果来自于activity,则需要检查requestCode。
FragmentActivity.onActivityResult
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
mFragments.noteStateNotSaved();
int requestIndex = requestCode>>16;
if (requestIndex != 0) {
requestIndex--;
String who = mPendingFragmentActivityResults.get(requestIndex);
mPendingFragmentActivityResults.remove(requestIndex);
if (who == null) {
Log.w(TAG, "Activity result delivered for unknown Fragment.");
return;
}
Fragment targetFragment = mFragments.findFragmentByWho(who);
if (targetFragment == null) {
Log.w(TAG, "Activity result no fragment exists for who: " + who);
} else {
targetFragment.onActivityResult(requestCode & 0xffff, resultCode, data);
}
return;
}
super.onActivityResult(requestCode, resultCode, data);
}
从这里可以看出来,首先是判断是不是从fragment过来的,只有大与0xffff的才是从fragment过来,需要将回调同时发给fragment。
然后在给到fragment之前,将requestCode进行还原。但是Activity的回调是没有还原的,因此requestCode是个奇怪的数字,并且每次都可能不一样。
这么设计的原因?
设计的原因是这样可以把result返回给fragment,同时又能影响到activity接收result。
以为fragment的回调需要依赖于activity接收到回调,所以activity的回调也会被触发。
但它的限制是requestCode必须<=0xffff。
而如果不是从fragment发起的,则直接被过滤,不会给到fragment。
总结
- 使用startActivityForResult的时候,requestCode一定不要大于0xffff(65535)
- 如果希望在Fragment的onActivityResult接收数据,就要调用
Fragment.startActivityForResult
,而不是Fragment.getActivity().startActivityForResult
- 对于result的回调,还是需要判断requestCode的值
- 编码和解码的很好的一个使用实例
Activity的继承关系
- FragmentActivity:如果需要使用兼容的Fragment和Loader,则使用这个类
- AppCompatActivity:如果需要使用兼容的ActionBar,则使用这个类