Fragment.startActivityForResult逻辑

Posted by phantomVK on April 2, 2019

Fragment 内部调用自有方法 startActivityForResult()

public void startActivityForResult(Intent intent, int requestCode) {
    startActivityForResult(intent, requestCode, null);
}

该方法辗转调用同名重载方法,方法内调用名为 mHost 变量的方法,该变量的类型为 FragmentHostCallback

public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
    // 如果Fragment没有绑定到Activity,即宿主实例不存在,会抛出异常
    if (mHost == null) {
        throw new IllegalStateException("Fragment " + this + " not attached to Activity");
    }
    // 调用方法
    mHost.onStartActivityFromFragment(this, intent, requestCode, options);
}

FragmentHostCallback 是一个抽象类,继承抽象类 FragmentContainer,由 Fragment 所依附的宿主实现。

FragmentHostCallback mHost;

实现该抽象类表示具体实现类具有保存和展示Fragment的能力。

public abstract class FragmentHostCallback<E> extends FragmentContainer {
    public void onStartActivityFromFragment(
            Fragment fragment, Intent intent, int requestCode, @Nullable Bundle options) {
        if (requestCode != -1) {
            throw new IllegalStateException(
                    "Starting activity with a requestCode requires a FragmentActivity host");
        }
        mContext.startActivity(intent);
    }
}

FragmentHostCallback 的实际实现类,是 FragmentActivity 的内部类 HostCallbacks。实现抽象类的同时还复写了父类的实现逻辑,当 Fragment 调用该方法时,实际调用 FragmentActivity 实现的成员方法。

public class FragmentActivity extends BaseFragmentActivityApi16 implements
        ViewModelStoreOwner,
        ActivityCompat.OnRequestPermissionsResultCallback,
        ActivityCompat.RequestPermissionsRequestCodeValidator {
            
    .....
        
    // 面试考点:非静态内部类隐式持有外部类的实例引用
    class HostCallbacks extends FragmentHostCallback<FragmentActivity> {
        .....

        @Override
        public void onStartActivityFromFragment(
                Fragment fragment, Intent intent, int requestCode, @Nullable Bundle options) {
            // 内部类调用了宿主的成员方法,相当于做了一层桥接
            FragmentActivity.this.startActivityFromFragment(fragment, intent, requestCode, options);
        }
    }
}

那看看宿主 FragmentActivity 的方法实现。方法内的实参 requestCode 高16位保存 (requestIndex+1) 的值,低16位保存来自 FragmentrequestCode 值。

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);
        // Activity给Fragment的请求生成一个请求索引值,用于后续匹配返回的Fragment
        int requestIndex = allocateRequestIndex(fragment);
        // 请求索引放在requestCode实参高16位,Fragment提供的requestCode放在实参低16位
        ActivityCompat.startActivityForResult(
                this, intent, ((requestIndex + 1) << 16) + (requestCode & 0xffff), options);
    } finally {
        mStartedActivityFromFragment = false;
    }
}

当结果从其他 Activity 回到 Fragment 所在宿主 Activity 时,宿主页面先检查 requestIndex 的值,判断原始请求是否来自 Fragment。检查逻辑很简单,只要回来 requestIndex 高16位非零,则表示该请求来自 Fragment

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    mFragments.noteStateNotSaved();
    // 取出requestCode高16位
    int requestIndex = requestCode>>16;
    if (requestIndex != 0) {
        requestIndex--;

        // 用requestCode查找对应的请求Fragment
        String who = mPendingFragmentActivityResults.get(requestIndex);
        mPendingFragmentActivityResults.remove(requestIndex);
        if (who == null) {
            Log.w(TAG, "Activity result delivered for unknown Fragment.");
            return;
        }
        // 从Activity的Fragment栈中查找返回数据的最终归宿,即Fragment实例
        Fragment targetFragment = mFragments.findFragmentByWho(who);
        // Fragment如果已经被销毁则可能为空
        if (targetFragment == null) {
            Log.w(TAG, "Activity result no fragment exists for who: " + who);
        } else {
            // 进行与操作后,就是请求的原始requestCode
            // 作为参数和返回结果让Fragment自行处理onActivityResult
            targetFragment.onActivityResult(requestCode & 0xffff, resultCode, data);
        }
        return;
    }

    ActivityCompat.PermissionCompatDelegate delegate =
            ActivityCompat.getPermissionCompatDelegate();
    if (delegate != null && delegate.onActivityResult(this, requestCode, resultCode, data)) {
        // Delegate has handled the activity result
        return;
    }

    super.onActivityResult(requestCode, resultCode, data);
}

由于上述调用位于 FragmentActivity 中,如果重写该方法的时候没有调用 super.onActivityResult(int requestCode, int resultCode, Intent data),则页面内的 Fragment 无法接受该结果通知。

同时可知,Activity 自己调用 onActivityResult 方法时传入的 requestCode 不能大于 0xffff,否则会和来自 Fragment 的请求 requestCode 混淆。