获取SD卡根目录,很多人会想到如下方法:
Environment.getExternalStorageDirectory()
打印该目录,如下:
/storage/emulated/0
这在早期的的Android手机上是没什么问题的,因为旧的Android手机时代,sd卡还是个奢侈品,所以手机出厂的时候不带sd卡,用户需要自己买sd卡插入进入,这时代的Android手机只有一张sd卡,后来发展了,硬件发展的贼快,sd卡成本也在不断下降,手机出厂时直接就内置了一张sd卡在手机里面,而且是不可移除的,用户可以再买一张sd卡插入进去,这一张是属于可移除的,这样,手机上就有了两张sd卡,如果想要获取可移除的这张sd卡的根目录,要怎么获取呢?好像有点头痛,翻了好久Android文档,发现有一个方法,如下:
context.getExternalFilesDirs(type)
比如,我想获取一个保存音乐文件的目录,代码如下:
getExternalFilesDirs(Environment.DIRECTORY_MUSIC).forEach(System.out::println)
输出结果如下:
/storage/emulated/0/Android/data/cn.android666.helloeven/files/Music
/storage/C695-0FC0/Android/data/cn.android666.helloeven/files/Music
Android文档告诉我们getExternalFilesDirs(type)会返回一个数组,数组的第一个元素是内置sd卡,第二个元素是外置sd卡,这种方式能获取到外置sd卡,而且往里面写文件是没有问题的,但是这个目录中写入的文件,在应用卸载的时候会被系统删除,所以我们想写到sd卡的根目录中,这样就不会被系统删除,但是如何获取外置sd卡的根目录呢?网上找了好一些答案,写的有些麻烦,后来我才想到,既然getExternalFilesDirs(type)函数可以获取到外置sd卡的某些目录,那获取根目录就可以通过这些子目录来完成,代码如下:
val dirs = getExternalFilesDirs(null)
if (dirs.size > 1) {
dirs[1]?.parentFile?.parentFile?.parentFile?.parentFile?.let { sdcardRootPath ->
println(sdcardRootPath)
}
}
输出结果如下:
/storage/C695-0FC0
Nice,就是这么简单,之前为什么要想这么复杂呢?这不就获取到根目录了吗!这就是Java基础没掌握好的原因了,通过子目录就能获取到父目录,这是Java基础呀!
接下来,试一下往sd卡根目录写入一个文件:
val dirs = getExternalFilesDirs(null)
if (dirs.size > 1) {
dirs[1]?.parentFile?.parentFile?.parentFile?.parentFile?.let { sdcardRootPath ->
val testFile = File(sdcardRootPath, "test.txt")
testFile.writeText("Hello!!!")
println(testFile.readText())
}
}
运行,报异常如下:
Caused by: java.io.FileNotFoundException: /storage/C695-0FC0/test.txt (Permission denied)
注意看后面的“Permission denied”,权限被拒绝,但是我已经加入了写外部存储的权限了,后来百度发现,外置的sd卡需要的是另一个权限,如下:
<uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE"
tools:ignore="ProtectedPermissions" />
系统会提示你,这是一个系统权限,也就是需要系统签名的那些应用才可以使用的,但是我在公司的一台设备上测试,没有使用系统签名,发现声明了这个权限之后也可以正常写入文件了,没有测试过在其他手机是否也可以。可以肯定的是,如果你的应用有系统签名,加入这个权限之后,肯定写文件就没问题了!
后来Android文档更新了,推荐我们使用另一个API来代替context.getExternalFilesDirs(type):
ContextCompat.getExternalFilesDirs(context, type)
同时,Android文档上还有一个注意事项,如下:
注意:如果应用在搭载 Android 4.3(API 级别 18)或更低版本的设备上使用,则该数组仅包含一个表示主外部存储卷的元素。
通过查看context.getExternalFilesDirs(type)这个API的文档,我们发现它是在API 19版本才有的函数,所以在API 18以及更低的版本的时候,那些版本时代的Android手机应该是都没有内置sd卡的,所以那些手机最多只有一个sd卡,而到了API 19的时候,谷歌考虑到多个sd卡的情况,所以出了context.getExternalFilesDirs(type)这个API来访问不同的SD卡位置。
最后,奉上Android文档连接:https://developer.android.google.cn/training/data-storage/app-specific#external-select-location