6
6
7
7
* [ 什么情况下需要适配分区存储特性] ( #什么情况下需要适配分区存储特性 )
8
8
9
+ * [ Android 11 授予了安装权限之后为什么应用重启了] ( #android-11-授予了安装权限之后为什么应用重启了 )
10
+
9
11
* [ 为什么授予了存储权限但是权限设置页还是显示未授权] ( #为什么授予了存储权限但是权限设置页还是显示未授权 )
10
12
11
13
* [ 我想在申请前和申请后统一弹对话框该怎么处理] ( #我想在申请前和申请后统一弹对话框该怎么处理 )
24
26
25
27
* [ 怎么处理权限请求成功但是返回空白通行证的问题] ( #怎么处理权限请求成功但是返回空白通行证的问题 )
26
28
29
+ * [ 为什么授权了还是无法访问 Android/data 目录下的文件] ( #为什么授权了还是无法访问-android-data-目录下的文件 )
30
+
31
+ * [ 如何应对国内某些应用商店在明确拒绝权限后 48 小时内不允许再次申请的问题] ( #如何应对国内某些应用商店在明确拒绝权限后-48-小时内不允许再次申请的问题 )
32
+
27
33
#### Android 11 定位权限适配
28
34
29
35
* 在 Android 10 上面,定位权限被划分为前台权限(精确和模糊)和后台权限,而到了 Android 11 上面,需要分别申请这两种权限,如果同时申请这两种权限会** 惨遭系统无情拒绝** ,连权限申请对话框都不会弹,立马被系统拒绝,直接导致定位权限申请失败。
@@ -93,11 +99,11 @@ XXPermissions.with(MainActivity.this)
93
99
94
100
#### 什么情况下需要适配分区存储特性
95
101
96
- * 如果你的应用需要上架 GooglePlay,那么需要详细查看:[ 谷歌应用商店政策(需要翻墙)] ( https://support.google.com/googleplay/android-developer/answer/9956427 )
102
+ * 如果你的应用需要上架 GooglePlay,那么需要详细查看:[ 谷歌应用商店政策(需要翻墙)] ( https://support.google.com/googleplay/android-developer/answer/9956427 ) 、 [ Google Play 通知 ] ( https://developer.android.google.cn/training/data-storage/manage-all-files#all-files-access-google-play )
97
103
98
104
* 分区存储的由来:谷歌之前收到了很多用户投诉,说很多应用都在 SD 卡下创建目录和文件,导致用户管理手机文件非常麻烦(强迫症的外国网友真多,哈哈),所以在 Android 10 版本更新中,谷歌要求所有开发者将媒体文件存放在自己内部目录或者 SD 卡内部目录中,不过谷歌在一版本上采取了宽松政策,在清单文件中加入 ` android:requestLegacyExternalStorage="true" ` 即可跳过这一特性的适配,不过在 Android 11 上面,你有两种选择:
99
105
100
- 1 . 适配分区存储:这个是谷歌推荐的一种方式,但是会增加工作量,因为分区存储适配起来十分麻烦,我个人感觉是这样的。不过对于一些特定应用,例如文件管理器,文件备份工具,防病毒应用等这类应用它们就一定需要用到外部存储 ,这个时候就需要用第二种方式来实现了。
106
+ 1 . 适配分区存储:这个是谷歌推荐的一种方式,但是会增加工作量,因为分区存储适配起来十分麻烦,我个人感觉是这样的。不过对于一些特定应用,例如文件管理器、备份和恢复应用、防病毒应用、文档管理应用、设备上的文件搜索、磁盘和文件加密、设备到设备数据迁移等这类应用它们就一定需要用到外部存储 ,这个时候就需要用第二种方式来实现了。
101
107
102
108
2 . 申请外部存储权限:这个是谷歌不推荐的一种方式,只需要 ` MANAGE_EXTERNAL_STORAGE ` 权限即可,适配起来基本无压力,但是会存在一个问题,就是上架谷歌应用市场的时候,要经过 Google Play 审核和批准。
103
109
@@ -107,6 +113,14 @@ XXPermissions.with(MainActivity.this)
107
113
108
114
2 . 如果你的应用只上架国内的应用市场,并且后续也没有上架谷歌应用市场的需要,那么你也可以直接申请 ` MANAGE_EXTERNAL_STORAGE ` 权限来读写外部存储
109
115
116
+ #### Android 11 授予了安装权限之后为什么应用重启了
117
+
118
+ * [ Android 11 特性调整,安装外部来源应用需要重启 App] ( https://cloud.tencent.com/developer/news/637591 )
119
+
120
+ * 先说结论,这个问题是 Android 11 的新特性,并非框架的问题导致的,当然这个问题是没有办法规避的,因为应用是被系统杀死的,应用的等级肯定不如系统的高,目前行业对这块也没有解决方案,如果你有好的解决方案,欢迎你提供给我。
121
+
122
+ * 另外经过实践,这个问题在 Android 12 上面已经不会再出现,证明问题已经被谷歌修复了。
123
+
110
124
#### 为什么授予了存储权限但是权限设置页还是显示未授权
111
125
112
126
* 首先我需要先纠正大家一个错误的想法,` READ_EXTERNAL_STORAGE ` 、` WRITE_EXTERNAL_STORAGE ` 这两个权限和 ` MANAGE_EXTERNAL_STORAGE ` 权限是两码事,虽然都叫存储权限,但是属于两种完全不同的权限,你如果申请的是 ` MANAGE_EXTERNAL_STORAGE ` 权限,并且授予了权限,但是在权限设置页并没有看到已授予,请注意这种情况是正常的,因为你在权限设置页看到的是存储授予状态是 ` READ_EXTERNAL_STORAGE ` 、` WRITE_EXTERNAL_STORAGE ` 权限状态的,而不是 ` MANAGE_EXTERNAL_STORAGE ` 权限状态的,但是这个时候已经获取到存储权限了,你大可不必管权限设置页显示的权限状态,直接读写文件即可,不会有权限问题的。
@@ -284,3 +298,53 @@ public class PermissionActivity extends AppCompatActivity implements OnPermissio
284
298
285
299
* 此问题无解,权限请求框架只能帮你申请权限,至于你申请权限做什么操作,框架无法知道,也无法干预,还有返回空白通行证是厂商自己的行为,目的就是为了保护用户的隐私,因为在某些应用上面不给权限就不能用,返回空白通行证是为了规避这种情况的发生。你要问我怎么办?我只能说胳膊拗不过大腿,别做一些无谓的抵抗。
286
300
301
+ #### 为什么授权了还是无法访问 Android/data 目录下的文件
302
+
303
+ * 首先无论你申请了哪种存储权限,在 Android 11 上面就是无法直接读取 Android/data 目录的,这个是 Android 11 上的新特性,需要你进行额外适配,具体适配流程可以参考这个开源项目 [ https://github.com/getActivity/AndroidVersionAdapter ] ( https://github.com/getActivity/AndroidVersionAdapter )
304
+
305
+ #### 如何应对国内某些应用商店在明确拒绝权限后 48 小时内不允许再次申请的问题
306
+
307
+ * 首先这种属于业务逻辑的问题,框架本身是不会做这种事情的,但并非不能实现,这得益于框架良好的设计,框架内部提供了一个叫 IPermissionInterceptor 的拦截器类,当前有权限申请的时候,会走 requestPermissions 方法的回调,你可以重写这个方法的逻辑,先去判断要申请的权限是否在 48 小时内已经申请过了一次了,如果没有的话,就走权限申请的流程,如果有的话,那么就直接回调权限申请失败的方法。
308
+
309
+ ``` java
310
+ public final class PermissionInterceptor implements IPermissionInterceptor {
311
+
312
+ private static final String SP_NAME_PERMISSION_REQUEST_TIME_RECORD = " permission_request_time_record" ;
313
+
314
+ @Override
315
+ public void requestPermissions (Activity activity , List<String > allPermissions , OnPermissionCallback callback ) {
316
+ SharedPreferences sharedPreferences = activity. getSharedPreferences(SP_NAME_PERMISSION_REQUEST_TIME_RECORD , Context . MODE_PRIVATE );
317
+ String permissionKey = String . valueOf(allPermissions);
318
+ long lastRequestPermissionTime = sharedPreferences. getLong(permissionKey, 0 );
319
+ if (System . currentTimeMillis() - lastRequestPermissionTime <= 1000 * 60 * 60 * 24 * 2 ) {
320
+ List<String > deniedPermissions = XXPermissions . getDenied(activity, allPermissions);
321
+ List<String > grantedPermissions = new ArrayList<> (allPermissions);
322
+ grantedPermissions. removeAll(deniedPermissions);
323
+ deniedPermissions(activity, allPermissions, deniedPermissions, true , callback);
324
+ if (! grantedPermissions. isEmpty()) {
325
+ grantedPermissions(activity, allPermissions, grantedPermissions, false , callback);
326
+ }
327
+ return ;
328
+ }
329
+ sharedPreferences. edit(). putLong(permissionKey, System . currentTimeMillis()). apply();
330
+ // 如果之前没有申请过权限,或者距离上次申请已经超过了 48 个小时,则进行申请权限
331
+ IPermissionInterceptor . super . requestPermissions(activity, allPermissions, callback);
332
+ }
333
+
334
+ @Override
335
+ public void grantedPermissions (Activity activity , List<String > allPermissions , List<String > grantedPermissions , boolean all , OnPermissionCallback callback ) {
336
+ if (callback == null ) {
337
+ return ;
338
+ }
339
+ callback. onGranted(grantedPermissions, all);
340
+ }
341
+
342
+ @Override
343
+ public void deniedPermissions (Activity activity , List<String > allPermissions , List<String > deniedPermissions , boolean never , OnPermissionCallback callback ) {
344
+ if (callback == null ) {
345
+ return ;
346
+ }
347
+ callback. onDenied(deniedPermissions, never);
348
+ }
349
+ }
350
+ ```
0 commit comments