Android中OnActivityResult()不被Fragment调用的解决办法及源码分析

前言

在项目用到Fragment嵌套Fragment的结构,出现子Fragment的OnActivityResult()不被回调的问题,参考鸿军大神的解决方案得以解决。后来由于项目需要对SDK版本进行升级,发现23版本以后,这个问题谷歌大神们已经修复了这个bug!良心啊!遂斗胆在此对源码分析一下,以作记录!

Fragment的OnActivityResult()源码分析

Fragment的OnActivityResult()实现的原理还是将数据传到Fragment依赖的Activity中,然后在Activity中将数据分发到其下的Fragment中。
首先再次声明这是讲SDK版本升级到23以后才解决的,23版本之前的还需要参考鸿军大神的解决方法。
以下是23之前的源码:

/**
 * Dispatch incoming result to the correct fragment.
 */
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    mFragments.noteStateNotSaved();
    int index = requestCode>>16;
    if (index != 0) {
        index--;
        if (mFragments.mActive == null || index < 0 || index >= mFragments.mActive.size()) {
            Log.w(TAG, "Activity result fragment index out of range: 0x"
                    + Integer.toHexString(requestCode));
            return;
        }
        ***Fragment frag = mFragments.mActive.get(index);***
        if (frag == null) {
            Log.w(TAG, "Activity result no fragment exists for index: 0x"
                    + Integer.toHexString(requestCode));
        } else {
            frag.onActivityResult(requestCode&0xffff, resultCode, data);
        }
        return;
    }

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

以下是23以后的源码:

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

不同点我已经用加粗标注了,下面我就着重分析一下加粗处的源码。
其中mPendingFragmentActivityResults是SparseArrayCompat 数据格式。
扩展一下:SparseArrayCompat()其实是一个map容器,它使用了一套算法优化了hashMap,可以节省至少50%的缓存。
缺点但是有局限性只针对下面类型:key: Integer; value: object。
针对mPendingFragmentActivityResults官方给出以下解释:

A map from request index to Fragment “who” (i.e. a Fragment’s unique identifier). Used to keep track of the originating Fragment for Fragment.startActivityForResult(…) calls, so we can dispatch the onActivityResult(…) to the appropriate Fragment. Will only contain entries for startActivityForResult calls where a result has not yet been delivered.

从解释中我们可知道通过mPendingFragmentActivityResults我们可以得到唯一的Fragment标识.

以下才是重中之重,关于mFragments.findFragmentByWho(who)方法的分析。

从源码中可以知道mFragments的类型为FragmentController。FragmentController下的findFragmentByWho(String who)方法源码为:

/**
 * Returns a fragment with the given identifier.
 */
@Nullable
Fragment findFragmentByWho(String who) {
    return mHost.mFragmentManager.findFragmentByWho(who);
}

继续看,mHost的类型为我们熟悉的FragmentManager,其下有重中之重的递归方法:

public Fragment findFragmentByWho(String who) {
    if (mActive != null && who != null) {
        for (int i=mActive.size()-1; i>=0; i--) {
            Fragment f = mActive.get(i);
            if (f != null && (f=f.findFragmentByWho(who)) != null) {
                return f;
            }
        }
    }
    return null;
}

当同时满足Fragment != null并且Fragment.findFragmentByWho(who)不为空时,返回的就是需要接收数据的Fragment了。
继续看源码,这次就看Fragment下面的findFragmentByWho(String who)了:

Fragment findFragmentByWho(String who) {
    if (who.equals(mWho)) {
        return this;
    }
    if (mChildFragmentManager != null) {
        return mChildFragmentManager.findFragmentByWho(who);
    }
    return null;
}

从这个方法中我们可以看到,如果当前的Fragment已经是你需要找到的那个,就直接返回,如果还不是,那就继续在它的子类中继续寻找,即mChildFragmentManager.findFragmentByWho(who),继续调用FragmentManager下的findFragmentByWho(String who)方法。通过调用递归方法,最终找到目标Fragment并返回。

总结

通过以上源码分析,我们可以知道,谷歌已经解决了子Fragment不能调用OnActivityResult()回调方法的bug,我们再也不用自己再自己解决这个问题,感觉好高兴!

另附低版本SDK解决的方法:
可以参考鸿军大神的博文:http://blog.csdn.net/shuaihj/article/details/46663109

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值