前言
感谢大家对这篇文章的支持,我们将深入研究安卓中常见的兼容性问题的原因以及解决方案,主要目录如下:
- 如何回调 Fragment 的 onActivityResult()方法;
- 监听 ScrollView 滑动底部的兼容问题;
- WebView的兼容问题
- Android 5.0 监听网络;
- Android 7.0 文件共享;
- Android 8.0 安装 Apk;
- Android 8.0 发送通知
- RelativeLayout 常见的布局问题。
- 其他
我们来依次研究这些兼容性问题。
###正文
Fragment 的 onActivityResult() 的不回调问题
首先我们写一个小例子帮助我们分析出现这个问题的原因:
创建 MainActivity,内部使用了 MainFragment,在MainFragment中点击按钮,跳转到另外一个 Activity,在 MainActivity 中也有一个按钮,点击也会跳转到另一个 Activity。
创建 SecondActivity,打开一秒后销毁,返回新的 String。
先了解一下我们的布局:
这两个跳转的区别是:分别调用了 Fragment.startActityForResult() 和 Activity.startActivityForResult()。
在 MainFragment 中重写 onActivityResult() 方法,显示返回的字符串:
首先我们点击 Fragment 的按钮,看一下结果:
很明显,我们得到了 SecondActivity 中的字符串,说明调用了 Fragment.startActivityForResult(),在 Fragment 的 onActivityResult() 是可以得到结果的。
然后我们再点击 MainActivity 的按钮,看一下结果:
没有效果!?惊讶之余后仔细思考,把 Activity 和 Fragment 的 startActivityForResult 分开处理,这种设计也是合理的。
我们在开发中大部分情况都是因为在 Fragment 中调用 Activity 的 startActivityForResult 方法,从而引起 onActivityForResult 失效。
例如我曾经写过的代码,封装从手机相册选择图片的公共方法:
因为参数的类型是 Activity,所以 Fragment 调用这个方法的时候是无法回调 onActivityResult 的。
那安卓是怎么做到区分 Fragment 和 Activity 的 startActivityForResult 的呢?我们从源码看一下从 startActivityForResult 到 onActivityResult 的主要流程:
首先 Fragment 的 startActivityForResult 最终调用的是 Fragment 的 startActivityFromFragment 方法:
其中被红线圈起来的地方是我们分析的重点:
- allocateRequestIndex 方法,主要把调用的 Fragment 通过键值对的方式保存起来,其中 key 是计算出来的索引值,value 是 Fragment 的 mWho 属性(在相同 FragmentManager 中,具有唯一性),等 onActivityResult 执行的时候,再取出这个 Fragment,执行他的 onActivityResult 方法。
- 下一个重点是对 RequestCode 进行了加工,安卓 SDK 区分回调的是 Activity 还是 Fragment,就是通过加工后的 RequestCode,所以即使双方使用了相同 RequestCode 也不会有影响。
- ActivityCompat.startActivityForResult 是对历史版本的兼容,调用的还是 Activity 的 startActivityForResult,所以说 RequestCode 加工是 Fragment 独有的。
当跳转的 Activity 销毁后,启动的 Activity 得到结果,执行了 onActivityResult:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
mFragments.noteStateNotSaved();
// 对加工过的requestCode进行反解
int requestIndex = requestCode>>16;
// 如果是Activity,requestIndex会等于0
if (requestIndex !=0) {
// ((requestIndex + 1) << 16)
// 因为加工的时候加了1,所以这里减1
requestIndex--;
// 通过requestIndex得到fragment.mWho属性
// 之前的allocateRequestIndex方法作用就在这里
String who = mPendingFragmentActivityResults.get(requestIndex);
mPendingFragmentActivityResults.remove(requestIndex);
if (who == null) {
Log.w(TAG, "Activity result delivered for unknown Fragment.");
return;
}
// fragment不为空,就执行fragment的onActivityResult
Fragment targetFragment = mFragments.findFragmentByWho(who);
if (targetFragment == null) {
Log.w(TAG, "Activity result no fragment exists for who: " + who);
} else {
// 这里还原了RequestCode
targetFragment.onActivityResult(requestCode & 0xffff, resultCode, data);
}
return;
}
// 执行Activity的onActivityResult流程
ActivityCompat.PermissionCompatDelegate delegate =
ActivityCompat.getPermissionCompatDelegate();
if (delegate != null && delegate.onActivityResult(this, requestCode, resultCode, data)) {