昨天看面经的时候看到一个问题,在Fragment中和Activity中调用StartActivityForResult有什么区别?
百度了一下没找到专门解释的,只看到一些怎么解决调用后onActivityResult()获取数据的笔记,或者是Fragment中调用startActivityForResult和getActivity().startActivityForResult区别的博文。
干脆自己追踪源码看一看好了。
如有误请读者指正![抱拳]
下面开始剖析,首先明确几个关键类:
- Activity,对就是单纯的Activity,直接继承自ContextThemeWrapper那个。
- Fragment。
- FragmentHostCallback。
- Activity的内部类HostCallbacks。
- FragmentActivity,以及它的内部类HostCallbacks。
Fragment中startActivityForResult(...):
public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
if (this.mHost == null) {
throw new IllegalStateException("Fragment " + this + " not attached to Activity");
} else {
this.mHost.onStartActivityFromFragment(this, intent, requestCode, options);
}
}
调用了mHost,这个变量就是FragmentHostCallback类,这是一个抽象类。
FragmentHostCallback mHost;
这个mHost内部持有Fragment持有Activity的引用。
Activity的内部类HostCallbacks:
class HostCallbacks extends FragmentHostCallback {
......
@Override
public void onStartActivityFromFragment(Fragment fragment, Intent intent, int requestCode,
Bundle options) {
Activity.this.startActivityFromFragment(fragment, intent, requestCode, options);
}
......
}
* @deprecated Use {@link android.support.v4.app.FragmentActivity#startActivityFromFragment(
* android.support.v4.app.Fragment,Intent,int,Bundle)}
*/
@Deprecated
public void startActivityFromFragment(@NonNull Fragment fragment,
@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) {
startActivityForResult(fragment.mWho, intent, requestCode, options);
}
被标记了不建议使用,建议使用FragmentActivity的方法。调用了Activity自身的startActivityForResult,和在Activity调用的区别在第一个参数,用途没找到。
而且Activity的onActivityResult方法是空方法,需要子类重写。也就是说返回的数据需要我们自己重写onActivityResult方法来手动判断、分发。
那为什么网上的博文都可以在Fragment中调用startActivityForResult并且直接在Fragment的onActivityResult中拿到返回数据?我的猜测是因为他们的并不是直接继承Activity
可能有小伙伴快猜到了,不急我们接着看FragmentActivity中的内部类HostCallbacks。
因为HostCallbacks是继承FragmentHostCallback的,自然在FragmentActivity中添加的Fragment的mHost也是用的HostCallbacks,关键点来了。
class HostCallbacks extends FragmentHostCallback {
......
public void onStartActivityFromFragment(Fragment fragment, Intent intent, int requestCode, @Nullable Bundle options) {
FragmentActivity.this.startActivityFromFragment(fragment, intent, requestCode, options);
}
......
}
是的,HostCallbacks中重写了这个关键的方法。
那我们看看FragmentActivity中的startActivityFromFragment干了啥(下面的源码稍长可以直接看结论)
FragmentActivity中的startActivityFromFragment:
public void startActivityFromFragment(Fragment fragment, Intent intent, int requestCode, @Nullable Bundle options) {
this.mStartedActivityFromFragment = true;
try {
if (requestCode == -1) {
ActivityCompat.startActivityForResult(this, intent, -1, options);
return;
}
checkForValidRequestCode(requestCode);
int requestIndex = this.allocateRequestIndex(fragment);
ActivityCompat.startActivityForResult(this, intent, (requestIndex + 1 << 16) + (requestCode & '\uffff'), options);
} finally {
this.mStartedActivityFromFragment = false;
}
}
FragmentActivity结合了调用方法的fragment和requestCode重新生成了一个requestCode(tips:从源码中可以看到我们在Fragment中startActivityForResult的requestCode不能大于0xffff,因为高16位被清0了)。
从这个操作我们也可以想到FragmentActivity中onActivityResult方法做了什么,就是把这个requestCode再拆成两部分,找到对应的Fragment并调用Fragment.onActivityResult。
(tips:我们FragmentActivity中调用startActivityForResult的requestCode不能大于0xffff,因为onActivityResult中requestCode的高16是要用来识别这个返回数据是要返回给某个Fragment还是返回给FragmentActivity自己的)
恩,流程结束了,最后是简单又关键的一点
我们平时新建一个空Activity,AS默认是帮我们继承自AppCompatActivity的,然后
public class AppCompatActivity extends FragmentActivity implements AppCompatCallback, SupportParentable, DelegateProvider
没错,Fragment可以调用startActivityForResult并接收返回的数据是因为使用的“Activity”是继承自FragmentActivity的。
顺便提一下,Fragment中调用startActivityForResult和getActivity().startActivityForResult的区别,Fragment直接调用的话FragmentActivity在收到返回数据后可以从高16位requestCode识别出需要数据的Fragment并传递过去;如果是getActivity().startActivityForResult的话就变成了是FragmentActivity自己发送的请求,最后收到数据后高16判定分发给FragmentActivity自己,Fragment就收不到了。