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/463580.html

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

相关文章

STM32系列——F103C8T6 控制SG90舵机(HAL库)

文章目录 一、舵机控制原理二、.CubeMX配置配置RCC、SYS、时钟树配置RCC配置SYS配置时钟树配置定时器产生PWM波形 Keil5代码接线图及效果如果您发现文章有错误请与我留言&#xff0c;感谢 一、舵机控制原理 舵机的控制一般需要一个20ms左右的时基脉冲&#xff0c;该脉冲的高电平…

前端框架的发展史介绍框架特点

目录 1.前端框架的发展历程 2.官网、优缺点、使用场景 2.1 jQuery 2.2 AngularJS 2.3 React 2.4 Vue.js 2.5 Angular 1.前端框架的发展历程 jQuery&#xff08;2006年&#xff09;&#xff1a;jQuery是一个非常流行的JavaScript库&#xff0c;用于简化DOM操作和事件处理…

HTML5:七天学会基础动画网页13

看完前面很多人可能还不是很明白0%-100%那到底是怎么回事&#xff0c;到底该怎么用&#xff0c;这里我们做一个普遍的练习——心跳动画 想让心❤跳起来&#xff0c;我们先分析一波&#xff0c;这个心怎么写&#xff0c;我们先写一个正方形&#xff0c;再令一个圆形前移: 再来一…

如何快速搭建物联网工业云平台

随着物联网技术的快速发展&#xff0c;物联网工业云平台已经成为推动工业领域数字化转型的重要引擎。合沃作为专业的物联网云服务提供商&#xff0c;致力于为企业提供高效、可靠的物联网工业云平台解决方案。本文将深入探讨物联网工业云平台的功能、解决行业痛点的能力以及如何…

使用Laravel安装器创建项目

使用Laravel安装器创建项目&#xff0c;使用Laravel安装器创建前先确保你的机器上已经下载了Laravel安装程序&#xff0c;可以通过终端界面查询是否下载了Laravel安装器&#xff0c;在终端中输入Laravel 查询&#xff0c;如下图所示则已下载Laravel安装程序&#xff0c;&#x…

OPENCV(0-1之0.2)

OPENCV-0.2 学习安排图像基础像素访问和修改像素值 色彩空间转换RGB到灰度的转换RGB到HSV的转换 图像操作裁剪缩放旋转和翻转 图像滤波平滑和模糊图像边缘检测 图像变换仿射变换透视变换 总结 官方文档 学习安排 图像基础 像素&#xff1a;了解像素的概念&#xff0c;包括像素…

【原创】java+swing+mysql二手车交易管理系统

前言&#xff1a; 本文主要介绍了二手车交易管理设计与实现。首先&#xff0c;通过市场需求&#xff0c;我们确定了二手车的功能&#xff0c;通常的二手车交易系统都是B/S架构&#xff0c;然而我们今天要用javaswing去开发一个C/S架构的二手车交易管理系统&#xff0c;主要功能…

从政府工作报告中的IT热词统计探计算机行业发展(一)数字+:21次

政府工作报告作为政府工作的全面总结和未来规划&#xff0c;不仅反映了国家整体的发展态势&#xff0c;也为各行各业提供了发展的指引和参考。随着信息技术的快速发展&#xff0c;计算机行业已经成为推动经济社会发展的重要引擎之一。因此&#xff0c;从政府工作报告中探寻计算…

Tomcat内存马

Tomcat内存马 前言 描述Servlet3.0后允许动态注册组件 这一技术的实现有赖于官方对Servlet3.0的升级&#xff0c;Servlet在3.0版本之后能够支持动态注册组件。 而Tomcat直到7.x才支持Servlet3.0&#xff0c;因此通过动态添加恶意组件注入内存马的方式适合Tomcat7.x及以上。…

蓝桥杯小白赛第 7 场 3.奇偶排序(sort排序 + 双数组)

思路&#xff1a;在第一次看到这道题的时候我第一想法是用冒泡&#xff0c;但好像我的水平还不允许我写出来。我又读了遍题目发现它的数据很小&#xff0c;我就寻思着把它分成奇偶两部分。应该怎么分呢&#xff1f; 当然在读入的时候把这个问题解决就最好了。正好它的数据范围…

