Fragment中startActivityForResult的原理分析

昨天看面经的时候看到一个问题,在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就收不到了。

所以如果需要FragmentActivity自动分发给Fragment,我们要在自己重写的onActivityResult中加上super.onActivityResult。

你可能感兴趣的:(Fragment中startActivityForResult的原理分析)