6
6
7
7
* 博文地址:[ 一句代码搞定权限请求,从未如此简单] ( https://www.jianshu.com/p/c69ff8a445ed )
8
8
9
- * 点击此处 [ 下载 Demo] ( XXPermissions.apk ) 进行演示或者测试
9
+ * 可以扫码下载 Demo 进行演示或者测试,如果扫码下载不了的,[ 点击此处可直接下载] ( XXPermissions.apk )
10
+
11
+ ![ ] ( picture/demo_code.png )
10
12
11
13
* 另外想对 Android 6.0 权限需要深入了解的,可以看这篇文章[ Android 6.0 运行权限解析] ( https://www.jianshu.com/p/6a4dff744031 )
12
14
@@ -43,7 +45,7 @@ android {
43
45
44
46
dependencies {
45
47
// 权限请求框架:https://github.com/getActivity/XXPermissions
46
- implementation 'com.github.getActivity:XXPermissions:12.5 '
48
+ implementation 'com.github.getActivity:XXPermissions:12.6 '
47
49
}
48
50
```
49
51
@@ -175,7 +177,7 @@ XXPermissions.startPermissionActivity(Fragment fragment, String... permissions);
175
177
176
178
| 功能及细节 | [ XXPermissions] ( https://github.com/getActivity/XXPermissions ) | [ AndPermission] ( https://github.com/yanzhenjie/AndPermission ) | [ PermissionX] ( https://github.com/guolindev/PermissionX ) | [ AndroidUtilCode] ( https://github.com/Blankj/AndroidUtilCode ) | [ RxPermissions] ( https://github.com/tbruyelle/RxPermissions ) | [ PermissionsDispatcher] ( https://github.com/permissions-dispatcher/PermissionsDispatcher ) | [ EasyPermissions] ( https://github.com/googlesamples/easypermissions ) |
177
179
| :--------: | :------------: | :------------: | :------------: | :------------: | :------------: | :------------: | :------------: |
178
- | 对应版本 | 12.5 | 2.0.3 | 1.6.0 | 1.30.6 | 0.12 | 4.9.1 | 3.0.0 |
180
+ | 对应版本 | 12.6 | 2.0.3 | 1.6.1 | 1.30.6 | 0.12 | 4.9.1 | 3.0.0 |
179
181
| issues 数 | [ ![ ] ( https://img.shields.io/github/issues/getActivity/XXPermissions.svg )] ( https://github.com/getActivity/XXPermissions/issues ) | [ ![ ] ( https://img.shields.io/github/issues/yanzhenjie/AndPermission.svg )] ( https://github.com/yanzhenjie/AndPermission/issues ) | [ ![ ] ( https://img.shields.io/github/issues/guolindev/PermissionX.svg )] ( https://github.com/guolindev/PermissionX/issues ) | [ ![ ] ( https://img.shields.io/github/issues/Blankj/AndroidUtilCode.svg )] ( https://github.com/Blankj/AndroidUtilCode/issues ) | [ ![ ] ( https://img.shields.io/github/issues/tbruyelle/RxPermissions.svg )] ( https://github.com/tbruyelle/RxPermissions/issues ) | [ ![ ] ( https://img.shields.io/github/issues/permissions-dispatcher/PermissionsDispatcher.svg )] ( https://github.com/permissions-dispatcher/PermissionsDispatcher/issues ) | [ ![ ] ( https://img.shields.io/github/issues/googlesamples/easypermissions.svg )] ( https://github.com/googlesamples/easypermissions/issues ) |
180
182
| 框架体积 | 28 KB | 127 KB | 78 KB | 500 KB | 28 KB | 91 KB | 48 KB |
181
183
| 安装包权限 | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
@@ -190,7 +192,7 @@ XXPermissions.startPermissionActivity(Fragment fragment, String... permissions);
190
192
| 新权限自动兼容旧设备 | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
191
193
| 屏幕方向旋转场景适配 | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ |
192
194
| 后台申请权限场景适配 | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
193
- | 低级错误检测机制 | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
195
+ | 错误检测机制 | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
194
196
195
197
#### 新权限自动兼容旧设备介绍
196
198
@@ -206,25 +208,37 @@ XXPermissions.startPermissionActivity(Fragment fragment, String... permissions);
206
208
207
209
* 当系统权限申请对话框弹出后对 Activity 进行屏幕旋转,会导致权限申请回调失效,因为屏幕旋转会导致框架中的 Fragment 销毁重建,这样会导致里面的回调对象直接被回收,最终导致回调不正常。解决方案有几种,一是在清单文件中添加 `android:configChanges="orientation"` 属性,这样屏幕旋转时不会导致 Activity 和 Fragment 销毁重建,二是直接在清单文件中固定 Activity 显示的方向,但是以上两种方案都要使用框架的人处理,这样显然是不够灵活的,解铃还须系铃人,框架的问题应当由框架来解决,而 **RxPermissions** 的解决方式是给 PermissionFragment 对象设置 `fragment.setRetainInstance(true)`,这样就算屏幕旋转了,Activity 对象会销毁重建,而 Fragment 也不会跟着销毁重建,还是复用着之前那个对象,但是存在一个问题,如果 Activity 重写了 **onSaveInstanceState** 方法会直接导致这种方式失效,这样做显然只是治标不治本,而 **XXPermissions** 的方式会更直接点,在 **PermissionFragment** 绑定到 Activity 上面时,把当前 Activity 的**屏幕方向固定住**,在权限申请结束后再把**屏幕方向还原回去**。
208
210
211
+ * 在所有的权限请求框架中,只要使用了 Fragment 申请权限都会出现这个问题,而 AndPermission 其实是通过创建新的 Activity 来申请权限,所以不会出现这个问题,PermissionsDispatcher 则是采用了 APT 生成代码的形式来申请权限,所以也没有这个问题,而 PermissionX 则是直接借鉴了 XXPermissions 的解决方案,详情请见 [ XXPermissions/issues/49] ( https://github.com/getActivity/XXPermissions/issues/49 ) 、[ PermissionX/issues/51] ( https://github.com/guolindev/PermissionX/issues/51 ) 。
212
+
209
213
#### 后台申请权限场景介绍
210
214
211
215
* 当我们做耗时操作之后申请权限(例如在闪屏页获取隐私协议再申请权限),在网络请求的过程中将 Activity 返回桌面去(退到后台),然后会导致权限请求是在后台状态中进行,在这个时机上就可能会导致权限申请不正常,表现为不会显示授权对话框,处理不当的还会导致崩溃,例如 [RxPeremission/issues/249](https://github.com/tbruyelle/RxPermissions/issues/249)。原因在于框架中的 PermissionFragment 在 **commit / commitNow** 到 Activity 的时候会做一个检测,如果 Activity 的状态是不可见时则会抛出异常,而 **RxPeremission** 正是使用了 **commitNow** 才会导致崩溃 ,使用 **commitAllowingStateLoss / commitNowAllowingStateLoss** 则可以避开这个检测,虽然这样可以避免崩溃,但是会出现另外一个问题,系统提供的 **requestPermissions** API 在 Activity 不可见时调用也不会弹出授权对话框,**XXPermissions** 的解决方式是将 **requestPermissions** 时机从 **create** 转移到了 **resume**,因为 Activity 和 Fragment 的生命周期方法是捆绑在一起的,如果 Activity 是不可见的,那么就算创建了 Fragment 也只会调用 **onCreate** 方法,而不会去调用它的 **onResume** 方法,最后当 Activity 从后台返回到前台时,不仅会触发 **Activity.onResume** 方法,同时也会触发 **PermissionFragment** 的 **onResume** 方法,在这个方法申请权限就可以保证最终 **requestPermissions** 申请的时机是在 Activity **处于可见状态的情况**下。
212
216
213
- #### 低级错误检测机制介绍
217
+ #### 错误检测机制介绍
218
+
219
+ * 在框架的日常维护中,有很多人跟我反馈过框架有 Bug,但是经过排查和定位发现,这其中有 95% 的问题来自于调用者一些不规范操作导致的,这不仅对我造成很大的困扰,同时也极大浪费了很多小伙伴的时间和精力,于是我在框架中加入了很多审查元素,在 ** debug 模式** 、** debug 模式** 、** debug 模式** 下,一旦有某些操作不符合规范,那么框架会直接抛出异常给调用者,并在异常信息中正确指引调用者纠正错误,例如:
220
+
221
+ * 传入的 Context 实例不是 Activity 对象,框架会抛出异常,又或者传入的 Activity 的状态异常(已经 ** Finishing** 或者 ** Destroyed** ),这种情况一般是在异步申请权限导致的,框架也会抛出异常,请在合适的时机申请权限,如果申请的时机无法预估,请在外层做好 Activity 状态判断再进行权限申请。
222
+
223
+ * 如果调用者没有传入任何权限就申请权限的话,框架会抛出异常,又或者如果调用者传入的权限不是危险权限或者特殊权限,框架也会抛出异常,因为有的人会把普通权限当做危险权限传给框架,系统会直接拒绝。
224
+
225
+ * 如果当前项目在没有适配分区存储的情况下,申请 ` READ_EXTERNAL_STORAGE ` 和 ` WRITE_EXTERNAL_STORAGE ` 权限
226
+
227
+ * 当项目的 ` targetSdkVersion >= 29 ` 时,需要在清单文件中注册 ` android:requestLegacyExternalStorage="true" ` 属性,否则框架会抛出异常,如果不加会导致一个问题,明明已经获取到存储权限,但是无法在 Android 10 的设备上面正常读写外部存储上的文件。
214
228
215
- * 在框架的日常维护中,有很多人跟我反馈过框架有 Bug,但是经过排查和定位发现,这其中有 95% 的问题来自于调用者一些不规范操作导致的,这不仅对我造成很大的困扰,同时也极大浪费了很多小伙伴的时间和精力,于是我在框架中加入了很多审查元素,在 ** Debug ** 模式下,一旦有某些操作不符合规范,那么框架会直接抛出异常给调用者,并在异常信息中正确指引调用者纠正错误,例如:
229
+ * 当项目的 ` targetSdkVersion >= 30 ` 时,则不能申请 ` READ_EXTERNAL_STORAGE ` 和 ` WRITE_EXTERNAL_STORAGE ` 权限,而是应该申请 ` MANAGE_EXTERNAL_STORAGE ` 权限
216
230
217
- 1 . 传入的 Context 实例不是 Activity 对象,框架会抛出异常,又或者传入的 Activity 的状态异常(已经 ** Finishing ** 或者 ** Destroyed ** ),这种情况一般是在异步申请权限导致的,框架也会抛出异常,请在合适的时机申请权限,如果申请的时机无法预估,请在外层做好 Activity 状态判断再进行权限申请。
231
+ * 如果当前项目已经适配了分区存储,那么只需要在清单文件中注册一个 meta-data 属性即可: ` <meta-data android:name="ScopedStorage" android:value="true" /> `
218
232
219
- 2 . 如果调用者没有传入任何权限就申请权限的话,框架会抛出异常,如果调用者传入的权限不是危险权限或者特殊权限,框架也会抛出异常,因为有的人会把普通权限当做危险权限传给框架,系统会直接拒绝 。
233
+ * 如果申请的权限中包含后台定位权限, 那么这里面则不能包含和定位无关的权限,否则框架会抛出异常,因为 ` ACCESS_BACKGROUND_LOCATION ` 和其他非定位权限定位掺杂在一起申请,在 Android 11 上会出现不申请直接被拒绝的情况 。
220
234
221
- 3 . 如果在没有适配分区存储的情况下申请外部存储权限,必须在清单文件中注册 ` android:requestLegacyExternalStorage="true" ` ,否则框架会抛出异常,如果不加会导致获取到存储权限,但是无法在 Android 10 的设备上面正常读写外部存储上的文件 。
235
+ * 如果申请的权限和项目中的 ** targetSdkVersion ** 对不上,框架会抛出异常,是因为 ** targetSdkVersion ** 代表着项目适配到哪个 Android 版本,系统会自动做向下兼容,假设申请的权限是 Android 11 才出现的,但是 ** targetSdkVersion ** 还停留在 29,那么在某些机型上的申请,会出现授权异常的情况,也就是用户明明授权了,但是系统返回的始终是 false 。
222
236
223
- 4 . 如果申请的权限中包含后台定位权限, 那么这里面则不能包含和定位无关的权限,否则框架会抛出异常,因为 ` ACCESS_BACKGROUND_LOCATION ` 和其他非定位权限定位掺和在一起申请,在 Android 11 上会出现不申请直接被拒绝的情况 。
237
+ * 如果动态申请的权限没有在 ` AndroidManifest.xml ` 中进行注册,框架会抛出异常,因为如果不这么做,是可以进行申请权限,但是不会出现授权弹窗,直接被系统拒绝,并且系统不会给出任何弹窗和提示,并且这个问题在每个机型上面都是 ** 必现的 ** 。
224
238
225
- 5 . 如果申请的权限和项目中的 ** targetSdkVersion ** 对不上 ,框架会抛出异常,是因为 ** targetSdkVersion ** 代表着项目适配到哪个 Android 版本,系统会自动做向下兼容,假设申请的权限是 Android 11 才出现的,但是 ** targetSdkVersion ** 还停留在 29,那么在某些机型上的申请,会出现授权异常的情况,也就是用户明明授权了,但是系统返回的始终是 false 。
239
+ * 如果动态申请的权限有在 ` AndroidManifest.xml ` 中进行注册,但是设定了不恰当的 ` android:maxSdkVersion ` 属性值 ,框架会抛出异常,举个例子: ` <uses-permission android:name="xxxx" android:maxSdkVersion="29" /> ` ,这样的设定会导致在 Android 11 ( ` Build.VERSION.SDK_INT >= 30 ` )及以上的设备申请权限,系统会认为这个权限没有在清单文件中注册,直接拒绝本次的权限申请,并且也是不会给出任何弹窗和提示,这个问题也是必现的 。
226
240
227
- 6 . 如果动态申请的权限没有进行在清单文件中注册,那么框架会抛出异常,因为如果不这么做,是可以进行申请权限,但是不会出现授权弹窗,直接被系统拒绝,并且系统不会给出任何弹窗和提示,并且这个问题在每个机型上面都是 ** 必现的 ** 。
241
+ * 如果你不需要上面这些检测,可通过设置 ` XXPermissions.setDebugMode(false) ` 来关闭,但是需要注意的是,我并不建议你去关闭这个检测,因为在 ** release 模式 ** 时它是关闭状态,不需要你手动关闭,而它只在 ** debug 模式 ** 下才会触发这些检测 。
228
242
229
243
* 出现这些问题的原因是,我们对这些机制不太熟悉,而如果框架不加以限制,那么引发各种奇奇怪怪的问题出现,作为框架的作者,表示不仅你们很痛苦,作为框架作者表示也很受伤。因为这些问题不是框架导致的,而是调用者的某些操作不规范导致的。我觉得这个问题最好的解决方式是,由框架做统一的检查,因为我是框架的作者,对权限申请这块知识点有** 较强的专业能力和足够的经验** ,知道什么该做,什么不该做,这样就可以对这些骚操作进行一一拦截。
230
244
@@ -244,7 +258,7 @@ XXPermissions.startPermissionActivity(Fragment fragment, String... permissions);
244
258
245
259
* 向下兼容属性:新权限在旧系统可以正常申请,框架会做自动适配,无需调用者适配
246
260
247
- * 自动检测错误:如果出现低级错误框架会主动抛出异常给调用者 (仅在 Debug 下判断,把 Bug 扼杀在摇篮中)
261
+ * 自动检测错误:如果出现错误框架会主动抛出异常给调用者 (仅在 Debug 下判断,把 Bug 扼杀在摇篮中)
248
262
249
263
#### 作者的其他开源项目
250
264
0 commit comments