【android】关于android10-11存储的一些知识

简介: 【android】关于android10-11存储的一些知识

数据存储简述


名称 作用 详细说明
应用专属存储空间 存储应用专属的文件 专属存储空间可以在内部存储和外部存储为用户开辟专属的目录,可以用来存储其它用户不能访问的文件,可以通过File api访问,应用被卸载后文件同时被删除,访问不需要权限
共享存储 存储所有应用共享的数据 存储您的应用打算与其他应用共享的文件,包括媒体、文档和其他文件,可以使用MediaStoreapi访问,应用卸载数据仍然存在,访问需要申请存储权限


外部存储空间的使用


使用外部专属存储空间

android4.4-android9的版本之中,应用被分配了外部专属存储空间,这个空间无需权限即可访问,如果应用想访问其它应用的外部专属存储空间(分区存储),那么需要申请存储权限。


android10以上的版本中,应用的外部存储空间成为了应用的私有空间,任何应用不可以访问其它应用的专属存储空间

  1. 特性

外部专属存储空间位于应用的外部存储中,我们可以在不申请权限的情况下访问外部专属存储空间,外部专属存储空间可以在应用卸载后被删除,专属空间目录媒体文件通常不应该被相册等媒体应用收录(也就是说相册中不会展示这个目录下的图片)(我们自定义的图片选择器也不应该扫描这个目录下的图片)

在低于 android10 的版本中,这个目录是对所有应用可见的

  1. 访问方式

获取目录路径:

  • getExternalFilesDir(""):/storage/emulated/0/Android/data/com.ananananzhuo.storage10demo/files
  • getExternalFilesDirs():获取目录下所有文件的列表
  1. 使用 demo
val file = File(getExternalFilesDir(""),"安安安安安卓.txt")
            val fos = FileOutputStream(file)
            fos.write("公众号:安安安安安卓".toByteArray(Charsets.UTF_8))
            fos.close()
复制代码

上面的代码中会在外部专属存储目录(/storage/emulated/0/Android/data/com.ananananzhuo.storage10demo/files)下创建一个安安安安安卓.txt的文件,这里我们没有声明和申请任何权限。执行代码后去看文件选择器中的展示如下:

image.png

在外部存储中访问非私有目录则必须声明申请权限下面我们写一个反面案例

  1. 代码
ItemData(title = "访问外部存储空间",{
            val file = File(Environment.getExternalStorageDirectory().absolutePath)
            val fileAn = File(file,"安安安.txt")
            logEE(file.absolutePath)
            val fos = FileOutputStream(fileAn)
            fos.write("公众号:安安安安安卓".toByteArray(Charsets.UTF_8))
            fos.close()
        })
复制代码
  1. 运行后崩溃日志 提示没有权限

image.png


将媒体文件存储在外部专属存储目录的媒体文件夹中

将图片存储到图片文件夹中

  1. 代码
val picFile = getExternalFilesDir(Environment.DIRECTORY_PICTURES)?.apply {
                if (!exists()) {
                    mkdirs()
                }
            }
            val file = File(picFile,"pic.jpg")
            FileOutputStream(file).apply {
                write(12)
                flush()
                close()
            }
复制代码
  1. 结果

成功存储文件

image.png

媒体文件夹还有其他选项:

  • Environment.DIRECTORY_MOVIES
  • Environment.DIRECTORY_DOWNLOADS
  • Environment.DIRECTORY_DOCUMENTS
  • Environment.DIRECTORY_SCREENSHOTS

存储媒体的时候我们应该尽量使用 Environment 中的字符串常量获取媒体文件目录


android10 以上版本能否使用 Environment.getExternalStorageDirectory 存取文件

高于android10的版本无法使用getExternalStorageDirectory api操作文件,因为 api 已经被删除,低于android10可以继续使用该 api

那么高于android10的版本我们应该如何访问这部分的文件呢?别急,后续的共享存储会详细说明

  1. 代码

image.png

  1. 效果图

image.png


android10以上版本如何获取sd卡中普通目录的图片

我们可以通过SAF在android11以上版本获取普通目录图片(即非专属目录,非共享目录的图片)

本例中的方法不适用于android10以下系统版本(强行使用会崩溃

  1. 代码
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_directory_scan11)
        findViewById<Button>(R.id.btn_secletdirectory).setOnClickListener {
            val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
            intent.addCategory(Intent.CATEGORY_OPENABLE)
            intent.type = "image/jpeg"
            startActivityForResult(intent, 100)//跳转系统浏览器
        }
    }
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        data?.data?.let {
            iv.setImageURI(it)//通过url处理图片
        }
    }
