Android分区存储到底该怎么做

文章目录

    • 一、Android存储结构
    • 二、什么是分区存储?
    • 三、私有目录和公有目录
    • 三、存储权限和分区存储有什么关系?
    • 四、我们应该该怎么做适配?
      • 4.1、利用File进行操作
      • 4.2、使用MediaStore操作数据库

一、Android存储结构

Android存储分为内部存储外部存储

在这里插入图片描述

二、什么是分区存储?

看下面的未使用分区存储时的结构图,App私有目录就是上面说的内部存储,共享存储空间就是上面说的外部存储

在这里插入图片描述

分区存储就是在外部存储中的这些文件夹不能随便放了,必须相应的文件类型存到相应的目录中才可以。比如图片文件只能放到Picture目录或者DCIM目录中,就不能放到Movies或者Music中了,否则就会报错崩溃。

在这里插入图片描述

这里提一句,Download目录可以放任何类型的文件,这个目录没有类型限制。

在这里插入图片描述

Android10以前,外部存储中的所有文件虽然有分类目录,但是不管文件是什么类型都可以随便存放,比如mp3音频文件可以放到Movies目录中或者Picture目录中。

对于Android10,Google第一次添加了分区存储方案,这是作为的一个过渡版本,并且Google在Android10上添加了一个属性让你来选择是否使用分区存储方案 ,就是在Manifest中配置的:android:requestLegacyExternalStorage="true",默认是false,即开启分区存储。

从Android11开始,Google强制使用分区存储,也就是说requestLegacyExternalStorage这个属性不再起作用了。

三、私有目录和公有目录

data目录下的可以理解为就是内部存储中的私有目录。
sdcard目录下的就作为公有目录,没有分区存储时,如果要访问里面的文件就需要权限。

在这里插入图片描述

如果使用了分区存储,要注意在sdcard目录中,也有私有目录和公有目录的概念。
在sdcard中的data目录下,可以看到应用的包名,这就是外部存储中的私有目录,访问这里面的文件不需要权限,只有访问Android目录外的文件才需要权限。并且卸载应用还会被删除。

在这里插入图片描述

三、存储权限和分区存储有什么关系?

存储权限也跟Android版本有关,我们很容易把它和分区存储的概念搞在一起弄的晕头转向,其实并没有什么关系,所以我们讲分区存储不需要考虑要什么什么权限,那是另外一回事。

四、我们应该该怎么做适配?

对于Android10以前(不包含Android10),没有分区存储的概念,并且我们操作文件都是用File对象。
对于Android10,我们有两种选择,即可以使用分区存储,也可以不使用。
对于Android10以后(不包含Android10),强制分区存储了,我们操作文件就需要用MediaStore来操作数据库才行。

4.1、利用File进行操作

这个就不过多进行介绍,随便百度都有很多。

4.2、使用MediaStore操作数据库

  1. 我们操作的数据库文件其实就存在内部存储的私有目录中:/data/data/com.android.providers.media
    我们可以将数据库导出利用数据库工具查看(需要root),里面有个files表,可以看到很多字段,这些字段就对应我们着我们平常代码中所写的:Media.DATAMedia.DISPLAY_NAMEMedia.DURATION等等,我们可以根据保存的文件类型以及自己的需求来选择需要的字段。

在这里插入图片描述

  1. 同时可以看到每种类型的文件目录也都有相应的字段,不管是文件夹目录还是文件在数据库中都会有一条数据相对应。

在这里插入图片描述

  1. 使用分区存储后,我们操作文件都需要注意相应的文件类型。
    例如MediaStore中的三种类型媒体:音频,视频,图片。
    每种类型都分别有三种Uri:内部存储,外部存储,可移动存储(这个不用太关心)。
        MediaStore.Images
        MediaStore.Video
        MediaStore.Audio
        MediaStore.Images.Media.INTERNAL_CONTENT_URI
        //content//media/internal/image/media
        MediaStore.Images.Media.EXTERNAL_CONTENT_URI
        //content//media/external/image/media
        MediaStore.Images.Media.getContentUri(volumeName)
        //content//media/<volumeName>/image/media

        MediaStore.Video.Media.INTERNAL_CONTENT_URI
        //content//media/internal/video/media
        MediaStore.Video.Media.EXTERNAL_CONTENT_URI
        //content//media/external/video/media
        MediaStore.Video.Media.getContentUri(volumeName)
        //content//media/<volumeName>/video/media

        MediaStore.Audio.Media.INTERNAL_CONTENT_URI
        //content//media/internal/audio/media
        MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
        //content//media/external/audio/media
        MediaStore.Audio.Media.getContentUri(volumeName)
        //content//media/<volumeName>/audio/media

