Android文件系统(01)文件及其目录的基础知识

最近反馈了一个问题,APP存储在相册中的图片,自己上传了华为系统整了一个弹窗,说我们删除了图片,问题就是业务诉求就是同一个文件名的图片认为是同一张图片,然后下载之前先把存在的文件删除了,下载到时候直接整了一个过度文件,下载成功后改名就好,所以我们首先定位的是这个业务诉求里面的删除逻辑。

因为是相册目录,所以解决方式也很简单,将之前成删除代码修改为通过绝对路径在ContentResolver中查询到当前文件的id.然后通过id去查找到文件的uri,最后通过ContentResolver删除查询到的uri即可。通过fileProvider转化出来的Uri和file.delete() 都会被系统检测出来,并且将图片移动到最近删除目录。

我尝试过将图片下载到到download 目录,然后刷新系统图库,系统图库可以识别到这个图片,同时删除的时候也没有被移动到最近删除的目录,通过结果分析过程,应该是系统检测到图片删除,然后备份了一份到最近删除里面,只要最近删除没有,是不是说明就不提示了。那么方案就多种多样了:

  • 放到APP自己的目录下系统图库就识别不到了
  • 图片放外置卡,不放相册目录和图片目录、视频、音频等目录也行。
  • 图片不存为图片,用其他后缀代替,或者无后缀,这个看手机,有的系统即使是这样,放media 等目录下,他也会识别出来是图片,通常可以识别的。
  • 图片存到一个.xxx 这种隐藏文件夹下面。
  • 过度文件不要放media目录,当下载完成后直接拷贝一次。
  • 比如说,现在APP的问题,老板让重命名文件了。直接改业务诉求。

当然,这些结论是如何得出来的呢?当然是Android文件系统的相关知识。因为在这个过程中,逻辑存在卡顿,没有那么当丝滑,所以总结一下。

正文

Android 中的目录

在Android系统中,/system、/storage/emulated/0和/data这三个目录代表了系统目录、外部存储和用户数据目录的不同部分。

/system是Android系统的核心文件目录,其中包含各种系统级别的应用程序和文件。这个目录通常包含系统级别的应用程序、库、字体和其他关键文件。作为系统管理员或高级用户,您可以在这个目录下进行一些系统级的操作,但普通用户通常不需要访问这个目录。

APP私有目录:/data/user/0/包名

/data是Android系统中的用户数据目录,也称为内部存储。这个目录通常包含用户安装的应用程序和它们的数据文件。与外部存储不同,/data目录对于每个用户来说都是私有的,其他用户无法直接访问。这意味着每个用户只能访问自己的应用程序和数据文件。这个目录通常是在用户设备上存储应用程序数据的最主要位置之一。 app私有目录就是在用户数据目录中:/data/user/0/包名。逻辑上,除非你通过contentprovider 提供出去,一般情况下,别的APP是无法访问到你的文件的,同时往这个里面读写文件往往是不需要文件的读写权限的。

外部存储

SD卡或内部存储

/storage/emulated/0是Android系统中的外部存储目录之一。在Android系统中,外部存储通常是指设备上的存储卡或其他可移动存储设备。/storage/emulated/0目录表示外部存储设备的根目录,这个目录下通常包含设备的公共目录(如图片、音乐、下载等)和其他应用程序的数据文件。对于应用程序来说,这个目录通常是它们共享数据的默认位置之一。

在Android 10(API级别29)之前,使用MediaStore.Files.getContentUri("external")可以访问外部存储设备上的文件。但是,从Android 10开始,由于隐私和安全性的考虑,此API已被弃用,并且无法再访问外部存储设备上的文件。所以用下面的这个:

arduino 复制代码
MediaStore.Files.getContentUri("external_primary")

对于公有目录的读写,则必须使用MediaStore提供的API或是SAF(存储访问框架),意味着我们不能再使用File那一套来随意操作公有目录了。

在Android 11中,没有了之前的兼容模式,不能用File的那一套,意味着以前访问 和读取App外置卡的目录失效了。使用分区存储的应用对自己创建的文件始终拥有读/写权限,无论文件是否位于应用的私有目录内。因此,如果您的应用仅保存和访问自己创建的文件,则无需请求获得 READ_EXTERNAL_STORAGEWRITE_EXTERNAL_STORAGE权限。若要访问其他应用创建的文件,则需要READ_EXTERNAL_STORAGE权限。并且仍然只能使用MediaStore提供的API或是SAF(存储访问框架)访问。

需要注意的是,MediaStore提供的API只能访问:图片、视频、音频,如果需要访问其它任意格式的文件,需要使用SAF(存储访问框架),它会调用系统内置的文件浏览器供用户自主选择文件。

外部SD卡和USB驱动

这是是系统支持的。

arduino 复制代码
MediaStore.Files.getContentUri("external_secondary")

总结

总结起来,这三个目录的区别在于:

  • /system是Android系统的核心文件目录,包含各种系统级的应用程序和文件。
  • /storage/emulated/0是外部存储的根目录,包含公共文件和其他应用程序的数据文件。
  • /data是用户数据的目录,也称为内部存储,只供当前用户访问和使用。

文件

在Android中,对于一些文件的读写,随着系统的限制,就出现了3种类型的定位表达方式:

  • /external/images/media/201 像这种URI 类型的。
  • /storage/emulated/0/DCIM/Homate/iButler-26862-1694506242593.jpg 这种绝对路径类型的。
  • content:// 这种通过fileProvider 提供出去的。

