当前位置: 首页 > news >正文

Android 10-11适配外部存储方案

Android Api 29 对文件和文件夹进行了重大更改。不允许使用外部存储,如下方法:

Environment.getExternalStorageDirectory() = /mnt/sdcard  
Environment.getExternalStoragePublicDirectory(“test”) = /mnt/sdcard/test

只能使用内部存储

getExternalFilesDir(“test”) = /mnt/sdcard/Android/data/com.my.app/files/test
getExternalFilesDir(null) = /mnt/sdcard/Android/data/com.my.app/files

但谷歌官方给了一个后门,在AndroidManifest.xml文件中application节点中加上android:requestLegacyExternalStorage="true"属性才可以访问沙盒路径下的数据

原来的项目就要重新适配

Android开发:filePath放在哪个文件夹

Environment.getDataDirectory() = /data
Environment.getDownloadCacheDirectory() = /cache  
外部存储
Environment.getExternalStorageDirectory() = /mnt/sdcard  
Environment.getExternalStoragePublicDirectory(“test”) = /mnt/sdcard/test
Environment.getRootDirectory() = /system
getPackageCodePath() = /data/app/com.my.app-1.apk
getPackageResourcePath() = /data/app/com.my.app-1.apk
getCacheDir() = /data/data/com.my.app/cache
getDatabasePath(“test”) = /data/data/com.my.app/databases/test
getDir(“test”, Context.MODE_PRIVATE) = /data/data/com.my.app/app_test
getExternalCacheDir() = /mnt/sdcard/Android/data/com.my.app/cache
应用内部存储
getExternalFilesDir(“test”) = /mnt/sdcard/Android/data/com.my.app/files/test
getExternalFilesDir(null) = /mnt/sdcard/Android/data/com.my.app/files
getFilesDir() = /data/data/com.my.app/files

参考文章:

安卓Enviroment类的详解-CSDN博客

权限申请

11以上添加如下权限:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

代码动态申请

public final class PermissionActivity extends AppCompatActivity {private static final int REQUEST_CODE = 1024;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);requestPermission();}private void requestPermission() {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {// 先判断有没有权限if (Environment.isExternalStorageManager()) {writeFile();} else {Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);intent.setData(Uri.parse("package:" + context.getPackageName()));startActivityForResult(intent, REQUEST_CODE);}} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {// 先判断有没有权限if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED &&ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {writeFile();} else {ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE);}} else {writeFile();}}@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);if (requestCode == REQUEST_CODE) {if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED &&ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {writeFile();} else {ToastUtils.show("存储权限获取失败");}}}@Overrideprotected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {super.onActivityResult(requestCode, resultCode, data);if (requestCode == REQUEST_CODE && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {if (Environment.isExternalStorageManager()) {writeFile();} else {ToastUtils.show("存储权限获取失败");}}}/*** 模拟文件写入*/private void writeFile() {ToastUtils.show("写入文件成功");}
}

动态申请代码三方框架XXPermissions

XXPermissions.with(this)// 不适配 Android 11 可以这样写//.permission(Permission.Group.STORAGE)// 适配 Android 11 需要这样写,这里无需再写 Permission.Group.STORAGE.permission(Permission.MANAGE_EXTERNAL_STORAGE).request(new OnPermissionCallback() {@Overridepublic void onGranted(List<String> permissions, boolean all) {if (all) {toast("获取存储权限成功");}}@Overridepublic void onDenied(List<String> permissions, boolean never) {if (never) {toast("被永久拒绝授权,请手动授予存储权限");// 如果是被永久拒绝就跳转到应用权限系统设置页面XXPermissions.startPermissionActivity(MainActivity.this, permissions);} else {toast("获取存储权限失败");}}});

项目地址:GitHub - getActivity/XXPermissions: Android 权限请求框架,已适配 Android 14

 // JitPack 远程仓库:https://jitpack.iomaven { url 'https://jitpack.io' }
   // 权限请求框架:https://github.com/getActivity/XXPermissionsimplementation 'com.github.getActivity:XXPermissions:18.5

# 表示将第三方库迁移到 AndroidX
android.enableJetifier = true

<manifest>

    <application>

        <!-- 告知 XXPermissions 当前项目已经适配了分区存储特性 -->
        <meta-data
            android:name="ScopedStorage"
            android:value="true" />

