fragment和activity的startActivityForResult的区别

直接的表现

fragment和activity都有startActivityForResult,都能触发onActivityResult的回调。

但是他们的差异是:

  • 由Activity发起的跳转请求,fragment是完全收不到结果的
  • 由fragment发起的请求,activity是可以接收到的,但是回调的requestCode完全不对,是很奇怪的数字,完全匹配不上

源码分析

Fragment#startActivityForResult

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);
}

mHost就是FragmentHostCallback,它的具体实现是在FragmentActivity中。

而mHost是直接将请求转发到FragmentActivity上了。

FragmentHostCallback#onStartActivityFromFragment

@Override
public void onStartActivityFromFragment(
    Fragment fragment, Intent intent, int requestCode, @Nullable Bundle options) {
    FragmentActivity.this.startActivityFromFragment(fragment, intent, requestCode, options);
}

FragmentActivity#startActivityFromFragment

public void startActivityFromFragment(Fragment fragment, Intent intent,
            int requestCode, @Nullable Bundle options) {
        mStartedActivityFromFragment = true;
        try {
            if (requestCode == -1) {
                ActivityCompat.startActivityForResult(this, intent, -1, options);
                return;
            }
            checkForValidRequestCode(requestCode);
            int requestIndex = allocateRequestIndex(fragment);
            ActivityCompat.startActivityForResult(
                    this, intent, ((requestIndex + 1) << 16) + (requestCode & 0xffff), options);
        } finally {
            mStartedActivityFromFragment = false;
        }
    }
1、requestCode合法性检查

requestCode的值必须是小于0XFFFF的。

static void checkForValidRequestCode(int requestCode) {
    if ((requestCode & 0xffff0000) != 0) {
        throw new IllegalArgumentException("Can only use lower 16 bits for requestCode");
    }
}
2、计算requestIndex,即请求的序号

序号是能超过0xffff - 1的,并且需要是顺序递增的。

private int allocateRequestIndex(Fragment fragment) {
        if (mPendingFragmentActivityResults.size() >= MAX_NUM_PENDING_FRAGMENT_ACTIVITY_RESULTS) {
            throw new IllegalStateException("Too many pending Fragment activity results.");
        }

        // Find an unallocated request index in the mPendingFragmentActivityResults map.
        while (mPendingFragmentActivityResults.indexOfKey(mNextCandidateRequestIndex) >= 0) {
            mNextCandidateRequestIndex =
                    (mNextCandidateRequestIndex + 1) % MAX_NUM_PENDING_FRAGMENT_ACTIVITY_RESULTS;
        }

        int requestIndex = mNextCandidateRequestIndex;
        mPendingFragmentActivityResults.put(requestIndex, fragment.mWho);
        mNextCandidateRequestIndex =
                (mNextCandidateRequestIndex + 1) % MAX_NUM_PENDING_FRAGMENT_ACTIVITY_RESULTS;

        return requestIndex;
    }
3、调用activity的startActivityXXX

这里的核心是requestCode的重新计算:

(requestIndex + 1) << 16) + (requestCode & 0xffff)

它的简化意思是:

(requestIndex+1)*65536+requestCode

因此这个的code肯定是大于65536的。

FragmentActivity#startActivityFromFragment

这个是从activity发起跳转的。是和从fragment过来有差异的。

@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);
}

如果请求是来自与fragment,那么则不需要再对requestCode做检查;如果来自于activity,则需要检查requestCode。

FragmentActivity.onActivityResult

@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);
    }

从这里可以看出来,首先是判断是不是从fragment过来的,只有大与0xffff的才是从fragment过来,需要将回调同时发给fragment。

然后在给到fragment之前,将requestCode进行还原。但是Activity的回调是没有还原的,因此requestCode是个奇怪的数字,并且每次都可能不一样。

这么设计的原因?

设计的原因是这样可以把result返回给fragment,同时又能影响到activity接收result。

以为fragment的回调需要依赖于activity接收到回调,所以activity的回调也会被触发。

但它的限制是requestCode必须<=0xffff。

而如果不是从fragment发起的,则直接被过滤,不会给到fragment。

总结

  1. 使用startActivityForResult的时候,requestCode一定不要大于0xffff(65535)
  2. 如果希望在Fragment的onActivityResult接收数据,就要调用Fragment.startActivityForResult,而不是Fragment.getActivity().startActivityForResult
  3. 对于result的回调,还是需要判断requestCode的值
  4. 编码和解码的很好的一个使用实例

Activity的继承关系

  • FragmentActivity:如果需要使用兼容的Fragment和Loader,则使用这个类
  • AppCompatActivity:如果需要使用兼容的ActionBar,则使用这个类

results matching ""

    No results matching ""