真机调试sdcard被占用无法访问

本文介绍了解决Android设备上SDCard无法读写的问题。通过调整USB连接模式为仅供电,并启用USB调试功能,成功实现在SDCard上保存文件的目标。

昨天下午到现在一直在搞通过poi将数据保存在sdcard中,但是真机调试时一直不能够在机子上查看保存的文件(sdcard被占用),通过网上查询及自己实践总结如下:

1、设置——》应用程序——》开发——》选择USB调试

2、选择任务栏中的USB链接——》选择仅供电

问题的根结是原来的选择是内存卡存取,这种设置能够在pc上看到这个盘并可以操作其中的文件

仅供电模式在调试时不但能够在机子上继续查看sdcard文件和可以通过eclipse的File explorer查看文件

<think>我们正在解决Android 15中访问/sdcard/目录的权限问题。根据错误日志,问题在于MediaProvider在插入文件时要求指定一个允许的子目录(如Download或Documents),而当前操作试图使用null作为目录(即根目录/sdcard/),这是不允许的。同时,还出现了FileNotFoundException,提示EPERM(Operation not permitted)。 结合引用内容和Android 15的存储策略(Scoped Storage),我们给出以下解决方案: ### 1. 使用MediaStore API访问共享存储 在Android 10(API 29)及以上版本,直接通过文件路径(如`/sdcard/...`)访问共享存储已被禁止。必须通过MediaStore API来操作。 #### 示例:在Download目录创建文件 ```kotlin val contentValues = ContentValues().apply { put(MediaStore.Downloads.DISPLAY_NAME, "Uniphiz_15.0.0_Sturgeon_V7.1.02T0_20251023_Debug.zip") put(MediaStore.Downloads.MIME_TYPE, "application/zip") put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS) } val resolver = context.contentResolver val uri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues) uri?.let { resolver.openOutputStream(it).use { outputStream -> // 将文件数据写入outputStream } } ?: throw IOException("Failed to create file") ``` - 关键点:必须指定`RELATIVE_PATH`为允许的目录(如`Download`)[^1][^2] ### 2. 请求正确的存储权限 Android 15中,需要根据文件类型请求对应的媒体权限: - 图片:`READ_MEDIA_IMAGES` - 视频:`READ_MEDIA_VIDEO` - 音频:`READ_MEDIA_AUDIO` - 其他文件(如文档、压缩包):使用`MANAGE_EXTERNAL_STORAGE`(需要特殊申请) #### 在AndroidManifest.xml中声明: ```xml <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> <!-- 如需访问所有文件,需声明以下权限(但需用户手动授权) --> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> ``` #### 动态请求权限(Activity中): ```kotlin // 检查并请求媒体权限 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { requestPermissions(arrayOf( Manifest.permission.READ_MEDIA_IMAGES, Manifest.permission.READ_MEDIA_VIDEO, Manifest.permission.READ_MEDIA_AUDIO ), REQUEST_CODE) } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { // 对于Android 11 (R) 到 Android 12 (S),使用MANAGE_EXTERNAL_STORAGE val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION) val uri = Uri.fromParts("package", packageName, null) intent.data = uri startActivity(intent) } ``` ### 3. 处理MANAGE_EXTERNAL_STORAGE权限 如果应用需要访问所有文件(如下载目录外的文件),则需申请`MANAGE_EXTERNAL_STORAGE`权限。但请注意,Google Play对使用此权限的应用有严格限制,通常只允许文件管理器类应用使用。 #### 检查是否已获得权限: ```kotlin fun hasStoragePermission(): Boolean { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { Environment.isExternalStorageManager() } else { // Android 10及以下,使用旧版存储权限 ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED } } ``` ### 4. 避免直接使用文件路径 错误日志中出现的`/storage/emulated/0/...`路径访问已被禁止,应替换为: - 访问应用私有目录:使用`context.getExternalFilesDir(null)` - 访问媒体文件:通过MediaStore的Content URI - 访问文档文件:使用Storage Access Framework (SAF) ### 5. 调试建议 检查adb日志中的权限错误: ``` adb shell pm list permissions -g adb shell dumpsys package <your_package_name> ``` ### 常见问题解决 **问题1**:插入文件时出现`Primary directory null not allowed` - **原因**:未指定`RELATIVE_PATH`或指定了不允许的目录 - **解决**:使用`MediaStore`时必须在`RELATIVE_PATH`中设置允许的目录(如`Download`)[^1][^2] **问题2**:`FileNotFoundException: open failed: EPERM` - **原因**:没有文件访问权限 - **解决**: 1. 确认已动态申请并授予对应权限 2. 使用`ContentResolver`代替`File`类操作文件 3. 若需访问非媒体文件,申请`MANAGE_EXTERNAL_STORAGE` **问题3**:只能访问图片和视频,无法访问txt等文档 - **解决**: - Android 13+:请求`READ_MEDIA_IMAGES`等权限仅能访问媒体文件 - 访问文档需使用Storage Access Framework或申请`MANAGE_EXTERNAL_STORAGE`[^3] §§相关问题§§ 1. 如何在Android 15中使用Storage Access Framework访问用户选择的文件? 2. 申请MANAGE_EXTERNAL_STORAGE权限被Google Play拒绝怎么办? 3. 如何为特定文件类型(如PDF)创建自定义MediaProvider? 4. Scoped Storage下如何实现文件备份功能? 5. 适配Scoped Storage后,如何保持与旧版Android的兼容性? [^1]: 引用内容1指出在Android 11中,应用在sdcard上创建的文件权限为`-rw-------`(仅应用自身可访问),这体现了Android的存储隔离策略。 [^2]: 引用内容2提到adb push的文件放在/sdcard/Download/下无法安装,这与MediaProvider的目录限制有关。 [^3]: 引用内容3指出,即使声明了`MANAGE_EXTERNAL_STORAGE`权限,也需要用户手动授予,且只能访问部分媒体文件,其他类型文件需要特殊处理。 [^4]: 引用内容4表明在/sdcard中创建目录失败,原因同样是Scoped Storage的限制。 </think>### Android 15 解决 `/sdcard/` 访问权限问题方案 #### 1. 正确使用 MediaStore API(核心方案) 在 Android 15 中,禁止直接访问 `/sdcard/` 路径,必须通过 MediaStore API 操作文件: ```kotlin // 创建文件示例 val resolver = context.contentResolver val values = ContentValues().apply { put(MediaStore.Downloads.DISPLAY_NAME, "example.txt") put(MediaStore.Downloads.MIME_TYPE, "text/plain") put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS) } val uri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values) uri?.let { resolver.openOutputStream(it)?.use { stream -> stream.write("Hello Android 15".toByteArray()) } } ``` - **关键点**:必须指定 `RELATIVE_PATH` 为允许的目录(如 `Download` 或 `Documents`)[^1] - **错误原因**:日志中 `Primary directory null not allowed` 表示未指定有效目录 #### 2. 权限声明与请求 **AndroidManifest.xml**: ```xml <uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" /> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> <uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" /> ``` - **动态请求**(Android 13+): ```kotlin val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { arrayOf(Manifest.permission.READ_MEDIA_IMAGES) } else { arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE) } ActivityCompat.requestPermissions(this, permissions, REQUEST_CODE) ``` #### 3. 特殊文件类型处理(如 txt) 对于非媒体文件(文本/压缩包等),需额外步骤: ```kotlin // 获取 URI 后添加永久权限 context.grantUriPermission( "com.your.package", fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION ) contentResolver.takePersistableUriPermission( uri, Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION ) ``` - **解决 `FileNotFoundException: EPERM`**:需持久化 URI 权限[^3] #### 4. 目录创建正确方式 ```kotlin // 在 Downloads 下创建子目录 val values = ContentValues().apply { put(MediaStore.Downloads.RELATIVE_PATH, "${Environment.DIRECTORY_DOWNLOADS}/MyFolder") put(MediaStore.Downloads.DISPLAY_NAME, "") // 空名称表示目录 put(MediaStore.Downloads.IS_PENDING, 1) } resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values) ``` - **禁止使用**:`Environment.getExternalStoragePublicDirectory()`(Android 11 已废弃)[^4] #### 5. 高级场景:全文件访问权限 若需访问所有文件路径(如文件管理器应用): ```xml <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> ``` ```kotlin // 检查并请求权限 if (Environment.isExternalStorageManager()) { // 已获得权限 } else { val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION) intent.data = Uri.parse("package:$packageName") startActivity(intent) } ``` - **Google Play 限制**:需声明合理用途[^3] ### 调试技巧 1. 检查 URI 权限状态: ```bash adb shell dumpsys package com.your.package | grep 'Uri Permission' ``` 2. 验证 MediaProvider 日志: ```bash adb logcat | grep -E 'MediaProvider|MediaStore' ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值