当前位置: 首页 > 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() {});

相关文章:

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

Android Api 29 对文件和文件夹进行了重大更改。不允许使用外部存储&#xff0c;如下方法&#xff1a; Environment.getExternalStorageDirectory() /mnt/sdcard Environment.getExternalStoragePublicDirectory(“test”) /mnt/sdcard/test 只能使用内部存储 getExterna…...

软件测试/测试开发丨Python:易学、强大、多用途的编程语言

点此获取更多相关资料 Python 发展历史 Python 是一门高级编程语言&#xff0c;由 Guido van Rossum&#xff08;龟叔&#xff09; 在 1989 年发明&#xff0c;设计 Python 语言的初衷是为了创造一种介于 C 和 shell 之间&#xff0c;简洁方便&#xff0c;易学易用&#xff0…...

一、VPN基础

VPN基础 1、定义及特征2、VPN优势3、VPN分类4、VPN体系结构5、VPN实现的模式 —————————————————————————————————————————————————— 1、定义及特征 虚拟专用网VPN是依靠Internet服务提供商ISP和网络服务提供商NSP在公共网…...

淘宝协议最新版

我可以为您提供一些示例代码&#xff0c;以演示一些与电商平台相关的功能。请注意&#xff0c;以下代码仅为示例&#xff0c;具体实现还需要根据您的应用程序的架构、技术栈和需求进行调整和扩展。 1. 用户注册功能&#xff1a; - 后端实现&#xff1a;在后端&#xff0c;您可…...

AI“走深向实”,蚂蚁蚁盾在云栖大会发布实体产业「知识交互建模引擎」

数字化起步晚、数据分散稀疏、专业壁垒高、行业知识依赖「老师傅」&#xff0c;是很多传统产业智能化发展面临的难题。2023年云栖大会上&#xff0c;蚂蚁集团安全科技品牌蚁盾发布“知识交互建模引擎”&#xff0c;将实体产业知识与AI模型有机结合&#xff0c;助力企业最快10分…...

如何估计池塘里鱼的数目,周边有多少车辆?

如何估计池塘里鱼的数目&#xff1f; 老李想估计一下自己池塘里鱼的数量&#xff0c;第一天他捕捞了50条鱼做好标记&#xff0c;然后全放回池塘。过了几天带标记的鱼完全混合于鱼群中&#xff0c;他又去捕捞了168条&#xff0c;发现做标记的鱼有8条。帮老李估算一下池塘里的鱼…...

docker中安装rabbitMq并配置启动

目录 1. 拉取镜像并安装&#xff08;此处实例安装的是最新版&#xff09;2.查看docker中已安装的镜像和版本3.启动RabbitMq4.配置管理端5.安装完成 1. 拉取镜像并安装&#xff08;此处实例安装的是最新版&#xff09; docker pull rabbitmq2.查看docker中已安装的镜像和版本 …...

viewfs://为Hadoop 中的一个特殊文件系统

解释 viewfs:// 是 Hadoop 中的一个特殊文件系统 URI&#xff0c;用于访问 Hadoop 的视图文件系统&#xff08;ViewFS&#xff09;。 ViewFS 是 Hadoop 提供的一种虚拟文件系统&#xff0c;它可以将来自多个底层文件系统的文件统一管理和访问。 通过 ViewFS&#xff0c;你可…...

UniPro自定义个人专属工作台 大幅提升工作效率

很多研发团队在开完每日站会后&#xff0c;工程师的工作习惯便是打开研发管理系统&#xff0c;先看看自己的待办事项&#xff0c;或是查看同事的需求、评论&#xff0c;亦或是查看今日份工作的高优先级项等等。 如何方便工程师能够快速查看和了解一天的工作究竟从哪开始呢&…...

python调用飞书机器人发送文件

当前飞书webhook机器人还不支持发送文件类型的群消息&#xff0c;可以申请创建一个机器人应用来实现群发送文件消息。 创建机器人后&#xff0c;需要开通一系列权限&#xff0c;然后发布。由管理员审核通过后&#xff0c;才可使用。 包括如下的权限&#xff0c;可以获取群的c…...

【产品应用】一体化伺服电机在焊接设备中的应用

随着制造业的不断发展&#xff0c;焊接设备在许多领域都得到了广泛应用&#xff0c;如汽车制造、机械加工、钢结构等领域。为了提高焊接设备的性能和效率&#xff0c;许多厂家开始采用一体化伺服电机作为焊接设备的主要驱动部件。本文将介绍一体化伺服电机在焊接设备中的应用背…...

uni+vue3+firstUI——组件弹框使用 v-model绑定参数

说明 将框架弹框组件 封装成子组件&#xff0c;在页面中引用该子组件&#xff0c;传参并控制弹框显示与隐藏。 子组件 <template><view><wh-modal :show"showPopup" :descr"descr" maskClosable click"onClick" :buttons"…...

【电路笔记】-正弦波形

正弦波 文章目录 正弦波1、概述2、波形产生3、总结 在 19 世纪末的 10 年间&#xff0c;许多技术成就使得交流电的使用得以扩展&#xff0c;并克服了直流电向公众供电的局限性。 1882 年&#xff0c;法国发明了变压器&#xff0c;它简化了交流电的分配&#xff0c;正如我们将在…...

