Fragment的onActivityResult方法
情景分析:
Activity : PhotoActivity
FragmentActivity: HomeActivity
Fragment: MenuLeftFragment
Fragment:TestFragment
Fragment: HomeFragment
Fragment: childFragment --> is a child of HomeFragment
这是一个有侧拉菜单的应用。进入应用加载MenuLeftFragment和TestFragment。
getSupportFragmentManager().beginTransaction()
.replace(R.id.home_content, TestFragment).commit();
getSupportFragmentManager().beginTransaction()
.replace(R.id.menu_Left, mMenuFragment).commit();
然后点击侧栏按钮进入HomeFragment,HomeFragment内部加载一个childFragment.
在childFragment中点击按钮,跳转到PhotoActivity,PhotoActivity返回时,childFragment获取数据。
childFragment代码如下
...
final int requestCode = 2;
...
void goPhotoActivty(){
Intent xzIntent = new Intent(getActivity(),PhotoActivity.class);
startActivityForResult(xzIntent, requestCode);
}
...
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.v(TAG,"====== childFragment onActivityResult ====");
}
PhotoActivity代码如下:
...
void backActivity(){
Intent it = new Intent();
setResult(RESULT_OK,it);
}
...
ok,问题来了,当我们调用PhotoActivity中的backActivity()方法时,并没有日志打印输出,也就是说没有触发childFragment的onActivityResult()方法。
下面我们在HomeActivity的onActivityResult方法中打印日志:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// TODO Auto-generated method stub
Log.v(TAG, "===== HomeActivity onActivityResult =====");
super.onActivityResult(requestCode, resultCode, data);
}
然后当我们调用PhotoActivity中的backActivity()方法时,会输出日志:
===== HomeActivity onActivityResult =====
然后我们重写HomeFragment的onActivityResult方法,会发现也会被调用。
ok,通过源码分析我们会发现主要原因是:
- 当一个childFragment想要实现onActivityResult方法时,必须要在parentFragment的onActivityResult方法内再次分发。
在FragmentActivity的中:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
mFragments.noteStateNotSaved();
int index = requestCode>>16;
if (index != 0) {
index--;
if (mFragments.mActive == null || index < 0 || index >= mFragments.mActive.size()) {
Log.w(TAG, "Activity result fragment index out of range: 0x"
+ Integer.toHexString(requestCode));
return;
}
Fragment frag = mFragments.mActive.get(index);
if (frag == null) {
Log.w(TAG, "Activity result no fragment exists for index: 0x"
+ Integer.toHexString(requestCode));
} else {
frag.onActivityResult(requestCode&0xffff, resultCode, data);
}
return;
}
super.onActivityResult(requestCode, resultCode, data);
}
我们会发现FragmentActivity的onActivityResult方法被调用时首先要从活动fragment列表中获取一个Fragment对象,如果该对象存在,则调用fragment.onActivityReuslt方法。不会调用activity.onActivityResult方法。
下面我们在看Fragment.onActivityResult:
public void onActivityResult(int requestCode, int resultCode, Intent data) {
}
在我们问题中,当调用PhotoActivity.backActivity方法时,首先HomeActivity.onActivityResult方法被调用,我们在super.方法之前打印日志会输出,在super.方法中,会获取当前活动列表中的一个fragment--HomeFragment,然后调用HomeFragment.onActivityResult方法。这是我们HomeFragment.onActivityResult方法内部的日志会输出,但是因为Fragment.onActivityResult方法内部为空,所以childFragment.onAcitivtyResult方法不会被调用。我们必须手动在HomeFragment中重新分发。
HomeFragment代码如下:
...
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
childFragment.onActivityResult(requestCode, resultCode, data);
}
...
ok,大功告成!
但是!!!
随着代码越来越多,问题也随之而来。突然childFragment.onActivityResult没有调用.
从头看源码:
在
childFragment中我们调用:void goPhotoActivty(){ Intent xzIntent = new Intent(getActivity(),PhotoActivity.class); startActivityForResult(xzIntent, requestCode); }查看
Fragment.startActivityForResult:public void startActivityForResult(Intent intent, int requestCode) { if (mActivity == null) { throw new IllegalStateException("Fragment " + this + " not attached to Activity"); } mActivity.startActivityFromFragment(this, intent, requestCode); }在该方法中首先判断如果当前上下文环境为空,则抛出异常,否则调用
activity.startActivityFromFragment,并传递三个参数分别是:当前Fragment对象childFragment,intent,requestCode.下面我们继续查看
activity.startActivityFromFragment的代码:/** * Called by Fragment.startActivityForResult() to implement its behavior. */ public void startActivityFromFragment(Fragment fragment, Intent intent, int requestCode) { if (requestCode == -1) { super.startActivityForResult(intent, -1); return; } if ((requestCode&0xffff0000) != 0) { throw new IllegalArgumentException("Can only use lower 16 bits for requestCode"); } super.startActivityForResult(intent, ((fragment.mIndex+1)<<16) + (requestCode&0xffff)); }在这里我们发现最终会调用
Activity.startActivityForResult方法。并传第一个requestCode参数。因为我们传递的requestCode必须是一个小于0xffff的16位二进制数,这里把fragment.mIndex参数左移16位后加上原来的requestCode传递给Activity.startActivityForResult.接下来Activity.onActivityForResult中回被调用。Activity的
onActivityForResult方法会被首先调用:/** * Dispatch incoming result to the correct fragment. */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { mFragments.noteStateNotSaved(); int index = requestCode>>16; if (index != 0) { index--; if (mFragments.mActive == null || index < 0 || index >= mFragments.mActive.size()) { Log.w(TAG, "Activity result fragment index out of range: 0x" + Integer.toHexString(requestCode)); return; } Fragment frag = mFragments.mActive.get(index); if (frag == null) { Log.w(TAG, "Activity result no fragment exists for index: 0x" + Integer.toHexString(requestCode)); } else { frag.onActivityResult(requestCode&0xffff, resultCode, data); } return; } super.onActivityResult(requestCode, resultCode, data); }这上面的代码中我们会发现首先通过位运算从
requestCode中取出mIndex变量。这个变量标识着当前Fragment在FragmentManager的List类型的mActive变量中的下标。然后根据这个下标从当前FragmentManager中取出对应的Fragment,就是我们在调用fragment.startActivityFromFragment传递的第一个参数:Fragment。如果当前Fragment依然有效。则调用这个fragment.onActivityResult方法。反之,则调用activity.onActivityResult. 看到这里我们就明白来,原来我们调用fragment.startActivityFromFragment传递的第一个参数是childFragment.而在activity.onActivityResult中就会根据childFragment.mIndex获取一个Fragment对象,调用该对象的onActivityResult方法。至于我们为什么第一次时可以正确调用。就是一个巧合,刚好mIndex对应HomeFragment。关于
mIndex方面的内容可以查看FragmentManager中的:makeActive、makeInactive等代码。至于第二次为什么不行了,就是因为根据mIndex找不到HomeFragment无法完成分发到childFragment的回调了。
小结:
当我们在Fragment中实现onActivityResult方法时,必须判断当前Fragment是否作为另一个Fragment的child。如果是,必须使用parentFragment的startActivityForResult方法。然后在parentFragment的onActivityResult方法内部进行再次分发。