#前言
还有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对象,那八成问题就在这了,我们先来分析一下代码:
我们的请求码右移了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为什么这么设计有更深入的了解,可以留言讨论,大家一起学习进步。
那就这样,祝大家生活愉快。