URI 类型

这个对象可以用于网络,但是这里主要用于描述本地文件,随着系统管控,我们处理相册、视频、音频等等media文件的时候,包括增删改查等操作,都需要使用这个对象。

uri 转绝对路径或转file

java 复制代码
public File getFileFromUri(Context context, Uri uri) {
    String filePath = null;
    Cursor cursor = null;
    try {
        cursor = context.getContentResolver().query(uri, new String[] { MediaStore.Images.Media.DATA }, null, null, null);
        if (cursor != null && cursor.moveToFirst()) {
            filePath = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
        }
    } finally {
        if (cursor != null) {
            cursor.close();
        }
    }
    return new File(filePath);
}

uri 转bitmap

java 复制代码
public Bitmap getBitmapFromUri(Context context, Uri uri) throws IOException {
    ParcelFileDescriptor parcelFileDescriptor = context.getContentResolver().openFileDescriptor(uri, "r");
    FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
    Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor);
    parcelFileDescriptor.close();
    return bitmap;
}

uri 转 content://

  1. 从Uri对象中获取原始文件路径:
java 复制代码
String filePath = uri.getPath();
  1. 如果原始文件路径以"/"开头,则将其替换为"content://"。否则,将原始文件路径与"content://"连接起来:
java 复制代码
if (filePath.startsWith("/")) {
    filePath = "content://" + filePath;
} else {
    filePath = "content://" + filePath;
}

请注意,以"content://"开头的路径是表示通过Content Provider访问文件的标准方式。通过这种方式,您的应用程序可以安全地访问和操作文件,而不必关心文件的实际存储位置和访问权限。

绝对路径转URI

在 Android 中,可以使用 FileProvider 将文件的绝对路径转换为 Uri。以下是一个示例:

  1. 首先,在你的 AndroidManifest.xml 文件中添加 FileProvider 的声明:
xml 复制代码
<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="${applicationId}.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>
  1. 在 res/xml/ 下创建一个名为 file_paths.xml 的文件,并添加你的外部存储路径:
xml 复制代码
<paths>
    <external-path name="external_files" path="." />
</paths>
  1. 然后,你可以使用以下代码将文件的绝对路径转换为 Uri
java 复制代码
public Uri getUriFromFilePath(Context context, String filePath) {
    File file = new File(filePath);
    return FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileprovider", file);
}

注意:在 Android 10 (API级别 29) 及更高版本中,由于 Scoped Storage 的限制,你无法直接访问设备的外部存储。所以,你可能需要请求用户授予你访问外部存储的权限,或者将文件保存在应用的内部存储中。 这种方式获取到的uri,可以删除文件,但是会被华为系统提醒,然后把图片整到一个最近删除里面。

ContentResolver来查找图片的URI

在Android中,您可以使用ContentResolver来查找图片的URI。给定图片的路径,您可以使用以下代码来查找其对应的URI:

java 复制代码
public Uri getImageUriFromPath(Context context, String imagePath) {
    Cursor cursor = context.getContentResolver().query(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            new String[] { MediaStore.Images.Media._ID },
            MediaStore.Images.Media.DATA + "=? ",
            new String[] { imagePath },
            null);

    if (cursor == null) {
        return null;
    }

    try {
        if (cursor.moveToFirst()) {
            int id = cursor.getInt(cursor.getColumnIndex(MediaStore.Images.Media._ID));
            return MediaStore.Images.Media.EXTERNAL_CONTENT_URI.buildUpon()
                    .appendPath(String.valueOf(id))
                    .build();
        }
    } finally {
        cursor.close();
    }

    return null;
}

而且这种找到的uri 删除的时候,不会被华为系统提醒。

content://

在Android中,文件路径以"content://"开头是因为它使用了Content Provider。Content Provider是Android中的一种机制,用于在应用程序之间共享数据。它提供了一种标准接口,允许不同的应用程序访问和操作同一份数据。

使用Content Provider可以确保数据的安全性和一致性,因为它允许对数据进行访问控制、事务处理和冲突解决。它还提供了一种统一的方式来访问不同类型的数据,包括文件、数据库、网络资源等。

当您在Android中使用文件路径时,使用"content://"前缀表示您正在通过Content Provider访问文件。这样可以确保您的应用程序能够正确地获取和操作文件,而不必关心文件的实际存储位置和访问权限。

转URI

在Android中,文件路径以"content://"开头,表示通过Content Provider访问文件。要将"content://"开头的路径转换为Uri对象,可以使用Uri.parse()方法。例如:

java 复制代码
String filePath = "content://com.example.app.provider/files/example.txt";
Uri uri = Uri.parse(filePath);

转绝对路径

要将Uri对象转换为绝对路径,可以使用Uri.getPath()方法。例如:

java 复制代码
String absolutePath = uri.getPath();

总结

当前还是需要是对于认知进行扩展,也没有太详细的代码。对于目录的获取什么的,推荐使用:AndroidUtilCode PathUtils 当然,随着系统限制,通过目录直接操作文件的日子一去不复返了,同时为了数据安全什么的,放数据还是应该慎重。

相关推荐
阿巴斯甜8 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker8 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95279 小时前
Andorid Google 登录接入文档
android
黄林晴11 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_1 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android