当然MediaStore也还有其他类型,目前一共有五个。
在这里插入图片描述

  1. 我们操作数据库文件每次用到的就是一个Uri,比如说我要插入一张图片,执行完下面这个方法,就可以直接在相册中查看到你添加的图片了。
    private fun insertImage() {
        val displayName = "test.jpg"

        val uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI

        val values = ContentValues()
        //根据文件类型和自己的需求选择字段,比如图片这里就必须要指定MIME_TYPE
        values.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, displayName)
        values.put(MediaStore.Images.ImageColumns.MIME_TYPE, "image/jpg")
        values.put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_PICTURES)

        val imageUri = contentResolver.insert(uri, values)
        //到这里创建的算是一个文件夹,如果通过下面的代码写入数据后就会变成图片文件

        if (imageUri != null) {
            try {
                val bitmap = BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher)
                val outputStream = contentResolver.openOutputStream(imageUri)
                if (outputStream != null) {
                    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
                    outputStream.close()
                }
                ToastUtil.showToast(this, "添加图片成功")
            } catch (e: Exception) {
                e.printStackTrace()
            }
        } else {
            ToastUtil.showToast(this, "操作失败")
        }
    }
  1. 简单的查询操作,比如查询上面添加的那张图片。
    private fun query(): Uri? {
        val uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI

        //查询条件,根据DISPLAY_NAME
        val selection = MediaStore.Images.Media.DISPLAY_NAME + "=?"
        val args: Array<String> = arrayOf("test.jpg")
        val projection: Array<String> = arrayOf(MediaStore.Images.Media._ID)

        //数据库查询
        val cursor = contentResolver.query(uri, projection, selection, args, null)
        return if (cursor != null && cursor.moveToFirst()) {
            val queryUri = ContentUris.withAppendedId(uri, cursor.getLong(0))
            cursor.close()
            ToastUtil.showToast(this, "查询成功:$queryUri")
            queryUri
        } else {
            ToastUtil.showToast(this, "查询失败")
            null
        }
    }
  1. 简单的删除操作,注意删除操作前需要先通过查询得到相应的uri。
    private fun delete() {
        //先查询后删除
        val uri = query()
        if (uri != null) {
            contentResolver.delete(uri, null, null)
        }
    }
  1. 简单的修改操作,跟删除一样,也需要先通过查询得到相应的uri。
    private fun update() {
        //先查询后修改
        val uri = query()
        if (uri != null) {
            //修改的字段
            val contentResolver = ContentValues()
            contentResolver.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, "修改后的图片.jpg")

            //操作数据库
            getContentResolver().update(uri, contentResolver, null, null)
        }
    }
  1. 总结一下操作数据库,其实就3步:拿到uri,构建字段条件,执行。拿插入图片来举例。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/473109.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

NBlog Java定时任务-备份MySQL数据

NBlog部署维护流程记录&#xff08;持续更新&#xff09;&#xff1a;https://blog.csdn.net/qq_43349112/article/details/136129806 为了避免服务器被攻击&#xff0c;给博客添加了一个MySQL数据备份功能。 此功能是配合博客写的&#xff0c;有些方法直接用的已有的&#xf…

Matlab中inv()函数的使用

在Matlab中&#xff0c;inv()函数是用来求解矩阵的逆矩阵的函数。逆矩阵是一个与原矩阵相乘后得到单位矩阵的矩阵。在数学中&#xff0c;矩阵A的逆矩阵通常用A^-1表示。 什么是逆矩阵 在数学中&#xff0c;对于一个n阶方阵A&#xff0c;如果存在一个n阶方阵B&#xff0c;使得…

Gradio官方文档

文章目录 构建您的第一个demo分享您的demo进度条受密码保护的应用程序The Interface class&#xff08;接口类&#xff09;Components Attributes&#xff08;组件属性&#xff09;多个输入和输出组件图像示例嵌套列表描述性内容手风琴中的附加输入The 4 Kinds of Gradio Inter…

Android: Gradle 命令

一、查看整个项目依赖传递关系 x.x.x (*) 该依赖已经有了&#xff0c;将不再重复依赖。x.x.x -> x.x.x 该依赖的版本被箭头所指的版本代替。x.x.x -> x.x.x(*) 该依赖的版本被箭头所指的版本代替&#xff0c;并且该依赖已经有了&#xff0c;不再重复依赖。 1. gradlew ap…

冰岛人[天梯赛]

文章目录 题目描述思路AC代码 题目描述 输入样例 15 chris smithm adam smithm bob adamsson jack chrissson bill chrissson mike jacksson steve billsson tim mikesson april mikesdottir eric stevesson tracy timsdottir james ericsson patrick jacksson robin patrickss…

2024年最新Anaconda3 2024版中Jupyter Notebook安装

一、 Anaconda3 2024版下载 1.下载&#xff1a;Free Download | Anaconda 2.等待 解释&#xff1a;默认选择等等下载 &#xff0c;时间可能数分钟 3.安装 解释&#xff1a;打开刚刚下载的Anaconda Navigator&#xff0c;并如图安装低版本&#xff0c;高版本会直接报错 4. …

[zdyz]FreeRTOS笔记

FreeRTOS基础知识 1&#xff0c;任务调度器简介 调度器就是使用相关的调度算法来决定当前需要执行的哪个任务 抢占式调度 时间片调度 协程式调度 略 2&#xff0c;任务状态 运行态 正在执行的任务&#xff0c;该任务就处于运行态&#xff0c;注意在STM32中&#xff0c;同…

