美文网首页程序员
Fragment中startActivityForResult的

Fragment中startActivityForResult的

作者: nemuni | 来源:发表于2018-10-18 15:42 被阅读0次

    昨天看面经的时候看到一个问题,在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<Activity> {
            ......
            @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<FragmentActivity> {
          ......
          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的

        本文链接:https://www.haomeiwen.com/subject/vaajzftx.html