详解Android10的分区存储机制(Scoped Storage)适配教程

1. 简介

大家应该都有过这样的体会,手机用着用着里面就充斥着各种不懂的文件夹和文件。甚至是连已经删除的软件的文件夹还存在。

为什么会发生的这样的问题呢?

因为Google的缺席,导致Android生态野蛮生长,导致很多开发规范没有完全被落实。
为了解决这样的问题,Google决定重拳出击,提出了分区存储(Scoped Storage)机制,也叫沙盒存储机制。
那么什么是沙盒存储机制呢。
沙盒机制是一种安全机制,用于防止应用读取其他应用的数据。

  1. 每个应用程序都有自己的存储空间。
  2. 应用程序不能翻过自己的目录,去访问公共目录。
  3. 应用程序请求的数据都要通过权限检测,不符合要求不会被放行。

2. 关于Android10的分区机制

以 Android 10(API 级别 29)及更高版本为目标平台的应用在默认情况下被赋予了对外部存储设备的分区访问权限(即分区存储), 对外部存储文件访问方式重新设计,便于用户更好的管理外部存储文件。如果不符合条件的会以兼容模式运行,兼容模式跟以前一样,根据路径可以直接存储文件。

应用只能看到本应用专有的目录(通过 Context.getExternalFilesDir() 访问)以及特定类型的媒体。除非您的应用需要访问存放在应用的专有目录以及 MediaStore 之外的文件,否则最好使用分区存储。
在发布Android10的时候官方明确表态:

2020年,主要平台版本将要求所有应用都使用分区存储,无论应用的目标 SDK 级别是多少。因此,您应该提前确保您的应用能够使用分区存储。为此,请确保针对搭载 Android 10(API 级别 29)及更高版本的设备启用了该行为。
翻译成通俗语言,不管是使用requestLegacyExternalStorage=true的方式以兼容模式运行还是降低targetSDK都无法在接下来2020年的Android(API 29)10更新中被豁免。

所以为了应用的稳定性,应该尽在进行适配。

3. 具体分区存储权限的介绍

默认情况下,对于targetSdkVersion大于等于29的应用,其访问权限范围限定为分区存储。此应用无需请求与存储相关的用户权限,即可以查看外部存储中以下类型的文件:

  1. 应用外部特定目录中的文件(使用getExternalFilesDir()访问)。
  2. 应用自己创建的照片、视频和音频(通过MediaStore访问)。

分区存储将影响在Android10系统首次安装启动、且targetSdkVersion >=29的应用。需要访问和共享外部存储文件的应用会受到影响,需要进行兼容性适配。

影响范围:
在Android 10上运行的应用:
1.targetSdkVersion <= 28,不受影响
2.如果targetSdkVersion >= 29,默认情况应用外部存储可见性将被过滤,应用需要对分区存储进行适配。

还有值得注意的是以下两种情况比较特殊,不会受到分区存储的影响:

如果应用最先安装在Android 10以下的系统,
1) 然后系统通过Fota升级到Android 10
2) 应用通过更新升级到targetSdkVersion >= 29

下面是关于分区存储权限和其他相关项目的表格。

类型 位置 访问应用自己生成的文件 访问其他应用生成的的文件 访问方法 卸载应用是否删除文件
外部存储 Photo/ Video/ Audio/ 无需权限 需要权限READ_EXTERNAL_STORAGE MediaStore Api
外部存储 Downloads 无需权限 无需权限 通过存储访问框架SAF,加载系统文件选择器
外部存储 应用特定的目录 无需权限 无法直接访问 getExternalFilesDir()获取到属于应用自己的文件路径

4. 专有目录存储

应用读取或写入应有专有的目录中的文件时,不需要获取存储权限。
在应用中想要获取当前应用的专有存储目录路径是可以用Context.getExternalFilesDir()的方式获取。

val dirpath = context.getExternalFilesDir("")
val fileString = dirpath + File.separator
val file = File(fileString)
...  // 剩下的步骤是用Java IO或者其他IO库来写入数据

5. 共享媒体集合存储

在共享媒体集合存储中保存媒体文件时,需要根据文件的类型选择MediaStore。

把相关数据放入到ContentValues中,最后把ContentValues插入到ContentResolver中,并获得返回的Uri。

通过Uri过得OutputStream,然后用Okio的IO库,进行文件的存储。

关于Okio的只是以后有机会的话,我们再好好讲一讲。

