继续研究可恨的安卓问题. 真是烦死安卓了. 一个图片路径都能搞出五花八门的问题.
Environment.MEDIA_MOUNTED在Android中表示SD卡的状态为已插上SD卡,并且应用程序对SD卡具有读写权限。这个状态表明SD卡已经被正确安装到手机上,并且应用程序可以对其进行读写操作。如果应用程序对SD卡只有读权限,那么状态将是MEDIA_MOUNTED_READ_ONLY.
用于判断的代码:
boolean sdCardExist = Environment.getExternalStorageState().equals
(Android.os.Environment.MEDIA_MOUNTED);
if (sdCardExist)
{
sdDir = Environment.getExternalStorageDirectory();//获取根目录
}
安卓分区存储将存储空间分为两部分.
- 公共目录: DownLoads Doucuments Pictures DCIM Movies Music Ringtones 等. 公共目录的文件在 App 卸载后不会删除. 可以通过 SAF、MediaStore 接口访问. 拥有权限, 也能通过路径直接访问.
- 应用专属目录.只能应用自己直接访问. App 卸载, 数据会清除. 这些目录在存储卷上显示为 Android/data 的子目录.
Environment.getExternalStorageDirectory() 在 API Level29 开始已被弃用, 开发者应迁移至 Context.getExternalFilesDir(String), MediaStore, 或 Intent.ACTION_OPEN_DOCUMENT.
应用 targetSdkVersion >= 30, 都会强制打开分区存储.
使用直接路径和原生库保存媒体文件时, 应用的性能会略有下降. 尽可能改用 MediaStore API.
应用专属存储空间: 存储仅供应用使用的文件,可以存储到内部存储卷中的专属目录或外部存储空间中的其他专属目录。使用内部存储空间中的目录保存其他应用不应访问的敏感信息。从内部存储空间访问, 可以使用 getFileDir() 或 getCacheDir() 方法, 从外部存储空间访问, 可以使用getExternalFilesDir() 或 getExternalCacheDir() 方法. 内部存储空间中用于存储应用专属数据的空间有限, 如果需要保存大量数据, 使用其他类型的存储空间.
如果数据仅供你的应用使用, 应使用应用专属存储空间. 对于可分享的媒体内容, 应使用共享的存储空间, 以便其他应用可以访问相应内容.
媒体:可共享的媒体文件, MediaStoreAPI
安卓提供两类物理存储位置: 内部存储空间和外部存储空间. 可移除卷(例如 SD 卡) 在文件系统中属于外部存储空间.安卓使用路径 (例如 /sdcard) 表示这些存储设备.
安卓系统的版本越新, 就越依赖于文件的用途而不是位置来确定应用对特定文件的访问和写入能力.安卓11引入了 MANAGE_EXTERNAL_STORAGE 权限, 该权限提供对应用专属目录和 MediaStore 之外文件的写入权限. 大多数应用无需声明此权限即可实现其用例.
为了让用户更好地管理自己的文件并减少混乱,以 Android 10(API 级别 29)及更高版本为目标平台的应用在默认情况下被授予了对外部存储空间的分区访问权限(即分区存储)。此类应用只能访问外部存储空间上的应用专属目录,以及本应用所创建的特定类型的媒体文件。
除非你的应用需要访问存储在应用专属目录和 MediaStore API 可以访问的目录之外的文件, 否则请使用分区存储. 如果将应用专属文件存储在外部存储空间中, 则可以将这些文件存放在外部存储空间中的应用专属目录内, 以便更加轻松地采用分区存储. 这样, 在启用分区存储后, 你的应用将可以继续访问这些文件.
应用专属内部存储空间目录: 这些目录既包括用于存储持久性文件的专属位置, 也包括用于存储缓存数据的其他位置.
应用专属外部存储空间目录: 这些目录既包括用于存储持久性文件的专属位置, 也包括用于存储缓存数据的其他位置. 其他应用可以在具有适当权限的情况下访问这些目录, 但存储在这些目录中的文件仅供你的应用使用. 如果你明确打算创建其他应用能够访问的文件, 你的应用应改为将这些文件存储在外部空间的共享存储空间部分.
在将应用专属文件写入内部存储空间之前, 应用应查询设备上的可用空间.
应用的普通持久性文件位于可以使用上下文对象的 filesDir 属性访问的目录中.
可以使用 File API 访问和存储文件.
File file = new File(context.getFilesDir(), filename);
除使用 File API 之外, 还可以调用 openFileOutput() 获取会写入 filesDir 目录中的文件的 FileOutputStream.
下面代码展示将一些文本写入文件:
String filename = "myfile";
String fileContents = "Hello world!";
try (FileOutputStream fos = context.openFileOutput(filename, Context.MODE_PRIVATE)) {
fos.write(fileContents.toByteArray());
}
如需允许其他应用访问存储在内部存储空间内此目录中的文件, 请使用具有 FLAG_GRANT_READ_URI_PERMISSION 属性的 FileProvider.
如需以信息流的形式读取文件, 请使用 openFileInput().
FileInputStream fis = context.openFileInput(filename);
InputStreamReader inputStreamReader =
new InputStreamReader(fis, StandardCharsets.UTF_8);
StringBuilder stringBuilder = new StringBuilder();
try (BufferedReader reader = new BufferedReader(inputStreamReader)) {
String line = reader.readLine();
while (line != null) {
stringBuilder.append(line).append('\n');
line = reader.readLine();
}
} catch (IOException e) {
// Error occurred when opening raw file for reading.
} finally {
String contents = stringBuilder.toString();
}
创建嵌套目录以及缓存文件相关操作, 暂时和我遇到的问题没什么关系, 先不看了.
外部存储空间也包含两个目录, 一个目录专为应用的持久性文件而设计, 而另一个目录包含应用的缓存文件.
由于外部存储空间位于用户可能能够移出的物理卷上, 因此在尝试从外部存储空间读取应用专属数据或将应用专属数据写入外部存储空间之前, 先要验证该卷是否可访问.
可以通过调用 Environment.getExternalStorageState() 查询该卷的状态. 确定是否可读写应用专属文件. MEDIA_MOUNTED 可读写, MEDIA_MOUNTED_READ_ONLY, 只读.
// 是否可读写
private boolean isExternalStorageWritable() {
return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
}
// 是否至少可读
private boolean isExternalStorageReadable() {
return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) ||
Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED_READ_ONLY);
}
选择物理存储位置
有时, 分配内部存储分区作为外部存储空间的设备也会提供 SD 卡插槽. 这意味着设备具有多个可能包含外部存储空间的物理卷, 因此需要选择用于应用专属存储空间的物理卷.
如下代码段中所示, 返回数组中的第一个元素被视为主外部存储卷, 除非该卷已满或不可用, 否则请使用该卷.
File[] externalStorageVolumes =
ContextCompat.getExternalFilesDirs(getApplicationContext(), null);
File primaryExternalStorage = externalStorageVolumes[0];
如需从外部存储设备访问应用专用文件, 请调用 getExternalFilesDir().
File appSpecificExternalDir = new File(context.getExternalFilesDir(null), filename);
在安卓11(API30)及以上更高版本中, 应用无法再外部存储设备(是设备不是空间)上创建自己的应用专属目录.
创建、移除缓存文件和内部存储操作类似.
如果你的应用支持使用仅在你的应用内对用户有价值的媒体文件, 最好将这些文件存储在外部存储空间中的应用专属目录中, 如下代码:
@Nullable
File getAppSpecificAlbumStorageDir(Context context, String albumName) {
// 获取外部存储空间上的应用专属目录内的 pictures 目录
File file = new File(context.getExternalFilesDir(
Environment.DIRECTORY_PICTURES), albumName);
if (file == null || !file.mkdirs()) {
Log.e(LOG_TAG, "Directory not created");
}
return file;
}
请务必使用 DIRECTORY_PICTURES 等 API 常量提供的目录名称. 这些目录名称可确保系统正确处理文件. 如果没有适合你文件的预定义子目录名称, 可以改为将 null 传递到 getExternalFilesDir(). 这将返回外部存储空间中的应用专属根目录.
查询可用空间, 创建存储空间管理activity, 让用户移除部分设备文件, 让用户移除所有缓存文件, 这些后面再弄.
picker获取的视频真实路径 为什么以/sdcard/.transforms/开头?
picker获取的视频真实路径以 /sdcard/.transforms/ 开头的原因是系统对文件路径进行了转换和封装. 当使用picker获取视频文件时,系统会对文件路径进行特殊处理,以确保文件的安全性和隔离性。这种处理方式包括将文件路径转换为以/sdcard/.transforms/开头的形式,这是Android系统中常见的做法. 具体来说,这种路径转换的目的是为了在应用之间提供隔离,防止一个应用直接访问另一个应用的数据。在Android系统中,每个应用都有自己的沙箱环境,系统通过修改文件路径来确保数据的安全和隐私。例如,使用 cameraPicker 进行拍好时, 拍摄的照片不会直接存储到公共图库中,而是存储在应用的沙箱目录内,通过特定的路径访问. 这种处理方式虽然增加了路径的复杂性,但有效地保护了用户数据的隐私和安全。开发者在使用picker获取文件时,需要注意这些路径的特殊性,确保应用能够正确处理和访问这些文件。
百度搜: Intent.ACTION_GET_CONTENT获取的视频 存储到应用专属外部存储空间.
这个能解决 intent 方式获取到的视频, 拿到真实路径也无法播放的问题, 应该先存储到应用专属外部存储空间.
弄了好久, 领导说必须图片和视频一起选. 明天继续, 做安卓开发太难了.