Skip to content

Commit 1e6ed55

Browse files
committed
优化框架中的一些重复代码
优化框架中的一些代码逻辑 优化框架文档及相关注释 优化屏幕旋转反方向的逻辑 提升读取清单文件的成功率 修改读取清单文件中权限的方式
1 parent 6e8acde commit 1e6ed55

17 files changed

+510
-428
lines changed

HelpDoc.md

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,10 @@ android
6969
* 最后直接调用下面这句代码
7070

7171
```java
72-
XXPermissions.with(this)
73-
// 不适配 Android 11 可以这样写
72+
XXPermissions.with(MainActivity.this)
73+
// 适配 Android 11 分区存储这样写
7474
//.permission(Permission.Group.STORAGE)
75-
// 适配 Android 11 需要这样写,这里无需再写 Permission.Group.STORAGE
75+
// 不适配 Android 11 分区存储这样写
7676
.permission(Permission.MANAGE_EXTERNAL_STORAGE)
7777
.request(new OnPermissionCallback() {
7878

@@ -82,17 +82,6 @@ XXPermissions.with(this)
8282
toast("获取存储权限成功");
8383
}
8484
}
85-
86-
@Override
87-
public void onDenied(List<String> permissions, boolean never) {
88-
if (never) {
89-
toast("被永久拒绝授权,请手动授予存储权限");
90-
// 如果是被永久拒绝就跳转到应用权限系统设置页面
91-
XXPermissions.startPermissionActivity(MainActivity.this, permissions);
92-
} else {
93-
toast("获取存储权限失败");
94-
}
95-
}
9685
});
9786
```
9887

README.md

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66