MySQL-JDBC初识

文章目录 前言一、数据库编程的必备条件二、 Java的数据库编程&#xff1a;JDBC三、JDBC工作原理四、JDBC使用4.1 JDBC开发案例4.2 JDBC使用步骤总结 五、JDBC常用接口和类5.1 JDBC API5.2 数据库连接Connection5.3 Statement对象5.4 ResultSet对象 前言 为最近学习的JDBC知识…

Github: Github actions 自动化工作原理与多workflow创建

Github actions 1 &#xff09;概述 Github Actions 是Github官方推出的 CI/CD 解决方案 https://docs.githu.com/en/actions 优点 自动发布流程可减少发布过程中手动操作成本&#xff0c;大幅提升ci/cd效率&#xff0c;快速实现项目发布上线 缺点 存在较高的技术门槛需要利用…

/usr/local/bin/docker-compose: line 1: Not: command not found

安装docker-compose 检查是否安装成功 docker-compose --version 出错 /usr/local/bin/docker-compose: line 1: Not: command not found 检查下载连接是否正确 官网 https://dockerdocs.cn/compose/install/ 根据官网上连接下载 发现下载不了 在版本前加个V 就可以解决 版…

【C++】类和对象终章

&#x1f525;博客主页&#xff1a; 小羊失眠啦. &#x1f3a5;系列专栏&#xff1a;《C语言》 《数据结构》 《C》 《Linux》 《Cpolar》 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 文章目录 一、初始化列表1.1 初始化列表的形式1.2 初始化列表的注意事项 二、explicit关键…

一个页面请求从在浏览器中输入网址到页面最终呈现

前言-与正文无关 生活远不止眼前的苦劳与奔波&#xff0c;它还充满了无数值得我们去体验和珍惜的美好事物。在这个快节奏的世界中&#xff0c;我们往往容易陷入工作的漩涡&#xff0c;忘记了停下脚步&#xff0c;感受周围的世界。让我们一起提醒自己&#xff0c;要适时放慢脚步…

校园闲置物品租售系统|基于springboot框架+ Mysql+Java+B/S架构的校园闲置物品租售系统设计与实现(可运行源码+数据库+设计文档)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 目录 前台功能效果图 管理员功能登录前台功能效果图 ​编辑 用户功能模块 商品购买管理 卖家功能模块 商品…

QML 布局管理器之GridLayout

一.QML GridLayout介绍 在QML中&#xff0c;GridLayout是一种用于布局元素的容器。它允许您以网格形式组织和排列元素。要使用rowspan、columnspan、layoutFillWidth和rowSpacing属性&#xff0c;您可以将一个元素跨越多行和多列&#xff0c;并填充整个宽度&#xff0c;同时设置…

【正则表达式】正则表达式里使用变量

码 const shuai No My Name Is ShuaiGe.match(new RegExp(shuai, gi)); //↑↑↑↑↑↑↑↑ //等同于 //↓↓↓↓↓↓↓↓ /No/gi.test(My Name Is ShuaiGe)用作领域 搜索的字符动态改变&#xff0c;例如↓模糊搜索例&#xff1a; 一个文本宽&#xff0c;输入文本模糊搜索用…

面试常问:为什么 Vite 速度比 Webpack 快?

前言 最近作者在学习 webpack 相关的知识&#xff0c;之前一直对这个问题不是特别了解&#xff0c;甚至讲不出个123....&#xff0c;这个问题在面试中也是常见的&#xff0c;作者在学习的过程当中总结了以下几点&#xff0c;在这里分享给大家看一下&#xff0c;当然最重要的是…

实现一个横向的picker

Picker 选择器显示一个或多个选项集合的可滚动列表&#xff0c;相比于原生 picker&#xff0c;实现了 iOS 与 Android 端体验的一致性。 要实现横向 picker&#xff0c;其实跟纵向 picker差不多&#xff0c;都支持滚动时停留在指定位置&#xff0c;并且支持滚动到边界支持反弹…