    </application>

</manifest>

框架扩展

XXPermissions.with(this)// 申请单个权限.permission(Permission.RECORD_AUDIO)// 申请多个权限.permission(Permission.Group.CALENDAR)// 设置权限请求拦截器(局部设置)//.interceptor(new PermissionInterceptor())// 设置不触发错误检测机制(局部设置)//.unchecked().request(new OnPermissionCallback() {@Overridepublic void onGranted(@NonNull List<String> permissions, boolean allGranted) {if (!allGranted) {toast("获取部分权限成功,但部分权限未正常授予");return;}toast("获取录音和日历权限成功");}@Overridepublic void onDenied(@NonNull List<String> permissions, boolean doNotAskAgain) {if (doNotAskAgain) {toast("被永久拒绝授权,请手动授予录音和日历权限");// 如果是被永久拒绝就跳转到应用权限系统设置页面XXPermissions.startPermissionActivity(context, permissions);} else {toast("获取录音和日历权限失败");}}});

其他API介绍

// 判断一个或多个权限是否全部授予了
XXPermissions.isGranted(Context context, String... permissions);// 获取没有授予的权限
XXPermissions.getDenied(Context context, String... permissions);// 判断某个权限是否为特殊权限
XXPermissions.isSpecial(String permission);// 判断一个或多个权限是否被勾选了《不再询问》的选项(一定要在权限申请的回调方法中调用才有效果)
XXPermissions.isDoNotAskAgainPermissions(Activity activity, String... permissions);// 跳转到应用权限设置页
XXPermissions.startPermissionActivity(Context context, String... permissions);
XXPermissions.startPermissionActivity(Activity activity, String... permissions);
XXPermissions.startPermissionActivity(Activity activity, String... permission, OnPermissionPageCallback callback);
XXPermissions.startPermissionActivity(Fragment fragment, String... permissions);
XXPermissions.startPermissionActivity(Fragment fragment, String... permissions, OnPermissionPageCallback callback);// 设置不触发错误检测机制(全局设置)
XXPermissions.setCheckMode(false);
// 设置权限申请拦截器(全局设置)
XXPermissions.setInterceptor(new IPermissionInterceptor() {});

http://www.lryc.cn/news/215310.html

相关文章:

  • 软件测试/测试开发丨Python:易学、强大、多用途的编程语言
  • 一、VPN基础
  • 淘宝协议最新版
  • AI“走深向实”,蚂蚁蚁盾在云栖大会发布实体产业「知识交互建模引擎」
  • 如何估计池塘里鱼的数目,周边有多少车辆?
  • docker中安装rabbitMq并配置启动
  • viewfs://为Hadoop 中的一个特殊文件系统
  • UniPro自定义个人专属工作台 大幅提升工作效率
  • python调用飞书机器人发送文件
  • 【产品应用】一体化伺服电机在焊接设备中的应用
  • uni+vue3+firstUI——组件弹框使用 v-model绑定参数
  • 【电路笔记】-正弦波形
  • Failed to launch task: 文件”Setup”不存在 Mac安装Adobe软件报错解决方案
  • 什么是 DevOps
  • java修仙基石篇->instanceof子父类检查
  • [笔记] ‘\0‘的含义:任何字符串后面都会有的结束符。
  • 雪花算法:分布式ID生成的优雅解决方案(建议收藏)
  • 全国产EtherCAT运动控制边缘控制器(六):RtBasic文件下载与连续轨迹加工的Python+Qt开发
  • git代码行统计
  • LEEDCODE 2235两整数相加
  • 魔术般的速度,焕然一新的磁盘空间 - Magic Disk Cleaner for Mac 2023
  • 项目切换多租户导致的数据库SQL执行异常
  • 安防视频监控平台EasyCVR服务器需要开启firewalld防火墙,该如何开放端口?
  • Ubuntu Desktop 20.04升级gcc-11
  • 网站如何改成HTTPS访问
  • LeetCode 996.正方形数组的数目
  • vue3写nav滚动事件中悬停在顶部
  • 关于qiling->UC_ERR_FETCH_UNMAPPED等执行EXE时内存错误的问题
  • 语言模型和人类的推理都依赖内容
  • 5.1 运输层协议概述