77
* 博文地址:[一句代码搞定权限请求,从未如此简单](https://www.jianshu.com/p/c69ff8a445ed)
88

9-
* 点击此处 [下载 Demo](XXPermissions.apk) 进行演示或者测试
9+
* 可以扫码下载 Demo 进行演示或者测试,如果扫码下载不了的,[点击此处可直接下载](XXPermissions.apk)
10+
11+
![](picture/demo_code.png)
1012

1113
* 另外想对 Android 6.0 权限需要深入了解的,可以看这篇文章[Android 6.0 运行权限解析](https://www.jianshu.com/p/6a4dff744031)
1214

@@ -43,7 +45,7 @@ android {
4345
4446
dependencies {
4547
// 权限请求框架:https://github.com/getActivity/XXPermissions
46-
implementation 'com.github.getActivity:XXPermissions:12.5'
48+
implementation 'com.github.getActivity:XXPermissions:12.6'
4749
}
4850
```
4951

@@ -175,7 +177,7 @@ XXPermissions.startPermissionActivity(Fragment fragment, String... permissions);
175177

176178
| 功能及细节 | [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) |
177179
| :--------: | :------------: | :------------: | :------------: | :------------: | :------------: | :------------: | :------------: |
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 |
179181
| 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) |
180182
| 框架体积 | 28 KB | 127 KB | 78 KB | 500 KB | 28 KB | 91 KB | 48 KB |
181183
| 安装包权限 ||||||||
@@ -190,7 +192,7 @@ XXPermissions.startPermissionActivity(Fragment fragment, String... permissions);
190192
| 新权限自动兼容旧设备 ||||||||
191193
| 屏幕方向旋转场景适配 ||||||||
192194
| 后台申请权限场景适配 ||||||||
193-
| 低级错误检测机制 ||||||||
195+
| 错误检测机制 ||||||||
194196

195197
#### 新权限自动兼容旧设备介绍
196198

@@ -206,25 +208,37 @@ XXPermissions.startPermissionActivity(Fragment fragment, String... permissions);
206208

207209
* 当系统权限申请对话框弹出后对 Activity 进行屏幕旋转,会导致权限申请回调失效,因为屏幕旋转会导致框架中的 Fragment 销毁重建,这样会导致里面的回调对象直接被回收,最终导致回调不正常。解决方案有几种,一是在清单文件中添加 `android:configChanges="orientation"` 属性,这样屏幕旋转时不会导致 Activity 和 Fragment 销毁重建,二是直接在清单文件中固定 Activity 显示的方向,但是以上两种方案都要使用框架的人处理,这样显然是不够灵活的,解铃还须系铃人,框架的问题应当由框架来解决,而 **RxPermissions** 的解决方式是给 PermissionFragment 对象设置 `fragment.setRetainInstance(true)`,这样就算屏幕旋转了,Activity 对象会销毁重建,而 Fragment 也不会跟着销毁重建,还是复用着之前那个对象,但是存在一个问题,如果 Activity 重写了 **onSaveInstanceState** 方法会直接导致这种方式失效,这样做显然只是治标不治本,而 **XXPermissions** 的方式会更直接点,在 **PermissionFragment** 绑定到 Activity 上面时,把当前 Activity 的**屏幕方向固定住**,在权限申请结束后再把**屏幕方向还原回去**。
208210

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+
209213
#### 后台申请权限场景介绍
210214

211215
* 当我们做耗时操作之后申请权限(例如在闪屏页获取隐私协议再申请权限),在网络请求的过程中将 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 **处于可见状态的情况**下。
212216

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 的设备上面正常读写外部存储上的文件。
214228

215-
* 在框架的日常维护中,有很多人跟我反馈过框架有 Bug,但是经过排查和定位发现,这其中有 95% 的问题来自于调用者一些不规范操作导致的,这不仅对我造成很大的困扰,同时也极大浪费了很多小伙伴的时间和精力,于是我在框架中加入了很多审查元素,在 **Debug** 模式下,一旦有某些操作不符合规范,那么框架会直接抛出异常给调用者,并在异常信息中正确指引调用者纠正错误,例如:
229+
* 当项目的 `targetSdkVersion >= 30` 时,则不能申请 `READ_EXTERNAL_STORAGE``WRITE_EXTERNAL_STORAGE` 权限,而是应该申请 `MANAGE_EXTERNAL_STORAGE` 权限
216230

217-
1. 传入的 Context 实例不是 Activity 对象,框架会抛出异常,又或者传入的 Activity 的状态异常(已经 **Finishing** 或者 **Destroyed**),这种情况一般是在异步申请权限导致的,框架也会抛出异常,请在合适的时机申请权限,如果申请的时机无法预估,请在外层做好 Activity 状态判断再进行权限申请。
231+
* 如果当前项目已经适配了分区存储,那么只需要在清单文件中注册一个 meta-data 属性即可: `<meta-data android:name="ScopedStorage" android:value="true" />`
218232

219-
2. 如果调用者没有传入任何权限就申请权限的话,框架会抛出异常,如果调用者传入的权限不是危险权限或者特殊权限,框架也会抛出异常,因为有的人会把普通权限当做危险权限传给框架,系统会直接拒绝
233+
* 如果申请的权限中包含后台定位权限, 那么这里面则不能包含和定位无关的权限,否则框架会抛出异常,因为 `ACCESS_BACKGROUND_LOCATION` 和其他非定位权限定位掺杂在一起申请,在 Android 11 上会出现不申请直接被拒绝的情况
220234

221-
3. 如果在没有适配分区存储的情况下申请外部存储权限,必须在清单文件中注册 `android:requestLegacyExternalStorage="true"`,否则框架会抛出异常,如果不加会导致获取到存储权限,但是无法在 Android 10 的设备上面正常读写外部存储上的文件
235+
* 如果申请的权限和项目中的 **targetSdkVersion** 对不上,框架会抛出异常,是因为 **targetSdkVersion** 代表着项目适配到哪个 Android 版本,系统会自动做向下兼容,假设申请的权限是 Android 11 才出现的,但是 **targetSdkVersion** 还停留在 29,那么在某些机型上的申请,会出现授权异常的情况,也就是用户明明授权了,但是系统返回的始终是 false
222236

223-
4. 如果申请的权限中包含后台定位权限, 那么这里面则不能包含和定位无关的权限,否则框架会抛出异常,因为 `ACCESS_BACKGROUND_LOCATION` 和其他非定位权限定位掺和在一起申请,在 Android 11 上会出现不申请直接被拒绝的情况
237+
* 如果动态申请的权限没有在 `AndroidManifest.xml` 中进行注册,框架会抛出异常,因为如果不这么做,是可以进行申请权限,但是不会出现授权弹窗,直接被系统拒绝,并且系统不会给出任何弹窗和提示,并且这个问题在每个机型上面都是**必现的**
224238

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`)及以上的设备申请权限,系统会认为这个权限没有在清单文件中注册,直接拒绝本次的权限申请,并且也是不会给出任何弹窗和提示,这个问题也是必现的
226240

227-
6. 如果动态申请的权限没有进行在清单文件中注册,那么框架会抛出异常,因为如果不这么做,是可以进行申请权限,但是不会出现授权弹窗,直接被系统拒绝,并且系统不会给出任何弹窗和提示,并且这个问题在每个机型上面都是**必现的**
241+
* 如果你不需要上面这些检测,可通过设置 `XXPermissions.setDebugMode(false)` 来关闭,但是需要注意的是,我并不建议你去关闭这个检测,因为在 **release 模式** 时它是关闭状态,不需要你手动关闭,而它只在 **debug 模式** 下才会触发这些检测
228242

229243
* 出现这些问题的原因是,我们对这些机制不太熟悉,而如果框架不加以限制,那么引发各种奇奇怪怪的问题出现,作为框架的作者,表示不仅你们很痛苦,作为框架作者表示也很受伤。因为这些问题不是框架导致的,而是调用者的某些操作不规范导致的。我觉得这个问题最好的解决方式是,由框架做统一的检查,因为我是框架的作者,对权限申请这块知识点有**较强的专业能力和足够的经验**,知道什么该做,什么不该做,这样就可以对这些骚操作进行一一拦截。
230244

@@ -244,7 +258,7 @@ XXPermissions.startPermissionActivity(Fragment fragment, String... permissions);
244258

245259
* 向下兼容属性:新权限在旧系统可以正常申请,框架会做自动适配,无需调用者适配
246260

247-
* 自动检测错误:如果出现低级错误框架会主动抛出异常给调用者(仅在 Debug 下判断,把 Bug 扼杀在摇篮中)
261+
* 自动检测错误:如果出现错误框架会主动抛出异常给调用者(仅在 Debug 下判断,把 Bug 扼杀在摇篮中)
248262

249263
#### 作者的其他开源项目
250264

XXPermissions.apk

861 Bytes
Binary file not shown.

app/build.gradle

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ android {
77
applicationId "com.hjq.permissions.demo"
88
minSdkVersion 14
99
targetSdkVersion 31
10-
versionCode 1250
11-
versionName "12.5"
10+
versionCode 1260
11+
versionName "12.6"
1212
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
1313
}
1414

@@ -62,7 +62,7 @@ dependencies {
6262
implementation 'com.android.support:appcompat-v7:28.0.0'
6363

6464
// 吐司框架:https://github.com/getActivity/ToastUtils
65-
implementation 'com.github.getActivity:ToastUtils:9.5'
65+
implementation 'com.github.getActivity:ToastUtils:9.6'
6666

6767
// 内存泄漏检测:https://github.com/square/leakcanary
6868
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'

app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
<uses-permission android:name="android.permission.WRITE_SETTINGS" tools:ignore="ProtectedPermissions" />
1818

1919
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
20-
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
20+
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
2121
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
2222

2323
<uses-permission android:name="android.permission.RECORD_AUDIO" />

app/src/main/java/com/hjq/permissions/demo/MainActivity.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
* author : Android 轮子哥
1919
* github : https://github.com/getActivity/XXPermissions
2020
* time : 2018/06/15
21-
* desc : Demo 演示
21+
* desc : 权限申请演示
2222
*/
2323
public final class MainActivity extends AppCompatActivity implements View.OnClickListener {
2424

@@ -129,9 +129,9 @@ public void onGranted(List<String> permissions, boolean all) {
129129
@Override
130130
public void run() {
131131
XXPermissions.with(MainActivity.this)
132-
// 不适配 Android 11 可以这样写
132+
// 适配 Android 11 分区存储这样写
133133
//.permission(Permission.Group.STORAGE)
134-
// 适配 Android 11 需要这样写,这里无需再写 Permission.Group.STORAGE
134+
// 不适配 Android 11 分区存储这样写
135135
.permission(Permission.MANAGE_EXTERNAL_STORAGE)
136136
.request(new OnPermissionCallback() {
137137

0 commit comments

Comments
 (0)