Fragment的onActivityResult方法为什么有的时候不回调

#前言

还有20天就要过年了,心情非常的激动,感觉自己已经要膨胀了,所以今天来讨论一下两个比较常见的问题,相信很多朋友都已经研究过了,不过年底了就当是复习了吧。

现在我们开始讨论今天的话题

(PS:请注意,我用的kotlin,并不是Java,但是我相信Java同学肯定看的懂)

#Fragment的onActivityResult方法

最近新项目里有这样一个使用场景:

在Activity里,嵌套了一个Fragment,这个Fragment用来从手机中选择照片。

val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
startActivityForResult(Intent.createChooser(intent, resources.getString(R.string.please_select_pic)), REQUEST_PICK)

写法还是很简单的,很幸运,我发现Fragment也是有onActivityResult方法,于是按照平时的套路写完了代码,结果非常成功

紧接着,出现了另一个需求:

编辑用户昵称,跳转到一个编辑Activity,返回显示新的用户昵称。

于是我满怀希望的按照刚才的套路写了一遍代码:

val intent = Intent(activity, EditShortInfoActivity::class.java)
intent.putExtra("title", title)
intent.putExtra("content", content)
intent.putExtra("length", length)
activity.startActivityForResult(intent, requestCode)

最终在编辑结束后,返回新的昵称:

val intent = Intent()
intent.putExtra("content", message)
setResult(Activity.RESULT_OK, intent)
finish()

赶紧运行测试一下,咦咦咦?为什么onActivityResult方法没有回调呢?

这不是再搞我吗?我十分气愤的吐槽中,首先打开了Fragment的onActivityResult方法:

public void onActivityResult(int requestCode, int resultCode, Intent data) {
}

空方法,说明Fragment内部没有做任何处理,不过问题应该不在这,Fragment被嵌套在Activity中,那么Activity调用了Fragment的可能性肯定是最大的,啥也别说了,看一下FragmentActivity的onActivity方法:

@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对象,那八成问题就在这了,我们先来分析一下代码:

int requestIndex = requestCode>>16;
我们的请求码右移了16位(相当于除了2的16次方,也就是65526),得到了一个索引值,这让我想到了之前我们研究过的哈西算法,然后得到对应的Fragment,如果你Fragment不等于空,就再把请求码再处理一次,然后回调onActivityResult方法。

我们先来断点看看在我第一次获取系统照片的时候都发生了什么?


首先我要告诉大家,我自己传的requestCode是101,但是在FragmentActivity的onActivityResult中竟然显示的是327781,那肯定是在调用相册的时候,系统把的请求码处理了,最终走到了Fragment的onActivityResult方法,


在我的EditUserInfoDetailsFragment得到的是101。

通过上面的研究,我们得出了结论,当我们在调用系统相册的时候,系统会把我们原来的请求码进行处理,然后再最终把处理过的请求码恢复,我们就可以通过判断我们原本的请求码,知道我们要进行的操作什么。

现在再来试试第二种情况,因为篇幅的缘故我就不贴图了,直接说出结果(其实是我懒,大家都懂的):

如果是app内部的startActivityForResult,是不会对请求码进行处理的,也就是说我们传的requestCode是什么,在FragmentActivity中得到也是什么,但是我们的值除以65536,那是肯定是等于0的,所以就什么也不执行了。

有些朋友说了,那我直接把我的请求码设置到65536以上不就得了?

我就说为什么你这么机智呢,我就赶紧实验了一下:


结果很凄凉,只能使用小于16位的requestCode,那就是说不可能大于65536,看来google比我们还机智。

那有什么办法解决这个问题呢?其实非常的简单,既然系统忽略了我们的请求码,那我们就自己分发请求码就好了:

/**
 * 重写此方法,子fragment才能出发回调onActivityResult
 */
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode != RESULT_OK) {
        return;
    }
    FragmentManager fm = getSupportFragmentManager();
    List<Fragment> fragments = fm.getFragments();
    if (fragments != null && fragments.size() > 0) {
        for (Fragment f : fragments) {
            f.onActivityResult(requestCode, resultCode, data);
        }
    }
}
我这里把resultCode不是ok的屏蔽了,当时你可以按照你自己的需求删掉就可以了,代码就不用解释了。

#总结

为什么android系统对我们的第三方app的请求码处理了呢?

我猜测可能有两点:

1、是为了系统安全,就好像7.0以上把文件的uri封装了是一样的道理。

2、可能是为了区别不同app请求了同一个app,这个值能靠猜了。

本来我今天是想讨论两个问题,没想到一写就这么长了,所以今天就结束了,如果大家对android为什么这么设计有更深入的了解,可以留言讨论,大家一起学习进步。

那就这样,祝大家生活愉快。




猜你喜欢

转载自blog.csdn.net/u011315960/article/details/79150271