概要
您可以在不同的api级别(运行时为API23 +)上授予对外部SD卡的读/写访问权限。
由于奇巧,权限是没有必要的,如果你使用特定应用程序的目录,需要的除外。
通用方式:
在历史上说,没有写入外部SD卡普遍的方式 ,但继续...
这些事实通过设备的外部存储配置的这些示例得以说明。
基于API的方式:
在使用KitKat之前,请尝试使用Doomsknight方法1,否则使用方法2。
在清单(Api <23)和运行时(Api> = 23)中请求权限。
推荐方式:
当您不需要共享文件时,ContextCompat.getExternalFilesDirs解决了访问错误。
共享它的安全方法是使用内容提供程序或新的Storage Access Framework。
隐私保护方式:
从Android Q Beta 4开始,默认情况下,面向Android 9(API级别28)或更低版本的应用程序看不到任何更改。
默认情况下(或选择启用)定位到Android Q的应用将获得过滤后的外部存储视图。
1.初步答案。
在Android上写入外部SD卡的通用方法
由于不断变化,因此没有通用的方法可以在Android上写入外部SD卡:
KitKat之前:官方Android平台完全不支持SD卡,除了例外。
KitKat:引入了API,使应用程序可以访问SD卡上特定于应用程序的目录中的文件。
棒棒糖:添加了API,以允许应用程序请求访问其他提供商拥有的文件夹。
牛轧糖:提供了简化的API以访问常见的外部存储目录。
... Android Q隐私更改:应用程序范围和媒体范围的存储
在不同的API级别上授予对外部SD卡的读/写访问权限的更好方法是什么
基于Doomsknight的回答和我的,和戴夫·史密斯和马克·墨菲博客文章:1,2,3:
2.更新了答案。
更新1。我从《毁灭战士》的答案中尝试了方法1,但无济于事:
如您所见,在尝试在SD上写入之前,我正在运行时检查权限...
我将使用特定于应用程序的目录来避免更新问题的出现,并ContextCompat.getExternalFilesDirs()
使用getExternalFilesDir文档作为参考。
改进启发式方法,以根据不同的api级别(例如,android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT
...但是我遇到了访问错误,在两个不同的设备上进行了尝试:HTC10和Shield K1。
请记住,Android 6.0支持便携式存储设备,并且第三方应用必须通过Storage Access Framework进行检查。您的设备HTC10和Shield K1可能是API 23。
您的日志显示权限被拒绝的异常访问/mnt/media_rw
,例如API 19+的此修复程序:
<permission name="android.permission.WRITE_EXTERNAL_STORAGE" >
<group gid="sdcard_r" />
<group gid="sdcard_rw" />
<group gid="media_rw" />
</permission>
我从没有尝试过,所以我不能共享代码,但是我会避免for
尝试在所有返回的目录上进行写操作,并根据剩余空间寻找要写入的最佳可用存储目录。
也许Gizm0可以替代您的getStorageDirectories()
方法,这是一个很好的起点。
ContextCompat.getExternalFilesDirs
如果您不需要访问其他文件夹,则可以解决此问题。
3. Android 1.0 .. Pre-KitKat。
在KitKat之前,请尝试使用Doomsknight方法1或通过Gnathonic阅读此响应。
public static HashSet<String> getExternalMounts() {
final HashSet<String> out = new HashSet<String>();
String reg = "(?i).*vold.*(vfat|ntfs|exfat|fat32|ext3|ext4).*rw.*";
String s = "";
try {
final Process process = new ProcessBuilder().command("mount")
.redirectErrorStream(true).start();
process.waitFor();
final InputStream is = process.getInputStream();
final byte[] buffer = new byte[1024];
while (is.read(buffer) != -1) {
s = s + new String(buffer);
}
is.close();
} catch (final Exception e) {
e.printStackTrace();
}
final String[] lines = s.split("\n");
for (String line : lines) {
if (!line.toLowerCase(Locale.US).contains("asec")) {
if (line.matches(reg)) {
String[] parts = line.split(" ");
for (String part : parts) {
if (part.startsWith("/"))
if (!part.toLowerCase(Locale.US).contains("vold"))
out.add(part);
}
}
}
}
return out;
}
将下一个代码添加到您的代码中,AndroidManifest.xml
并阅读获取对外部存储的访问权限
对外部存储的访问受到各种Android权限的保护。
从Android 1.0开始,写入访问WRITE_EXTERNAL_STORAGE
权限由权限保护。
从Android 4.1开始,读取访问受到该READ_EXTERNAL_STORAGE
权限的保护。
为了...在外部存储设备上写入文件,您的应用必须获得...系统权限:
<manifest ...>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest>
如果同时需要...,则只需要请求WRITE_EXTERNAL_STORAGE
权限。
阅读Mark Murphy的解释并推荐 Dianne Hackborn和Dave Smith的帖子
- 直到Android 4.4为止,Android才没有对可移动媒体的官方支持。从KitKat开始,FMW API中出现了“主要”和“次要”外部存储的概念。
- 先前的应用程序仅依靠MediaStore索引,随硬件一起提供或检查安装点并应用一些启发式方法来确定代表可移动媒体的内容。
由于错误而忽略下一个注释,但是请尝试使用ContextCompat.getExternalFilesDirs()
:
- 自Android 4.2以来,Google一直要求设备制造商锁定可移动媒体以提高安全性(多用户支持),并在4.4中添加了新测试。
- 由于
getExternalFilesDirs()
添加了KitKat和其他方法,以在所有可用的存储卷上返回可用路径(返回的第一项是主卷)。
- 下表说明了开发人员可能尝试做的事情以及KitKat将如何响应:
注意:从Android 4.4开始,如果您仅读取或写入应用程序专用的文件,则不需要这些权限。有关更多信息...,请参阅保存应用程序专用文件。
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
</manifest>
还请阅读Paolo Rovelli的解释,并从KitKat开始尝试使用Jeff Sharkey的解决方案:
在KitKat中,现在有一个公共API可与这些辅助共享存储设备进行交互。
新方法Context.getExternalFilesDirs()
和
Context.getExternalCacheDirs()
方法可以返回多个路径,包括主设备和辅助设备。
然后,您可以遍历它们并检查Environment.getStorageState()
并
File.getFreeSpace()
确定存储文件的最佳位置。
ContextCompat
在support-v4库中也可以使用这些方法。
从Android 4.4开始,现在基于目录结构综合了外部存储设备上文件的所有者,组和模式。这使应用程序可以在外部存储上管理其特定于软件包的目录,而无需获得广泛的
WRITE_EXTERNAL_STORAGE
许可。例如,具有包名称的应用com.example.foo
现在可以Android/data/com.example.foo/
在没有权限的情况下自由访问
外部存储设备。通过将原始存储设备包装在FUSE守护程序中,可以实现这些综合权限。
使用KitKat,您获得“无根”的“完整解决方案”的机会几乎为零:
Android项目肯定在这里搞砸了。没有应用程序可以完全访问外部SD卡:
- 文件管理器:您不能使用它们来管理外部SD卡。在大多数区域中,它们只能读取而不能写入。
- 媒体应用程序:您无法再对媒体收藏集进行重新标记/重新组织,因为这些应用程序无法对其进行写入。
- 办公应用:几乎相同
唯一的地方3次第三方应用程序可以写你的外置卡上有“自己的目录”(即
/sdcard/Android/data/<package_name_of_the_app>
)。
真正修复的唯一方法是需要制造商(其中一些修复了该问题,例如,华为为其P6进行了Kitkat更新)或root用户(Izzy的解释在此处继续)
5. Android 5.0引入了更改和DocumentFile帮助器类。
getStorageState
已在API 19中添加,在API 21中已弃用,请
使用getExternalStorageState(File)
这是与KitKat中的Storage Access Framework进行交互的出色教程。
与Lollipop中的新API交互非常相似(Jeff Sharkey的解释)。
6. Android 6.0 Marshmallow引入了新的运行时权限模型。
请求权限在运行时,如果API级别23+和阅读在运行时请求权限
从Android 6.0(API级别23)开始,用户可以在应用运行时向应用授予权限,而不是在安装应用...或更新应用...时授予用户权限。
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.WRITE_EXTERNAL_STORAGE);
Android 6.0引入了新的运行时权限模型,其中,应用程序在运行时需要时会请求功能。由于新模型包括READ/WRITE_EXTERNAL_STORAGE
权限,因此平台需要动态授予存储访问权限,而不会终止或重新启动已经运行的应用程序。它通过维护所有已安装的存储设备的三个不同视图来实现此目的:
- / mnt / runtime / default显示给没有特殊存储权限的应用...
- / mnt / runtime / read显示给具有READ_EXTERNAL_STORAGE的应用
- / mnt / runtime / write显示给具有WRITE_EXTERNAL_STORAGE的应用
7. Android 7.0提供了简化的API以访问外部存储目录。
作用域目录访问
在Android 7.0中,应用程序可以使用新的API请求访问特定的
外部存储目录,包括可移动媒体(例如SD卡)上的目录...
有关更多信息,请参见作用域目录访问培训。
阅读Mark Murphy的文章:小心范围内的目录访问。在Android Q中已弃用:
请注意,Android Q中已弃用了7.0中添加的作用域目录访问。
具体来说,不建议使用StorageVolumecreateAccessIntent()
上的方法。
他们添加了一个createOpenDocumentTreeIntent()
可以替代的方式。
8. Android 8.0 Oreo .. Android Q Beta更改。
从Android O开始,Storage Access Framework允许自定义文档提供商
为驻留在远程数据源中的文件创建可搜索的文件描述符。
权限,
在Android O之前,如果某个应用程序在运行时请求了权限并被授予该权限,则系统还会错误地向该应用程序授予属于同一权限组且已在清单中注册的其余权限。
对于面向Android O的应用,此问题已得到纠正。仅向该应用授予已明确请求的权限。但是,一旦用户向该应用程序授予了权限,该权限组中所有随后的权限请求都将被自动授予。
例如,READ_EXTERNAL_STORAGE
和WRITE_EXTERNAL_STORAGE
...
更新: Android Q早期的Beta版本暂时将READ_EXTERNAL_STORAGE
和WRITE_EXTERNAL_STORAGE
权限替换为更细粒度的特定于媒体的权限。
注意: Google在Beta 1上引入了角色,并在Beta 2之前将其从文档中删除了...
注:该权限具体到媒体集合在早期测试releases-中引入READ_MEDIA_IMAGES
,READ_MEDIA_AUDIO
和READ_MEDIA_VIDEO
-现已过时。更多信息:
Mark Murphy撰写的Q Beta 4(最终API)评论:外部存储的消亡:Saga的终结(?)
“死亡比生命更普遍。每个人死亡,但不是每个人都活着。” ―安德鲁·萨克斯
9.相关问题和推荐答案。
如何获取Android 4.0+的外部SD卡路径?
mkdir()在内部闪存中工作,但不能在SD卡中工作吗?
getExternalFilesDir和getExternalStorageDirectory()之间的差异
为什么getExternalFilesDirs()在某些设备上不起作用?
如何使用为Android 5.0(Lollipop)提供的新SD卡访问API
在Android 5.0及更高版本中写入外部SD卡
使用SAF(存储访问框架)的Android SD卡写许可
SAFFAQ:存储访问框架常见问题
10.相关的错误和问题。
错误:在Android 6上,当使用getExternalFilesDirs时,它不允许您在结果中创建新文件
没有写许可权,无法在Lollipop上写入由getExternalCacheDir()返回的目录