浅析Fragment中startActivityForResult()与getActivity().startActivityForResult()的异同

来源:互联网 发布:休斯顿国际电影节知乎 编辑:程序博客网 时间:2024/06/05 07:45
在Fragment类中启动另一个Activity并获取返回结果,可以使用this.startActivityForResult(),也可以使用getActivity().startActivityForResult()。那么,这两种方式有何异同呢?

先来看一个示例。

创建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=112-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. */@Overridepublic 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. */@Overrideprotected 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。
0 0
原创粉丝点击