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

本文探讨Fragment中startActivityForResult()与getActivity().startActivityForResult()的区别。通过创建TestActivity和TestFragment,分析它们在处理onActivityResult()时的差异,重点关注requestCode的传递和接收。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在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=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。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值