【Web】记录[长城杯 2022 高校组]b4bycoffee题目复现

目录 前言 环境准备 简单分析 EXP(两种打法) 生成Payload 恶意类 ①Spring命令执行回显类 ②Filter型内存马 前言 本地jar包运行打通了&#xff0c;远程500&#xff0c;nss靶机有问题&#xff0c;换了bugku就可( 主要记录下做题过程&#xff0c;纯菜狗&#xff0c;小…

深圳库卡机器人KR460控制柜维修快速解决

一、库卡机器人KR460控制柜常见故障类型 库卡机器人KR460控制柜可能出现的故障类型多种多样&#xff0c;常见的包括电源故障、通信故障、过热保护以及电路板损坏等。这些故障可能导致机器人不能启动、运行不稳定或突然停机等问题。 二、库卡机器人KR460控制柜维修前的准备 在开…

代码随想录刷题day29|非递减子序列全排列全排列II

文章目录 day29学习内容一、非递减子序列1.1、代码-错误写法1.1.1 多了一个return语句。1.1.2、nums[i-1] > nums[i]&#xff0c;这个条件写错了&#xff0c;为什么呢&#xff1f;1. 忽略了回溯算法的动态决策过程2. 限制了可能的递增子序列的探索 1.2、代码-正确写法 二、全…

javaSwing超级玛丽

一、摘要 摘要 近年来&#xff0c;Java作为一种新的编程语言&#xff0c;以其简单性、可移植性和平台无关性等优点&#xff0c;得到了广泛地应用。J2SE称为Java标准版或Java标准平台。J2SE提供了标准的SDK开发平台。利用该平台可以开发Java桌面应用程序和低端的服务器应用程序…

小白学视觉 | 超详细!Python中 pip 常用命令

本文来源公众号“小白学视觉”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;超详细&#xff01;Python中 pip 常用命令 相信对于大多数熟悉Python的人来说&#xff0c;一定都听说并且使用过pip这个工具&#xff0c;但是对它的了…

C语言例3-35:长度运算的例子

长度运算符的表现形式&#xff1a; sizeof(数据类型符&#xff09; 或 sizeof(变量&#xff09; 长度运算符的优先级&#xff1a; 与单目算术运算符、单目逻辑运算符、自增和自减运算符的优先级相同。上述优先级相同的运算符的结合性都是从右至左。 长度运算的例子 代码如…

python网络爬虫实战教学——urllib的使用(2)

文章目录 专栏导读1、前言2、URLError3、HTTPError4、urlparse5、urlunparse 专栏导读 ✍ 作者简介&#xff1a;i阿极&#xff0c;CSDN 数据分析领域优质创作者&#xff0c;专注于分享python数据分析领域知识。 ✍ 本文录入于《python网络爬虫实战教学》&#xff0c;本专栏针对…

SpringWEB组件及运行流程

SpringWEB组件 前端控制器&#xff1a; DispatcherServlet&#xff08;不需要程序员开发&#xff09;,由框架提供&#xff0c;在 web.xml 中配置。 作用&#xff1a;统一处理请求和响应&#xff0c;整个流程控制的中心&#xff0c;由它调用其它组件处理 用户的请求. 处理…

基于QGraphicsView的图像显示控件,支持放大、缩小、鼠标拖动

原链接 前言 这是一个Qt平台的基于QGraphicsView类的图像显示控件&#xff0c;支持输入QPixmap、QImage、opencv的从cv::Mat类。 实现平台&#xff1a;Windows 10 x64 Qt 6.2.3 MSVC 2019 opencv 4.5 先来看演示视频 控件类实现 ImageViewer.h文件 #ifndef IMAGEVIEWER…

力扣刷题Days23-35.搜索插入的位置(js)

1&#xff0c;题目 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。请必须使用时间复杂度为 O(log n) 的算法。 2&#xff0c;代码 /*** param {number[]} nums*…

Autosar Crypto Driver学习笔记(二)

文章目录 Crypto DriverFunction definitionsGeneral APICrypto_InitCrypto_GetVersionInfo Job Processing InterfaceCrypto_ProcessJob Job Cancellation InterfaceKey Management InterfaceKey Setting Interface密钥设置接口Crypto_KeyElementSetCrypto_KeySetValid Key Ex…

206.翻转链表

给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1]示例 2&#xff1a; 输入&#xff1a;head [1,2] 输出&#xff1a;[2,1]示例 3&#xff1a; 输入&#xff1a;head [] 输…

软件设计师:03 - 数据库系统

一、数据模型的分类 1.1、概念数据模型 1.2、结构数据模型 1.3 真题 二、三级模式 概念模式对应的是基本表&#xff0c;概念模式也称为模式 外模式对应的是视图&#xff0c;也称用户模式或者子模式 内模式对应的是数据库里面的存储文件&#xff0c;也称存储模式 真题 三、两级…