复制代码
  1. 效果

image.png


内部存储


使用 openFileOutput 和 openFileInput 操作内部存储

将一个文件存储到内部存储的 files 目录下

  1. 代码
openFileOutput("安安安安卓openfileoutput.txt", MODE_PRIVATE).apply {
    write("openFileOutput方法输入流".toByteArray(Charsets.UTF_8))
    flush()
    close()
}
复制代码
  1. 执行后文件查看

image.png

openFileInput 方法和 openFileOutput 一样的使用方式


fileList 获取内部存储全部文件路径

  1. 代码
ItemData(title = "获取目录中所有文件路径",{
            fileList().forEach {
                logEE("文件路径:$it")
            }
        })
复制代码
  1. 输出日志
E/安安安安卓: 文件路径:安安安安卓openfileoutput.txt
复制代码


getCacheDir 获取内部存储缓存文件

内部存储缓存中的文件可能会在应用被卸载后被删除,也可能在未卸载前被删除(内部存储空间不足的情况)。所以我们使用内部存储空间缓存文件的时候需要先判断文件是否存在

context.getCacheDir()
复制代码


共享存储空间(android10 以上版本)


媒体

android10以上将媒体文件文件按类型保存在公共目录上,可以使用 MediaStore 访问媒体文件

一下表格列举所有共享媒体文件类型

媒体类型 位置 备注
图片 存储在 Pictures 和 DICM 目录中,系统将这些文件存放在 MediaStore.Images 中
视频 存储 DICM、Movies、Pictures 目录中,系统将这些文件添加到 MediaStore.VIDEO 表中
音频文件 存储在 Alarms、Audiobooks、Music、Notifications、Podcasts、Ringtones 目录中,系统将这些文件添加到 MediaStore.Audio 表中
下载文件 存储在 Download 目录下,系统将这些文件添加到 MediaStore.Download 表中 低于 android10 的版本中不可用
文件集合 存在于 MediaStore.Files 表中 如果使用了分区存储,这个集合只会显示本应用创建的照片、视频、音频文件

开启分区存储权限,媒体的处理

如果应用使用分区存储,您需要在应用的清单中声明 ACCESS_MEDIA_LOCATION 权限,然后在运行时申请此权限。申请方法后面会讲


编写一个相册

  1. 首先需要获取共享存储中所有的图片地址,代码如下
private fun getGallaryData(): MutableList<Image> {
        val cursor = contentResolver.query(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            null, null, null, "${MediaStore.Images.Media.DISPLAY_NAME} ASC"
        )
        val tempdatas = mutableListOf<Image>()
        cursor?.use {
            val idColumn = it.getColumnIndexOrThrow(MediaStore.Images.Media._ID)
            val nameColumn = it.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME)
            while (it.moveToNext()) {
                val id = it.getLong(idColumn)
                val contentUrl =
                    ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id)
                val name = it.getString(nameColumn)
                val image = Image(contentUrl, "")
                tempdatas.add(image)
                logEE("文件名:${contentUrl.path}")
            }
            it.close()
        }
        return tempdatas
    }
复制代码
  1. 在协程中获取图片 uri,并展示到适配器上
private fun initData() {
        lifecycleScope.launch(Dispatchers.IO) {
            datas.addAll(getGallaryData())
            withContext(Dispatchers.Main) {
                recycle.adapter?.notifyDataSetChanged()
            }
        }
    }
复制代码
  1. 效果

image.png


关于android11版本MANAGE_EXTERNAL_STORAGE权限

android10的版本是不能访问所有文件的,可能google也意识到这是不合理的,所以在android11的版本上重新支持了所有文件的访问

android11的系统版本上,如果想扫描应用的所有文件,那么可以声明MANAGE_EXTERNAL_STORAGE权限

MANAGE_EXTERNAL_STORAGE权限需要使用Intent进行权限申请,会跳转到一个系统页面确认权限

需要说明的是:这种方式可以访问共享存储中的文件,但是不可以访问专属存储目录中的文件(Android/data)


如下方法可以判断是否拥有MANAGE_EXTERNAL_STORAGE权限

Environment.isExternalStorageManager()
复制代码

