在Fragment类中启动另一个Activity并获取返回结果,可以使用this.startActivityForResult(),也可以使用getActivity().startActivityForResult()。那么,这两种方式有何异同呢?
先来看一个示例。
创建TestFragment类,继承自Fragment。添加一个按钮,点击后启动另一个Activity,重写Fragment类的onActivityResult()方法,打印requestCode参数。
当我们在TestFragment中使用startActivityForResult(),打印的日志如下:
而使用getActivity().startActivityForResult()时,打印的日志如下:
通过日志结果,可以得出一个结论:
当使用startActivityForResult()时,Fragment和FragmentActivity的onActivityResult()方法都能接收到返回结果,但FragmentActivity中接收到的requestCode对应不上。
当使用getActivity().startActivityForResult()时,只有FragmentActivity的onActivityResult()方法能接收到返回结果,Fragment的onActivityResult()方法不会被调用。
现在有两个疑问。一,调用startActivityForResult()时,FragmentActivity类中接收到的requestCode为何会对应不上?二,调用getActivity().startActivityForResult()时,Fragment类的onActivityResult()方法为何不会被调用?
下面,我们进入源码来分析。
(1).首先找到Fragment类中的startActivityForResult()方法。
(2).mHost是Fragment类中FragmentHostCallback类型的成员变量。FragmentHostCallback是一个抽象类,其实现类仅有HostCallbacks。HostCallbacks是FragmentActivity中的内部类。
(3).接下来找到FragmentActivity类中的startActivityFromFragment()方法。
第1步,requestCode==-1的情况,当Fragment中调用startActivity()而非startActivityForResult()时,requestCode值为-1。此处我们忽略这种情况。
第2步,检查requestCode是否合法。从checkForValidRequestCode()可以看出,requestCode值的合法性被限定在0 ~ 0xffff,如果超出限制会抛异常。当requestCode的值合法时,会继续执行第3步。
第4步,调用ActivityCompat.startActivityForResult()来启动Activity。ActivityCompat是android-support-v4包中的兼容性类,ActivityCompat.startActivityForResult()通过传入Activity类型参数,内部会调用activity.startActivityForResult()方法。
这里的关键在于对requestCode值的修改,将requestCode修改为(requestIndex + 1) << 16) + (requestCode & 0xffff),即(requestIndex + 1) * 0xffff + requestCode。因为requestIndex>=0,所以最后得到的计算结果必然>=0xffff。
二.FragmentActivity类中的startActivityForResult()
当我们在Fragment中调用getActivity().startActivityForResult()时,其实调用的是宿主FragmentActivity的startActivityForResult()方法。
三.回归onActivityResult()方法
前面已经详细介绍了Fragment和FragmentActivity中startActivityForResult()方法的执行逻辑,最后都是调用Activity类的startActivityForResult()方法。区别在于,Fragment类对requestCode的值进行了修改,即(requestIndex + 1) * 0xffff + requestCode。
当调用Activity类的startActivityForResult()方法时,onActivityResult()方法会被调用。下面来看一下FragmentActivity类的onActivityResult()方法。
在onActivityResult()方法中,首先通过requestCode>>16得到requestIndex,判断requestIndex是否等于0。那么为什么要将requestCode右移16位呢?从前面的分析中我们已经知道,requestCode如果来自Fragment类的startActivityForResult()方法,那么其值必然>=0xffff(即65535),如果来自FragmentActivity类的startActivityForResult()方法,其值则维持原始数值不变。
所以,当调用Fragment类的startActivityForResult()方法,最后进入到onActivityResult()时,requestIndex不等于0,此时进入if条件语句,通过requestIndex找到对应的Fragment对象(即targetFragment),执行Fragment类的onActivityResult()方法,并将requestCode的值还原后作为参数传递进去(requestCode & 0xffff),最后通过reture语句返回(不再执行之后的super.onActivityResult()了)。当调用FragmentActivity类的startActivityForResult()方法时,由于requestIndex=0,不进入if语句,直接执行super.onActivityResult()语句。
这也就解释了最初的疑问,在Fragment类中,
当使用startActivityForResult()时,Fragment和FragmentActivity的onActivityResult()方法都能接收到返回结果,但FragmentActivity中接收到的requestCode会对应不上。
当使用getActivity().startActivityForResult()时,只有FragmentActivity的onActivityResult()方法能接收到返回结果。
所以, 当我们在Fragment中启动Activity并获取返回值时,需要使用startActivityForResult()。调用startActivityForResult()时传入的requestCode一定不要大于65535。
先来看一个示例。
创建TestFragment类,继承自Fragment。添加一个按钮,点击后启动另一个Activity,重写Fragment类的onActivityResult()方法,打印requestCode参数。
package net.csdn.blog.fragment;
public class TestFragment extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment, null);
view.findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// startActivityForResult(new Intent(getContext(), OtherActivity.class), 1);
// 或
// getActivity().startActivityForResult(new Intent(getContext(), OtherActivity.class), 1);
}
});
return view;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Log.i("test", "TestFragment onActivityResult() requestCode=" + requestCode);
}
}
创建TestActivity类,继承自FragmentActivity。将上面创建的TestFragment添加进来,同样重写onActivityResult()方法打印requestCode参数。
package net.csdn.blog.fragment;
public class TestActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportFragmentManager().beginTransaction().replace(R.id.activity_main, new TestFragment()).commit();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Log.i("test", "TestActivity onActivityResult() requestCode=" + requestCode);
}
}
当我们在TestFragment中使用startActivityForResult(),打印的日志如下:
12-06 14:31:14.606 11119-11119/net.csdn.blog.fragment I/test: TestFragment onActivityResult() requestCode=1
12-06 14:31:14.606 11119-11119/net.csdn.blog.fragment I/test: TestActivity onActivityResult() requestCode=65537
而使用getActivity().startActivityForResult()时,打印的日志如下:
12-06 14:31:37.362 11119-11119/net.csdn.blog.fragment I/test: TestActivity onActivityResult() requestCode=1
通过日志结果,可以得出一个结论:
当使用startActivityForResult()时,Fragment和FragmentActivity的onActivityResult()方法都能接收到返回结果,但FragmentActivity中接收到的requestCode对应不上。
当使用getActivity().startActivityForResult()时,只有FragmentActivity的onActivityResult()方法能接收到返回结果,Fragment的onActivityResult()方法不会被调用。
现在有两个疑问。一,调用startActivityForResult()时,FragmentActivity类中接收到的requestCode为何会对应不上?二,调用getActivity().startActivityForResult()时,Fragment类的onActivityResult()方法为何不会被调用?
下面,我们进入源码来分析。
一.Fragment类中的startActivityForResult()
(1).首先找到Fragment类中的startActivityForResult()方法。
public void startActivityForResult(Intent intent, int requestCode) {
startActivityForResult(intent, requestCode, null);
}
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);
}
可以看到,在startActivityForResult()方法内部,会去调用mHost.onStartActivityFromFragment()方法。
(2).mHost是Fragment类中FragmentHostCallback类型的成员变量。FragmentHostCallback是一个抽象类,其实现类仅有HostCallbacks。HostCallbacks是FragmentActivity中的内部类。
// Host this fragment is attached to.
FragmentHostCallback mHost;
class HostCallbacks extends FragmentHostCallback<FragmentActivity> {
public HostCallbacks() {
super(FragmentActivity.this /*fragmentActivity*/);
}
....
@Override
public void onStartActivityFromFragment(
Fragment fragment, Intent intent, int requestCode, @Nullable Bundle options) {
FragmentActivity.this.startActivityFromFragment(fragment, intent, requestCode, options);
}
....
}
从这段代码中可以看到,上面的mHost.onStartActivityFromFragment()的调用,会执行到FragmentActivity类中的startActivityFromFragment()方法,并将当前的Fragment对象作为参数传递过去。
(3).接下来找到FragmentActivity类中的startActivityFromFragment()方法。
/**
* Called by Fragment.startActivityForResult() to implement its behavior.
*/
public void startActivityFromFragment(Fragment fragment, Intent intent,
int requestCode, @Nullable Bundle options) {
mStartedActivityFromFragment = true;
try {
// 第1步
if (requestCode == -1) {
ActivityCompat.startActivityForResult(this, intent, -1, options);
return;
}
// 第2步
checkForValidRequestCode(requestCode);
// 第3步
int requestIndex = allocateRequestIndex(fragment);
// 第4步
ActivityCompat.startActivityForResult(
this, intent, ((requestIndex + 1) << 16) + (requestCode & 0xffff), options);
} finally {
mStartedActivityFromFragment = false;
}
}
startActivityFromFragment()方法内部逻辑分4步执行,我们将其拆分来看。
第1步,requestCode==-1的情况,当Fragment中调用startActivity()而非startActivityForResult()时,requestCode值为-1。此处我们忽略这种情况。
第2步,检查requestCode是否合法。从checkForValidRequestCode()可以看出,requestCode值的合法性被限定在0 ~ 0xffff,如果超出限制会抛异常。当requestCode的值合法时,会继续执行第3步。
static void checkForValidRequestCode(int requestCode) {
if ((requestCode & 0xffff0000) != 0) {
throw new IllegalArgumentException("Can only use lower 16 bits for requestCode");
}
}
第3步,获取当前Fragment的请求索引,赋值给requestIndex。
第4步,调用ActivityCompat.startActivityForResult()来启动Activity。ActivityCompat是android-support-v4包中的兼容性类,ActivityCompat.startActivityForResult()通过传入Activity类型参数,内部会调用activity.startActivityForResult()方法。
这里的关键在于对requestCode值的修改,将requestCode修改为(requestIndex + 1) << 16) + (requestCode & 0xffff),即(requestIndex + 1) * 0xffff + requestCode。因为requestIndex>=0,所以最后得到的计算结果必然>=0xffff。
二.FragmentActivity类中的startActivityForResult()
当我们在Fragment中调用getActivity().startActivityForResult()时,其实调用的是宿主FragmentActivity的startActivityForResult()方法。
/**
* Modifies the standard behavior to allow results to be delivered to fragments.
* This imposes a restriction that requestCode be <= 0xffff.
*/
@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);
}
startActivityForResult()方法在注释中明确写出requestCode必须要<=0xffff(即65535)。在方法内部同样通过checkForValidRequestCode()方法检查requestCode是否在限定的范围内。如果requestCode合法,会通过super.startActivityForResult()来调用Activity类中的startActivityForResult()方法。
三.回归onActivityResult()方法
前面已经详细介绍了Fragment和FragmentActivity中startActivityForResult()方法的执行逻辑,最后都是调用Activity类的startActivityForResult()方法。区别在于,Fragment类对requestCode的值进行了修改,即(requestIndex + 1) * 0xffff + requestCode。
当调用Activity类的startActivityForResult()方法时,onActivityResult()方法会被调用。下面来看一下FragmentActivity类的onActivityResult()方法。
/**
* Dispatch incoming result to the correct fragment.
*/
@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);
}
在onActivityResult()方法中,首先通过requestCode>>16得到requestIndex,判断requestIndex是否等于0。那么为什么要将requestCode右移16位呢?从前面的分析中我们已经知道,requestCode如果来自Fragment类的startActivityForResult()方法,那么其值必然>=0xffff(即65535),如果来自FragmentActivity类的startActivityForResult()方法,其值则维持原始数值不变。
所以,当调用Fragment类的startActivityForResult()方法,最后进入到onActivityResult()时,requestIndex不等于0,此时进入if条件语句,通过requestIndex找到对应的Fragment对象(即targetFragment),执行Fragment类的onActivityResult()方法,并将requestCode的值还原后作为参数传递进去(requestCode & 0xffff),最后通过reture语句返回(不再执行之后的super.onActivityResult()了)。当调用FragmentActivity类的startActivityForResult()方法时,由于requestIndex=0,不进入if语句,直接执行super.onActivityResult()语句。
这也就解释了最初的疑问,在Fragment类中,
当使用startActivityForResult()时,Fragment和FragmentActivity的onActivityResult()方法都能接收到返回结果,但FragmentActivity中接收到的requestCode会对应不上。
当使用getActivity().startActivityForResult()时,只有FragmentActivity的onActivityResult()方法能接收到返回结果。
所以, 当我们在Fragment中启动Activity并获取返回值时,需要使用startActivityForResult()。调用startActivityForResult()时传入的requestCode一定不要大于65535。