Android - 分区存储 MediaStore、SAF
一、概念
分区存储(Scoped Storage)的推出是针对 APP 访问外部存储的行为(乱建乱获取文件和文件夹)进行规范和限制,以减少混乱使得用户能更好的控制自己的文件。
公有目录被分为两大类:媒体文件(图片、音频、视频)的访问使用 MediaStore,其它文件通过系统的文件选择器访问 Storage Access Framework(简称SAF)。
二、MediaStore
跳转ContentProvider
class MediaStore.Images | 所有图片内容的类。 |
class MediaStore.Video | 所有视频内容的类。 |
class MediaStore.Audio | 所有音频内容的类。 |
class MediaStore.Files | 文件储存库中所有文件的索引,包括非媒体文件和媒体文件类。 |
interface MediaStore.MediaColumns | 文件储存库中表的公共字段(文件的各种信息)。 |
2.1 获取 Uri
使用 Context 获取到 ContentResolver 对象,通过 Uri 即可获取各种媒体库的 ContentProvider,从而对媒体文件进行操作。
文件类型 | MediaStore 常量 | Uri 地址 |
图片 | MediaStore.Images.Media.EXTERNAL_CONTENT_URI | content://media/external/images/media |
视频 | MediaStore.Video.Media.EXTERNAL_CONTENT_URI | content://media/external/video/media |
音频 | MediaStore.Audio.Media.EXTERNAL_CONTENT_URI | content://media/external/audio/media |
非媒体文件 | MediaStore.Downloads.Media.EXTERNAL_CONTENT_URI | content://media/external/downloads |
val uri1 = Uri.parse("content://media/external/images/media")
val uri2 = MediaStore.Images.Media.getContentUri("external")
val uri3 = MediaStore.Images.Media.EXTERNAL_CONTENT_URI //推荐
2.2 读取媒体文件
列名(文件信息)可以在 MediaStore.MediaColumns 取公共常量字段,也可以根据文件类型的不同在具体内部类中取值。
文件类型 | MediaStore 常量(常用列名) | 说明 |
图片 | MediaStore.Images.Media._ID | 磁盘上文件的路径 |
MediaStore.Images.Media.DATA | 磁盘上文件的路径 | |
MediaStore.Images.Media.DATE_ADDED | 文件添加到media provider的时间(单位秒) | |
MediaStore.Images.Media.DATE_MODIFIED | 文件最后一次修改单元的时间 | |
MediaStore.Images.Media.DISPLAY_NAME | 文件的显示名称 | |
MediaStore.Images.Media.HEIGHT | 图像/视频的高度,以像素为单位 | |
MediaStore.Images.Media.MIME_TYPE | 文件的 MIME 类型 | |
MediaStore.Images.Media.SIZE | 文件的字节大小 | |
MediaStore.Images.Media.TITLE | 标题 | |
MediaStore.Images.Media.WIDTH | 图像/视频的宽度,以像素为单位 | |
视频 | MediaStore.Video.Media.TITLE | 名称 |
MediaStore.Video.Media.DURATION | 总时长 | |
MediaStore.Video.Media.DATA | 地址 | |
MediaStore.Video.Media.SIZE | 大小 | |
MediaStore.Video.Media.WIDTH | 视频的宽度,以像素为单位 | |
MediaStore.Video.Media.HEIGHT | 视频的高度,以像素为单 | |
音频 | MediaStore.Audio.Media.TITLE | 歌名 |
MediaStore.Audio.Media.ARTIST | 歌手 | |
MediaStore.Audio.Media.DURATION | 总时长 | |
MediaStore.Audio.Media.DATA | 地址 | |
MediaStore.Audio.Media.SIZ | 大小 |
public final Cursor query ( Uri uri, //要查询的 ContentProvider 的 Uri String[] projection, //要查询的字段(列Column),用 null 表示返回所有字段内容。 String selection, //查询条件,相当于SQL语句中的where,用 null 表示不进行筛选。 String[] selectionArgs, //如果 selection 里有?符号这里可以以实际值代替。没有的话可以为null。 String sortOrder //对结果进行排序,相当于SQL语句中的Order by,升序 asc /降序 desc,null为默认排序。 ) 返回的是一个封装了结果集的游标对象 Cursor ,资源用完需要调用 close() 关闭。 |
//获取图片类型的Uri
val uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
//要获取的信息(列名)
val projection = arrayOf(MediaStore.Images.Media._ID, //获取IDMediaStore.Images.Media.MIME_TYPE, //获取MIME_TYPEMediaStore.Images.Media.DISPLAY_NAME //获取DISPLAY_NAME
)
//筛选条件(png格式的图片)
val selection = "${MediaStore.Images.Media.DISPLAY_NAME}='.png'" // ='xx.png' 改成 =?
//筛选条件的参数
val selectionArgs = arrayOf(".png") //替换筛选条件语句中?部分
//对结果的排序方式
val sortOrder = "${ContactsContract.Contacts._ID} DESC" //注意:desc前有空格
//开始查询(返回的是一个封装了结果集的游标对象,资源用完需要关闭使用use函数)
contentResolver.query(uri, projection, selection, selectionArgs, sortOrder)?.use { cursor ->//表都是通过行和列定位到具体的位置然后数据将其取出cursor.run {//获取字段在第几列(查询什么才能取出什么,否则空指针异常)val idIndex = getColumnIndexOrThrow(MediaStore.Images.Media._ID)val mimeTypeIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.MIME_TYPE)val displayNameIndex = cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME)//循环取出每一行对应字段的数据while (moveToNext()) {val id = getLong(idIndex)val mineType = getString(mimeTypeIndex)val displayName = getString(displayNameIndex)//合成图片的UriContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id)//TODO...}}
}
//获取到的 Uri 可以通过 Glide 显示
Glide.with(context).load(uri).into(imageView)
//手动解析成图片的话
contentResolver.openFileDescriptor(uri, "")?.use {val bitmap = BitmapFactory.decodeFileDescriptor(it.fileDescriptor)imageView.setImageBitmap(bitmap)
}
2.3 写入媒体文件
通过 MediaStore 创建文件会保存到对应类型的默认目录中,也可以指定存放到其它同类型的公有目录或子文件夹中。如果存放到不同类型的公有目录中会报错 IllegalArgumentException(但是三种都可以存到Download中)。
文件类型 | mimeType 文件类型 | 默认存储目录(其它允许存储目录) |
图片 | image/* | Pictures(DICM) |
视频 | video/* | Movies(DICM) |
音频 | audio/* | Music(Alarms、Notifications、Podcasts、Ringtones) |
文件 | file/* | Download |
public final Uri insert( Uri url, ContentValues values) 构造一个 ContentValues 对象通过 ContentResolver.insert 插入到对应的目录中,对返回的 Uri 对象进行文件流写入即可。 |
val values = ContentValues().apply {//指定 MimeTypeput(MediaStore.Images.Media.MIME_TYPE,"image/png")//指定文件名put(MediaStore.Images.Media.DISPLAY_NAME,"${System.currentTimeMillis()}.png")//指定保存的文件目录(如果不设置这个值,则会被默认保存到对应的媒体类型的文件夹下)if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {//Android 10中新增了一个RELATIVE_PATH常量,表示文件存储的相对路径,可选值有DIRECTORY_DCIM、DIRECTORY_PICTURES、DIRECTORY_MOVIES、DIRECTORY_MUSICput(MediaStore.Images.Media.RELATIVE_PATH, "${Environment.DIRECTORY_PICTURES}/DemoPicture")} else {//之前的系统版本中并没有RELATIVE_PATH,所以要使用 DATA 并拼装出一个文件存储的绝对路径才行put(MediaStore.MediaColumns.DATA, "${Environment.getExternalStorageDirectory().path}${File.separator}${Environment.DIRECTORY_DCIM}${File.separator}${System.currentTimeMillis()}.png")}
}
//插入文件数据库并获取到文件的Uri
val uri= contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
//对Uri进行文件流写入
uri?.let {//通过outputStream将本地图片bitmap或网络图片输入流写入UrlcontentResolver.openOutputStream(it)?.use { outputStream ->//TODO...//bitmap.compress(Bitmap.CompressFormat.PNG,100, outputStream)}
}
2.4 下载文件到Download目录
方式和上面的写入一样,将网络获取的输入流写入。
- 注意:MediaStore.Downloads是Android 10中新增的API,Android 9及以下的系统版本仍然使用之前的代码来进行文件下载。
val inputStream = XXX.inputStream
val bis = BufferedInputStream(inputStream)
val buffer = ByteArray(1024)//对Uri进行文件流写入
insertUri?.let {//通过outputStream将本地bitmap或网络输入流写入UrlcontentResolver.openOutputStream(it)?.use { outputStream ->BufferedOutputStream(outputStream).use { bos ->var bytes = bis.read(buffer)while (bytes >= 0) {bos.write(buffer, 0, bytes)bos.flush()bytes = bis.read(buffer)}}}
}
三、使用文件选择器 SAF
对于非媒体文件,无法像之前那样手写一个文件浏览器,而是必须使用系统提供的内置文件选择器。通过 Intent 启动系统的文件选择器,然后在 onActivityResult() 中获取到用户选中文件的 Uri 通过ContentResolver打开文件输入流来进行读取就可以了。
const val PICK_FILE = 1private fun pickFile() {val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)intent.addCategory(Intent.CATEGORY_OPENABLE)intent.type = "*/*"startActivityForResult(intent, PICK_FILE)
}override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {super.onActivityResult(requestCode, resultCode, data)when (requestCode) {PICK_FILE -> {if (resultCode == Activity.RESULT_OK && data != null) {val uri = data.dataif (uri != null) {val inputStream = contentResolver.openInputStream(uri)// 执行文件读取操作}}}}
}
四、第三方库不支持的解决办法
编写一个文件复制功能,将Uri对象所对应的文件复制到应用程序的关联目录下,然后再将关联目录下这个文件的绝对路径传递给第三方SDK,这样就可以完美进行适配了。
fun copyUriToExternalFilesDir(uri: Uri, fileName: String) {val inputStream = contentResolver.openInputStream(uri)val tempDir = getExternalFilesDir("temp")if (inputStream != null && tempDir != null) {val file = File("$tempDir/$fileName")val fos = FileOutputStream(file)val bis = BufferedInputStream(inputStream)val bos = BufferedOutputStream(fos)val byteArray = ByteArray(1024)var bytes = bis.read(byteArray)while (bytes > 0) {bos.write(byteArray, 0, bytes)bos.flush()bytes = bis.read(byteArray)}bos.close()fos.close()}
}
五、管理设备上所有的文件(公有目录 + 自定义目录)
绝大部分的应用程序都不应该申请这个权限,仅适用于文件浏览器、病毒查杀类APP,需要跳转到系统页面让用户手动授权,Play商店上架也会更严格。即便得到授权也只能访问 公有目录 + 自定义目录,依然无法访问私有目录。
5.1 权限声明
//不加 ignore 属性 AndroidStudio 会用警告提醒。
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"tools:ignore="ScopedStorage" />
5.2 跳转系统页面授权
//系统低于11或者方法返回true说明已经拥有整个SD卡管理权限
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R || Environment.isExternalStorageManager()) {Toast.makeText(this, "已获得访问所有文件权限", Toast.LENGTH_SHORT).show()
} else {//否则弹窗告知申请原因并跳转到系统授权界面让用户手动授权val builder = AlertDialog.Builder(this).setMessage("本程序需要您同意允许访问所有文件权限").setPositiveButton("确定") { _, _ ->val intent = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)startActivity(intent)}builder.show()
}
六、修改其它APP贡献的文件
修改其它APP贡献的文件是不安全的行为,默认情况下会抛异常,需要跳转到系统页面让用户手动授权,仅适用于美图秀秀类APP。在 Android 10 中每次跳转授权只能操作一张图片,如果一个程序需要修改很多张图片会很麻烦,在 Android 11 中提供了 Batch Operations 从而一次性对多个文件的操作权限进行申请。
- 由于10 之前没有分区存储,10 和 11以后是两套处理方案,专门针对 10 一个版本去写处理方案会很麻烦,由于 10 不是强制启用分区存储,可以在 AndroidManifest 中配置 requestLegacyExternalStorage 来禁用。
createWriteRequest() 请求对多个文件的写入权限。 |
createFavoriteRequest() 请求将多个文件加入到Favorite(收藏)的权限。 |
createTrashRequest() 请求将多个文件移至回收站的权限。 |
createDeleteRequest() 请求将多个文件删除的权限。 |
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {/创建了一个集合用于存放所有要批量申请权限的文件Urival urisToModify = listOf(uri1, uri2, uri3, uri4)//创建一个PendingIntentval editPendingIntent = MediaStore.createWriteRequest(contentResolver, urisToModify)//进行权限申请startIntentSenderForResult(editPendingIntent.intentSender, EDIT_REQUEST_CODE, null, 0, 0, 0)
}override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {super.onActivityResult(requestCode, resultCode, data)when (requestCode) {EDIT_REQUEST_CODE -> {if (resultCode == Activity.RESULT_OK) {Toast.makeText(this, "用户已授权", Toast.LENGTH_SHORT).show()} else {Toast.makeText(this, "用户没有授权", Toast.LENGTH_SHORT).show()}}}
}
相关文章:
Android - 分区存储 MediaStore、SAF
官方页面 参考文章 一、概念 分区存储(Scoped Storage)的推出是针对 APP 访问外部存储的行为(乱建乱获取文件和文件夹)进行规范和限制,以减少混乱使得用户能更好的控制自己的文件。 公有目录被分为两大类:…...
Shiro框架权限控制
首先去通过配置类的用户认证,在用户认证完成后,进行用户授权,用户通过授权之后再跳转其他的界面时,会进行一个验证,当前账号是否有权限。 前端权限控制显示的原理 在前端中,通常使用用户的角色或权限信息来…...
centOS7 安装tailscale并启用子网路由
1、在centOS7上安装Tailscale客户端 #安装命令所在官网位置:https://tailscale.com/download/linux #具体命令为: curl -fsSL https://tailscale.com/install.sh | sh #命令执行后如下图所示2、设置允许IP转发和IP伪装。 安装后,您可以启动…...
spring 项目中如何处理跨越cors问题
1.使用 CrossOrigin 注解 作用于controller 方法上 示例如下 RestController RequestMapping("/account") public class AccountController {CrossOriginGetMapping("/{id}")public Account retrieve(PathVariable Long id) {// ...}DeleteMapping(&quo…...
importlib --- import 的实现
3.1 新版功能. 源代码 Lib/importlib/__init__.py 概述 importlib 包具有三重目标。 一是在 Python 源代码中提供 import 语句的实现(并且因此而扩展 __import__() 函数)。 这提供了一个可移植到任何 Python 解释器的 import 实现。 与使用 Python 以…...
【PyTorch】现代卷积神经网络
文章目录 1. 理论介绍1.1. 深度卷积神经网络(AlexNet)1.1.1. 概述1.1.2. 模型设计 1.2. 使用块的网络(VGG)1.3. 网络中的网络(NiN)1.4. 含并行连结的网络(GoogLeNet)1.5. 批量规范化…...
用python编写九九乘法表
1 问题 我们在学习一门语言的过程中,都会练习到编写九九乘法表这个代码,下面介绍如何编写九九乘法表的流程。 2 方法 (1)打开pycharm集成开发环境,创建一个python文件,并编写第一行代码,主要构建…...
Google Gemini 模型本地可视化
Google近期发布了Gemini模型,而且开放了Gemini Pro API,Gemini Pro 可免费使用! Gemini Pro支持全球180个国家的38种语言,目前接受文本、图片作为输入并生成文本作为输出。 Gemini Pro的表现超越了其他同类模型,当前版…...
数据修复:.BlackBit勒索病毒来袭,安全应对方法解析
导言: 黑色数字罪犯的新玩具——.BlackBit勒索病毒,近来成为网络安全领域的头号威胁。这种恶意软件以其高度隐秘性和毁灭性而引起广泛关注。下面是关于.BlackBit勒索病毒的详细介绍,如不幸感染这个勒索病毒,您可添加我们的技术服…...
拓扑排序实现循环依赖判断 | 京东云技术团队
本文记录如何通过拓扑排序,实现循环依赖判断 前言 一般提到循环依赖,首先想到的就是Spring框架提供的Bean的循环依赖检测,相关文档可参考: https://blog.csdn.net/cristianoxm/article/details/113246104 本文方案脱离Spring Be…...
Java的NIO工作机制
文章目录 1. 问题引入2. NIO的工作方式3. Buffer的工作方式4. NIO数据访问方式 1. 问题引入 在网络通信中,当连接已经建立成功,服务端和客户端都会拥有一个Socket实例,每个Socket实例都有一个InputStream和OutputStream,并通过这…...
一个简单的光线追踪渲染器
前言 本文参照自raytracing in one weekend教程,地址为:https://raytracing.github.io/books/RayTracingInOneWeekend.html 什么是光线追踪? 光线追踪模拟现实中的成像原理,通过模拟一条条直线在场景内反射折射,最终…...
C++学习笔记(十二)------is_a关系(继承关系)
你好,这里是争做图书馆扫地僧的小白。 个人主页:争做图书馆扫地僧的小白_-CSDN博客 目标:希望通过学习技术,期待着改变世界。 提示:以下是本篇文章正文内容,下面案例可供参考 文章目录 前言 一、继承关系…...
DC电源模块的设计与制造技术创新
BOSHIDA DC电源模块的设计与制造技术创新 DC电源模块的设计与制造技术创新主要涉及以下几个方面: 1. 高效率设计:传统的DC电源模块存在能量转换损耗较大的问题,技术创新可通过采用高效率的电路拓扑结构、使用高性能的功率开关器件和优化控制…...
Sketch for Mac:实现你的创意绘图梦想的矢量绘图软件
随着数字时代的到来,矢量绘图软件成为了广告设计、插画创作和UI设计等领域中必不可少的工具。在众多矢量绘图软件中,Sketch for Mac(矢量绘图软件)以其强大的功能和简洁的界面脱颖而出,成为了众多设计师的首选。 Sket…...
ReactNative0.73发布,架构升级与更好的调试体验
这次更新包含了多种提升开发体验的改进,包括: 更流畅的调试体验: 通过 Hermes 引擎调试支持、控制台日志历史记录和实验性调试器,让调试过程更加高效顺畅。稳定的符号链接支持: 简化您的开发工作流程,轻松将文件或目录链接到其他…...
SVN忽略文件的两种方式
当使用版本管理工具时,提交到代码库的文档我们不希望存在把一些临时文件也推送到仓库中,这样就需要用到忽略文件。SVN的忽略相比于GIT稍显麻烦,GIT只需要在.gitignore添加忽略规则即可。而SVN有两种忽略方式,一个是全局设置&#…...
手写VUE后台管理系统10 - 封装Axios实现异常统一处理
目录 前后端交互约定安装创建Axios实例拦截器封装请求方法业务异常处理 axios 是一个易用、简洁且高效的http库 axios 中文文档:http://www.axios-js.com/zh-cn/docs/ 前后端交互约定 在本项目中,前后端交互统一使用 application/json;charsetUTF-8 的请…...
JavaScript装饰者模式
JavaScript装饰者模式 1 什么是装饰者模式2 模拟装饰者模式3 JavaScript的装饰者4 装饰函数5 AOP装饰函数6 示例:数据统计上报 1 什么是装饰者模式 在程序开发中,许多时候都我们并不希望某个类天生就非常庞大,一次性包含许多职责。那么我们就…...
C++学习笔记01
01.C概述(了解) c语言在c语言的基础上添加了面向对象编程和泛型编程的支持。 02.第一个程序helloworld(掌握) #define _CRT_SECURE_NO_WARNINGS #include<iostream> using namespace std;//标准命名空间int main() {//co…...
【UE5】初识MetaHuman 创建虚拟角色
步骤 在UE5工程中启用“Quixel Bridge”插件 打开“Quixel Bridge” 点击“MetaHumans-》MetaHuman Presets UE5” 点击“START MHC” 在弹出的网页中选择一个虚幻引擎版本,然后点击“启动 MetaHuman Creator” 等待一段时间后,在如下页面点击选择一个人…...
物流实时数仓:数仓搭建(DWD)一
系列文章目录 物流实时数仓:采集通道搭建 物流实时数仓:数仓搭建 物流实时数仓:数仓搭建(DIM) 物流实时数仓:数仓搭建(DWD)一 文章目录 系列文章目录前言一、文件编写1.目录创建2.b…...
MATLAB安装
亲自验证有效,多谢这位网友的分享: https://blog.csdn.net/xiajinbiaolove/article/details/88907232...
C语言——预处理详解(#define用法+注意事项)
#define 语法规定 #define定义标识符 语法: #define name stuff #define例子 #include<stdio.h> #define A 100 #define STR "abc" #define FOR for(;;)int main() {printf("%d\n", A);printf("%s\n", STR);FOR;return 0; } 运行结果…...
Linux(23):Linux 核心编译与管理
编译前的任务:认识核心与取得核心原始码 Linux 其实指的是核心。这个【核心(kernel)】是整个操作系统的最底层,他负责了整个硬件的驱动,以及提供各种系统所需的核心功能,包括防火墙机制、是否支持 LVM 或 Quota 等文件系统等等&a…...
Oracle RAC环境下redo log 文件的扩容
环境: 有一个2节点RAC每一个节点2个logfile group每一个group含2个member每一个member的大小为200M 目标:将每一个member的大小有200M扩充到1G。 先来看下redo log的配置: SQL> select * from v$log;GROUP# THREAD# SEQUENCE# …...
Java入门学习笔记一
一、Java语言环境搭建 1、JAVA语言的跨平台原理 1.1、什么是跨平台性? 跨平台就是说,同一个软件可以在不同的操作系统(例如:Windows、Linux、mad)上执行,而不需要对软件做任务处理。即通过Java语言编写的…...
分布式块存储 ZBS 的自主研发之旅|元数据管理
重点内容 元数据管理十分重要,犹如整个存储系统的“大黄页”,如果元数据操作出现性能瓶颈,将严重影响存储系统的整体性能。如何提升元数据处理速度与高可用是元数据管理的挑战之一。SmartX 分布式存储 ZBS 采用 Log Replication 的机制&…...
六大设计原则
六大设计原则 1、单一职责原则 一个类或者模块只负责完成一个职责或者功能。 2、开放封闭原则 规定软件中的对象、类、模块和函数对扩展应该是开放的,对于修改应该是封闭的。用抽象定义结构,用具体实现扩展细节。 3、里氏替换原则 如果S是T的子类型…...
dockerfile创建镜像 lNMP+wordpress
dockerfile创建镜像 lNMPwordpress nginx dockernginx mysql dockermysql php dockerphp nginx vim nginx.conf vim Dockerfile docker network create --subnet172.17.0.0/16 --opt "com.docker.network.bridge.name""docker1" mynetwork docker buil…...
wordpress添加flash游戏/经典软文案例200字
为什么80%的码农都做不了架构师?>>> Logback是由log4j创始人设计的又一个开源日志组件。 logback当前分成三个模块:logback-core,logback- classic和logback-access。logback-core是其它两个模块的基础模块。logback-classic是log4j的一个 改…...
网站怎么添加广告/怎么做竞价托管
首先在qq邮箱的设置中打开第一个pop3/SMTAP授权,并且记住授权码 from django.core.mail import send_mail subject 天天生鲜 message 正文 sender settings.EMAIL_FROM receiver [email] send_mail(subject,message,sender,receiver)在settings配置文件中写入 EMAIL_BACK…...
做调查的网站推荐/石家庄关键词优化平台
对于一个网站刚上线,没人知道怎么办?登陆BAIDU、GOOGLE等各大搜索引擎提交你的网站信息吗?友情链接比你到各大搜索引擎提交来的更快。友情链接:网站之间链接互换是互相推广的一种重要方式。这样可以加深网站之间资源共享、用户共享、互相推荐等&#x…...
哪个cms可以做交友网站/网站秒收录
键盘操作 wasd q e f z c alt 蓝图 delay 延迟 加法减法乘法 / 除法 append 字符串加字符串 buildstring 类型强转 创建对象的应用 选择场景物体->场景蓝图右键创建引用 branch 类似if else doonece 只能触发一次 don 触发n次 for循环(n次&…...
校园网站建设与管理/游戏推广员骗局
本文转自摄像头的MIPI接口、DVP接口和CSI接口-百度经验 (baidu.com),感谢作者分享 一般来讲,摄像头的接口主要有MIPI接口、DVP接口、CSI接口三大类; 我们常用的电脑摄像头接口是USB接口,而常见的智能手机上的摄像头是MIPI接口&am…...
做钓鱼网站查处/二级域名注册平台
vector(向量) 数据结构:一个可变长空间的数组,内存中连续存放。优点:遍历效率高,数据插入尾部效率也高。缺点:数据插入或删除时,如果操作的位置不在末尾效率比较底。在同等容器中优…...