声明权限方式如下:

  1. manifest中声明权限
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
复制代码
  1. 申请权限代码
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
    startActivity(Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION))
}
```kt
3. 实现效果
![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f9aa67860d724340b6ed82ab7091a364~tplv-k3u1fbpfcp-zoom-1.image)
### 其它
#### 权限方面
- 使用使用共享存储空间访问其它应用创建的应用需要申请存储权限,自己应用本次安装创建的文件不需要申请权限。
- 如果应用`target`版本大于 9,那么我们仅应该在<=9 的版本中进行权限申请,可以通过如下配置设置:
maxSdkVersion
```kt
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
                 android:maxSdkVersion="28" />
复制代码
  • 如果应用使用了存储兼容功能,那么仍然需要访问存储权限

存储兼容指的就是我们准备升级android10但是暂时不想使用分区存储

开启使用存储兼容只需要在 manifest 中 application 标签声明如下配置即可:

android:requestLegacyExternalStorage="true"
复制代码


切换媒体文件待处理状态

如果应用操作可能非常耗时(例如写入文件),那么在我们操作文件期间应该避免让其他应用有处理文件的机会。我们可以通过将 ContentValue.put(MediaStore.Audio.Media.IS_PENDING, 1) 标记的值设为 1 来获取此独占访问权限。这样就只有我们的的应用可以操作该文件,直到我们的应用将 IS_PENDING 的值改回 0。


照片中的位置信息

相册中的照片可能会包含敏感信息,例如位置信息,这个信息默认是不允许用户进行查看的,如果想查看需要申请 ACCESS_MEDIA_LOCATION权限



相关文章
|
6月前
|
存储 安全 Java
Android DataStore:安全存储和轻松管理数据
Android DataStore:安全存储和轻松管理数据
|
9月前
|
存储 编解码 Android开发
如何使用 VMware 安装安卓虚拟机,如何配置虚拟机的网络和存储?
如何使用 VMware 安装安卓虚拟机,如何配置虚拟机的网络和存储?
772 0
|
存储 XML 缓存
Android 初代 K-V 存储框架 SharedPreferences,旧时代的余晖?
SharedPreferences 是 Android 平台上轻量级的 K-V 存储框架,亦是初代 K-V 存储框架,至今被很多应用沿用。 有的小伙伴会说,SharedPreferences 是旧时代的产物,现在已经有 DataStore 或 MMKV 等新时代的 K-V 框架,没有学习意义。但我认为,虽然 SharedPreference 这个方案已经过时,但是并不意味着 SharedPreference 中使用的技术过时。做技术要知其然,更要知其所以然,而不是人云亦云,如果要你解释为什么 SharedPreferences 会过时,你能说到什么程度?
114 0
|
存储 Java Android开发
Android 7.1 设置-存储信息显示不正确
Android 7.1 设置-存储信息显示不正确
202 0
Android 7.1 设置-存储信息显示不正确
|
存储 API 文件存储
Android | 作用域存储适配
Android | 作用域存储适配
|
存储 Shell Android开发
【Android 逆向】获取安装在手机中的应用的 APK 包 ( 进入 adb shell | 获取 root 权限 | 进入 /data/app/ 目录 | 拷贝 base.apk 到外置存储 )
【Android 逆向】获取安装在手机中的应用的 APK 包 ( 进入 adb shell | 获取 root 权限 | 进入 /data/app/ 目录 | 拷贝 base.apk 到外置存储 )
434 0
【Android 逆向】获取安装在手机中的应用的 APK 包 ( 进入 adb shell | 获取 root 权限 | 进入 /data/app/ 目录 | 拷贝 base.apk 到外置存储 )
|
存储 XML API
Android调用相机拍照录视频录音以及存储,7.0以上及以下都可使用。
Android调用相机拍照录视频录音以及存储,7.0以上及以下都可使用。
295 0
|
存储 缓存 Linux
Android内、外存储分区&常用存储目录,getExternalStorageDirectory获取的是storage/emulated/0/
Android内、外存储分区&常用存储目录,getExternalStorageDirectory获取的是storage/emulated/0/
1325 0
|
SQL 存储 Oracle
【Android】7.0复杂数据的存储SQLite(内置数据库)Room框架
常用的数据库 sqlserver oracle mysql Android嵌入式数据库SQLite SQLite数据库支持SQL语法和ACID事务,适用于存储大量的关系型数据 eg:qq的聊天列表 学习强国的新闻
510 0
|
存储 API 文件存储
Android 文件存储-图片存储
因 Android1 1谷歌禁止使用requestLegacyExternalStorage ,故将存储方式分为两种方式来进行文件存储。 存储你的应用打算与其他应用共享的文件,包括媒体、文档和其他文件。在这里咱们将图片保存至图库(共享文件)。
143 0
Android 文件存储-图片存储