7、权限与Fragment:Fragment中请求权限、onRequestPermissionsResult分发、兼容性处理
Fragment 里的权限请求,说实话,是很多 Android 开发者的“噩梦”。
为什么?因为 Fragment 的生命周期比 Activity 更复杂,而且权限回调的分发机制,稍不注意就会踩坑。我自己刚转 Android 那会儿,就因为在 Fragment 里直接调了 requestPermissions(),结果回调死活收不到……排查了半天,最后发现是 Activity 没做分发。
今天我们就来彻底搞定这件事。
7.1 Fragment 中请求权限的基本姿势
在 Fragment 里请求权限,其实和 Activity 很像。核心方法就是 requestPermissions(),但注意——这个方法 Fragment 自己就有,不需要去拿 Activity 的。
// 在 Fragment 中直接调用
requestPermissions(new String[]{Manifest.permission.CAMERA}, REQUEST_CODE_CAMERA);
嗯,这里有个细节:Fragment 的 requestPermissions() 内部其实调用了 Activity 的版本,但它会额外做一件事——把当前 Fragment 的引用传进去。这样回调才能正确分发到 Fragment。
我个人习惯在 Fragment 里定义一个常量来标记请求码,比如:
private static final int REQUEST_CODE_CAMERA = 100;
这样后面处理回调时,一眼就能看出是哪个权限。
7.2 onRequestPermissionsResult 的分发机制
这是最容易出问题的地方。你想想看,当用户点击“允许”或“拒绝”后,系统会回调 Activity 的 onRequestPermissionsResult()。但如果你的 Fragment 也等着这个回调,那 Activity 就必须手动把结果分发给 Fragment。
说白了,就是 Activity 要“转发”一下。
核心原则:Activity 必须调用 super.onRequestPermissionsResult(),否则 Fragment 收不到回调。
来看一个标准的 Activity 实现:
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
// 其他逻辑...
}
注意那个 super 调用。如果你忘了写,Fragment 里的 onRequestPermissionsResult() 永远不会被触发。我曾经在一个遗留项目里排查了整整一下午,最后发现就是 Activity 的 super 被注释掉了……
那 Fragment 这边怎么收呢?很简单:
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_CODE_CAMERA) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 权限已授予,打开相机
} else {
// 权限被拒绝
}
}
}
这里有个坑:如果 Fragment 嵌套了 Fragment(比如 ViewPager 里套 Fragment),那么子 Fragment 的权限回调需要父 Fragment 手动分发。系统不会自动帮你做这件事。
避坑指南:我曾经在多层 Fragment 嵌套时,子 Fragment 请求权限后回调丢失。后来发现,每一层 Fragment 的 onRequestPermissionsResult() 都必须调用 super,并且父 Fragment 要主动调用子 Fragment 的对应方法。否则回调就断了。
7.3 兼容性处理:Fragment 与 Support Library
如果你还在用旧的 android.app.Fragment(API 28 之前),那兼容性是个大问题。因为老版本的 Fragment 对权限回调的支持并不完善。
我个人强烈建议:统一使用 AndroidX 的 Fragment。也就是 androidx.fragment.app.Fragment。它内部已经处理好了权限分发的逻辑,你只需要保证 Activity 继承自 AppCompatActivity 或 FragmentActivity。
| Fragment 类型 | 是否需要手动分发 | 推荐度 |
|---|---|---|
| android.app.Fragment | 是(且容易出问题) | ❌ 不推荐 |
| androidx.fragment.app.Fragment | 否(自动分发) | ✅ 强烈推荐 |
如果你因为某些原因必须用老版本 Fragment,那记得在 Activity 里手动遍历所有 Fragment 并分发回调。代码大概长这样:
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
// 手动分发给所有 Fragment
List<Fragment> fragments = getFragmentManager().getFragments();
for (Fragment fragment : fragments) {
if (fragment != null) {
fragment.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
}
嗯,这代码看着就烦。所以还是用 AndroidX 吧。
7.4 知识体系图:Fragment 权限请求全流程
下面这张图,我画了 Fragment 请求权限的完整流程。你可以把它当作一个“检查清单”,每次写权限代码时对照一下。
小提示:如果你用 AndroidX,Activity 的 super 调用会自动把结果分发给所有关联的 Fragment。但如果你用了 FragmentPagerAdapter 或 FragmentStatePagerAdapter,记得检查 Fragment 是否还活着——有时候 Fragment 已经被销毁了,回调就没人收了。
7.5 实战中的几个常见问题
最后,我总结几个我在项目中真实遇到过的坑:
- 问题一:Fragment 里调了 requestPermissions,但回调没触发。
原因:Activity 没调super,或者 Fragment 被 detach 了。解决方案:检查 Activity 代码,确保 super 存在;同时确认 Fragment 在回调时仍处于 added 状态。 - 问题二:多个 Fragment 同时请求权限,回调混乱。
原因:请求码冲突。解决方案:每个 Fragment 使用独立的请求码,最好用System.identityHashCode()生成唯一码。 - 问题三:用户勾选“不再询问”后,权限弹窗不再弹出。
原因:系统行为如此。解决方案:调用shouldShowRequestPermissionRationale()判断,如果返回 false,引导用户去设置页手动开启。
嗯,关于 Fragment 的权限控制,核心就是三点:用 AndroidX、Activity 记得调 super、注意 Fragment 生命周期。把这三点刻在脑子里,基本不会出大问题。
好了,这一节就到这里。下一节我们会聊权限与 Service 的结合,那又是另一番风景了。