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方法内部进行再次分发。