不要忘了这里需要获取权限。

// 把图片下载到共有媒体集合中,并在相册中显示
// 创建ContentValues, 并加入信息
val values = ContentValues()
values.put(MediaStore.Images.Media.DESCRIPTION, downloadedFile.name)
values.put(MediaStore.Images.Media.DISPLAY_NAME, downloadedFile.name)
values.put(MediaStore.Images.Media.MIME_TYPE, mimeType)
values.put(MediaStore.Images.Media.TITLE, downloadedFile.name)
values.put(
  MediaStore.Images.Media.RELATIVE_PATH,
  "${Environment.DIRECTORY_PICTURES}/${downloadedFile.name}"
)
// 插入到ContentResolver,并返回Uri
val insertUri = context.contentResolver.insert(
  MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
  values
)

if (insertUri != null) {
  // 获取OutputStream
  val outputStream = context.contentResolver.openOutputStream(insertUri)
if (outputStream != null) {
  sink = outputStream.sink().buffer()
} else {
  return@runCatching FileDownloadResult.OthersError
  }
} else {
  return@runCatching FileDownloadResult.OthersError
}

 val responseBody = response.body ?: return@runCatching FileDownloadResult.OthersError

try {
  val contentLength = responseBody.contentLength()
  if (contentLength > FileUtil.getAvailableSize(dirPath)) {
    continuation.resume(FileDownloadResult.StorageError)
  }
  var totalRead: Long = 0
  var lastRead: Long

  do {
    lastRead = responseBody.source().read(sink.buffer(), BUFFER_SIZE)
    if (lastRead == -1L) {
      break
    }
    totalRead += lastRead
    sink.emitCompleteSegments()
  } while (true)
  sink.writeAll(responseBody.source())
  sink.close()
  responseBody.close()
}

6. 其他

Github: https://github.com/HyejeanMOON/ScopedStorageDemo

