问题
前段时间在开发中遇到一个问题,在Fragment中启动一个Activity并返回数据,但是结果并没有回调onActivityResult方法,代码很简单应该没什么问题呀。在网上查资料,都是说是因为调用activity.startActivityForResult造成的,应该fragment.startActivityForResult,试了下发现确实可以解决问题,但是原理是什么呢?
原理
我们在Activity重写onActivityResult,发现是会回调onActivityResult的,那么肯定是内部做了什么特殊的处理,于是我查看FragmentActivity中的onActivityResult代码:
@Override
@CallSuper
protected void onActivityResult(int requestCode, int resultCode, @Nullable 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;
}
ActivityCompat.PermissionCompatDelegate delegate =
ActivityCompat.getPermissionCompatDelegate();
if (delegate != null && delegate.onActivityResult(this, requestCode, resultCode, data)) {
// Delegate has handled the activity result
return;
}
super.onActivityResult(requestCode, resultCode, data);
}
在代码中可以看出,回调会先调用Activity中的onActivityResult,如果requestIndex != 0会调用Fragment中的onActivityResult,既然没有回调,那么可能是requestIndex!=0不成立,那么就断点调试一下:
activity.startActivityForResult:
fragment.startActivityForResult:
两种方式经过对比发现activity.startActivityForResult启动回调requestCode是正常的,activity.startActivityForResult启动回调requestCode的值是65537,在fragment中用activity启动的requestCode不是我们传的值,那么肯定是在启动的时候做了什么处理,我们在源码中可以找到答案:
androidx.fragment.app.Fragment
public void startActivityForResult(@SuppressLint("UnknownNullness") Intent intent,
int requestCode) {
startActivityForResult(intent, requestCode, null);
}
public void startActivityForResult(@SuppressLint("UnknownNullness") 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);
}
androidx.fragment.app.FragmentHostCallback
public void onStartActivityFromFragment(
@NonNull Fragment fragment, @SuppressLint("UnknownNullness") Intent intent,
int requestCode, @Nullable Bundle options) {
if (requestCode != -1) {
throw new IllegalStateException(
"Starting activity with a requestCode requires a FragmentActivity host");
}
mContext.startActivity(intent);
}
FragmentHostCallback是一个抽象方法,在FragmentActivity中创建了HostCallbacks并继承了FragmentHostCallback。
androidx.fragment.app.FragmentActivity
class HostCallbacks extends FragmentHostCallback<FragmentActivity> implements
ViewModelStoreOwner,
OnBackPressedDispatcherOwner {
...
@Override
public void onStartActivityFromFragment(@NonNull Fragment fragment, Intent intent,
int requestCode) {
FragmentActivity.this.startActivityFromFragment(fragment, intent, requestCode);
}
...
}
public void startActivityFromFragment(@NonNull Fragment fragment,
@SuppressLint("UnknownNullness") Intent intent, int requestCode) {
startActivityFromFragment(fragment, intent, requestCode, null);
}
public void startActivityFromFragment(@NonNull Fragment fragment,
@SuppressLint("UnknownNullness") 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;
}
}
最终会调用FragmentActivity中的startActivityFromFragment方法,requestCode在这里发生了改变,由此可以知道fragment调用startActivityForResult时requestCode会发生改变,所以在Fragment中调用activity.startActivityForResult,不会回调onActivityResult方法。
结论
在fragmrnt中启动Activty调用fragment.startActivityForResult,否则不会回调onActivityResult方法。