Failed to launch task: 文件”Setup”不存在 Mac安装Adobe软件报错解决方案

在安装 Adobe 软件时&#xff0c;软件提示 Failed to launch task: 文件”Setup”不存在 &#xff0c;这个时候怎么处理呢&#xff1f; 解决方法如下&#xff1a; 1、安装 AnitCC 或 或 Creative Cloud 环境&#xff0c;保证软件所需要的环境 2、如果安装后也不起作用&#x…...

什么是 DevOps

DevOps是一套融合软件开发&#xff08;Dev&#xff09;和 IT 运营&#xff08;Ops&#xff09;的实践&#xff0c;旨在缩短应用程序开发周期并确保以高软件质量持续交付&#xff0c;通过采用 DevOps 实践&#xff0c;您可以帮助组织更可靠、更快速、更高效地交付软件。 什么是…...

java修仙基石篇->instanceof子父类检查

instanceof检查子父类&#xff08;或者是否能被强转&#xff09; 作用1&#xff1a;检查某对象是否是某类的子类 如&#xff1a;儿子类继承了父亲类。 检查儿子类对象是否属于父亲类 作用2&#xff1a;检查两个对象是否可以强转 语法&#xff1a; 子类对象 instanceof 父…...

[笔记] ‘\0‘的含义:任何字符串后面都会有的结束符。

参考博客&#xff1a;C语言中 \0 代表什么 摘录&#xff1a; ‘\0’ 是字符串的结束符&#xff0c;任何字符串之后都会自动加上’\0’。如果字符串末尾少了‘\0’字符&#xff0c;则其在输出时可能会出现乱码问题。 字符串总是以“\0”作为串的结束符&#xff1b;因此当把一个…...

雪花算法:分布式ID生成的优雅解决方案(建议收藏)

在分布式系统中&#xff0c;唯一ID的生成和管理是一项重要而棘手的任务。雪花算法&#xff0c;由Twitter开源的一种分布式ID生成算法&#xff0c;为这个问题提供了一种优雅的解决方案。本文将详细介绍雪花算法的原理、设计和实现&#xff0c;并通过示例代码和图片帮助读者更好地…...

全国产EtherCAT运动控制边缘控制器(六):RtBasic文件下载与连续轨迹加工的Python+Qt开发

今天&#xff0c;正运动小助手给大家分享一下全国产EtherCAT运动控制边缘控制器ZMC432H如何使用PythonQT实现连续轨迹加工。 01 功能简介 全国产EtherCAT运动控制边缘控制器ZMC432H是正运动的一款软硬件全国产自主可控&#xff0c;运动控制接口兼容EtherCAT总线和脉冲型的独立…...

git代码行统计

本文介绍统计项目代码行的方式&#xff0c;包括使用git log统计、git ls-files统计和使用linux命令行方式统计。 一、使用git log统计 1.统计所有代码行数 当前代码都存放在git仓库下&#xff0c;当需进行代码行数统计时&#xff0c;让开发人员在代码路径下运行如下指令&…...

利用ngx_stream_return_module构建简易 TCP/UDP 响应网关

一、模块概述 ngx_stream_return_module 提供了一个极简的指令&#xff1a; return <value>;在收到客户端连接后&#xff0c;立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量&#xff08;如 $time_iso8601、$remote_addr 等&#xff09;&a…...

DAY 47

三、通道注意力 3.1 通道注意力的定义 # 新增&#xff1a;通道注意力模块&#xff08;SE模块&#xff09; class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...

uniapp微信小程序视频实时流+pc端预览方案

方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度​WebSocket图片帧​定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐​RTMP推流​TRTC/即构SDK推流❌ 付费方案 &#xff08;部分有免费额度&#x…...

视觉slam十四讲实践部分记录——ch2、ch3

ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)

漏洞概览 漏洞名称&#xff1a;Apache Flink REST API 任意文件读取漏洞CVE编号&#xff1a;CVE-2020-17519CVSS评分&#xff1a;7.5影响版本&#xff1a;Apache Flink 1.11.0、1.11.1、1.11.2修复版本&#xff1a;≥ 1.11.3 或 ≥ 1.12.0漏洞类型&#xff1a;路径遍历&#x…...

PAN/FPN

import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...

08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险

C#入门系列【类的基本概念】&#xff1a;开启编程世界的奇妙冒险 嘿&#xff0c;各位编程小白探险家&#xff01;欢迎来到 C# 的奇幻大陆&#xff01;今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类&#xff01;别害怕&#xff0c;跟着我&#xff0c;保准让你轻松搞…...

【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制

使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下&#xff0c;限制某个 IP 的访问频率是非常重要的&#xff0c;可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案&#xff0c;使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...

抽象类和接口(全)

一、抽象类 1.概念&#xff1a;如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象&#xff0c;这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法&#xff0c;包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中&#xff0c;⼀个类如果被 abs…...

人工智能 - 在Dify、Coze、n8n、FastGPT和RAGFlow之间做出技术选型

在Dify、Coze、n8n、FastGPT和RAGFlow之间做出技术选型。这些平台各有侧重&#xff0c;适用场景差异显著。下面我将从核心功能定位、典型应用场景、真实体验痛点、选型决策关键点进行拆解&#xff0c;并提供具体场景下的推荐方案。 一、核心功能定位速览 平台核心定位技术栈亮…...