到此这篇关于详解Android10的分区存储机制(Scoped Storage)适配教程的文章就介绍到这了,更多相关Android10 分区存储机制内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • AndroidQ(10)分区存储完美适配方法

    前言 最近时间在做AndroidQ的适配,截止到今天AndroidQ分区存储适配完成,期间出现很多坑,目前网上的帖子大部分都是概述变更内容,接下来的几篇帖子都是对分区存储实际经验代码总结,填坑经验,特此记录一下,也为大家提供帮助. 本篇主要是对AndroidQ(10)分区存储适配具体实现 要点: Android Q文件存储机制修改成了沙盒模式 APP只能访问自己目录下的文件和公共媒体文件 对于AndroidQ以下,还是使用老的文件存储方式 这里需要注意:在适配AndroidQ的时候还要兼容Q系统

  • 详解Android10的分区存储机制(Scoped Storage)适配教程

    1. 简介 大家应该都有过这样的体会,手机用着用着里面就充斥着各种不懂的文件夹和文件.甚至是连已经删除的软件的文件夹还存在. 为什么会发生的这样的问题呢? 因为Google的缺席,导致Android生态野蛮生长,导致很多开发规范没有完全被落实. 为了解决这样的问题,Google决定重拳出击,提出了分区存储(Scoped Storage)机制,也叫沙盒存储机制. 那么什么是沙盒存储机制呢. 沙盒机制是一种安全机制,用于防止应用读取其他应用的数据. 每个应用程序都有自己的存储空间. 应用程序不能翻过

  • 详解bash中的初始化机制

    Bash初始化文件 交互式login shell 在下列情况下,我们可以获得一个login shell: 登录系统时获得的顶层shell,无论是通过本地终端登录,还是通过网络ssh登录.这种情况下获得的login shell是一个交互式shell. 在终端下使用--login选项调用bash,可以获得一个交互式login shell. 在脚本中使用--login选项调用bash(例如:#!/bin/bash --login)可以得到一个非交互式的login shell. 使用su -切换到指定用

  • 详解mysql中的存储引擎

    mysql存储引擎概述 什么是存储引擎? MySQL中的数据用各种不同的技术存储在文件(或者内存)中.这些技术中的每一种技术都使用不同的存储机制.索引技巧.锁定水平并且最终提供广泛的不同的功能和能力.通过选择不同的技术,你能够获得额外的速度或者功能,从而改善你的应用的整体功能. 例如,如果你在研究大量的临时数据,你也许需要使用内存存储引擎.内存存储引擎能够在内存中存储所有的表格数据.又或者,你也许需要一个支持事务处理的数据库(以确保事务处理不成功时数据的回退能力). 这些不同的技术以及配套的相关

  • 详解python的内存分配机制

    开始 作为一个实例,让我们创建四个变量并为其赋值: variable1 = 1 variable2 = "abc" variable3 = (1,2) variable4 = ['a',1] #打印他们的ids print('Variable1: ', id(variable1)) print('Variable2: ', id(variable2)) print('Variable3: ', id(variable3)) print('Variable4: ', id(variabl

  • 详解Android应用沙盒机制

    前言 Android使用沙盒来保护用户不受恶意应用的侵害,同时也将应用隔离开来,防止他们互相访问其数据,本文主要对Android应用沙盒中的几种技术做简要的总结. 一.Android应用DAC沙盒 稍微了解Android一点的人都知道,Android上的App并不像Linux上的用户程序那样,启动应用的uid默认就是登录用户的uid,除非你使用sudo或者setuid等机制.而是每个Android应用都对应了一个uid,也就是一个用户,通过Linux系统的DAC机制将应用的数据严格隔离开来. A

  • 详解MySQL多版本并发控制机制(MVCC)源码

    目录 一.前言 二.MVCC(多版本并发控制机制) 2.1.Repeatable Read 2.2.Read Commit 2.3.MVCC的优势 三.MVCC(实现机制) 3.1.select运行栈 3.2.read_view的创建过程 3.3.行版本可见性 3.4.undolog搜索可见版本的过程 3.5.read_view创建时机再讨论 四.MVCC和锁的同时作用导致的一些现象 五.总结 一.前言 作为一个数据库爱好者,自己动手写过简单的SQL解析器以及存储引擎,但感觉还是不够过瘾.<<

  • 详解MySql中InnoDB存储引擎中的各种锁

    目录 什么是锁 InnoDB存储引擎中的锁 锁的算法 行锁的3种算法 幻像问题 锁的问题 脏读 不可重复读 丢失更新 死锁 什么是锁 现实生活中的锁是为了保护你的私有物品,在数据库中锁是为了解决资源争抢的问题,锁是数据库系统区别于文件系统的一个关键特性.锁机制用于管理对共享资源的并发访. 数据库系统使用锁是为了支持对共享资源进行并发访问,提供数据的完整性和一致性 InnoDB存储引擎区别于MyISAM的两个重要特征就是:InnoDB存储引擎支持事务和行级别的锁,MyISAM只支持表级别的锁 In

  • 一文详解Java中的类加载机制

    目录 一.前言 二.类加载的时机 2.1 类加载过程 2.2 什么时候类初始化 2.3 被动引用不会初始化 三.类加载的过程 3.1 加载 3.2 验证 3.3 准备 3.4 解析 3.5 初始化 四.父类和子类初始化过程中的执行顺序 五.类加载器 5.1 类与类加载器 5.2 双亲委派模型 5.3 破坏双亲委派模型 六.Java模块化系统 一.前言 Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最 终形成可以被虚拟机直接使用的Java类型,这个过程

  • 图文详解Java中的序列化机制

    目录 概述 对象序列化和反序列化机制 修改默认的序列化机制 使用transient关键字 自定义readObject.writeObject方法 实现Externalizable接口 serialVersionUID的作用 使用序列化clone 概述 java中的序列化可能大家像我一样都停留在实现Serializable接口上,对于它里面的一些核心机制没有深入了解过.直到最近在项目中踩了一个坑,就是序列化对象添加一个字段以后,使用方系统报了反序列化失败,原因是我们双方的序列化对象没有加上seri

  • 详解Selenium-webdriver绕开反爬虫机制的4种方法

    之前爬美团外卖后台的时候出现的问题,各种方式拖动验证码都无法成功,包括直接控制拉动,模拟人工轨迹的随机拖动都失败了,最后发现只要用chrome driver打开页面,哪怕手动登录也不可以,猜测driver肯定是直接被识别出来了.一开始尝试了改user agent等方式,仍然不行,由于其他项目就搁置了.今天爬淘宝生意参谋又出现这个问题,经百度才知道原来chrome driver的变量有一个特征码,网站可以直接根据特征码判断,经百度发现有4种方法可以解决,记录一下自己做的尝试. 1.mitproxy

随机推荐