当前位置: 首页 > news >正文

Android笔记(十三):结合JetPack Compose和CameraX实现视频的录制和存储

在“Android笔记(八):基于CameraX库结合Compose和传统视图组件PreviewView实现照相机画面预览和照相功能”,文中介绍了拍照功能的实现,在本文中将介绍结合JetPack Compose和CameraX实现视频的录制。
新建一个项目
在这里插入图片描述
在项目中做如下处理:

一、增视频录制的相关依赖库

在项目的模块对应的build.gradle.kt中增加如下的依赖库:
增加CameraX相关库

  val camerax_version = "1.3.0-alpha04"implementation("androidx.camera:camera-core:$camerax_version")implementation("androidx.camera:camera-camera2:${camerax_version}")implementation("androidx.camera:camera-lifecycle:${camerax_version}")implementation("androidx.camera:camera-video:${camerax_version}")implementation("androidx.camera:camera-view:${camerax_version}" )implementation("androidx.camera:camera-extensions:${camerax_version}")

增加Material3中扩展的图标库

  val icon_extended_version = "1.5.4"implementation("androidx.compose.material:material-icons-extended:$icon_extended_version")

注意:因为在前面展示的Android移动应用中使用的图标可选有限,因此增加“androidx.compose.material:material-icons-extended”图标扩展库,丰富图标的选择。

二、在AndroidManifest.xml权限设置使用照相机

  <uses-feature android:name="android.hardware.camera"  android:required="false" /><uses-permission android:name="android.permission.CAMERA" /><uses-permission android:name="android.permission.RECORD_AUDIO" />

三、CameraX视频捕获架构

视频录制需要做两个方面的处理,需要录制视频流和同时录制音频流。然后对音频流和视频流进行压缩处理,最终写入到磁盘保存起来。在图1展示的是这样工作的示意图。
在这里插入图片描述
图1
LifecycleCameraController
CameraX提供了LifecycleCameraController。LifecycleCameraController提供了CameraX的大部分的特性,它是一个高级的控制器类提供了CameraX的核心特性,用于处理照相机的初始化、创建和配置用例(这里的用例提供了将用例参数映射到相机的可用参数的功能,具体这些用例是指:拍照用例:CameraController.IMAGE_CAPTURE、 图像分析用例:CameraController.IMAGE_ANALYSIS、视频录制用例:CameraController.VIDEO_CAPTURE,…),并将它们绑定到一个生命周期拥有者对象。LifecycleCamera监听设备的Motion Sensor(运动感应器),设置目标的旋转参数。

val cameraController: LifecycleCameraController
= LifecycleCameraController(applicationContext).apply {
setEnabledUseCases(CameraController.VIDEO_CAPTURE) //设置用例
}

CameraSelector
CameraSelector用于选择摄像头,具体选择包括:

使用 CameraSelector.DEFAULT_FRONT_CAMERA 请求默认的前置摄像头。
使用 CameraSelector.DEFAULT_BACK_CAMERA 请求默认的后置摄像头。
使用CameraSelector.Builder.addCameraFilter() 按 CameraCharacteristics 过滤可用设备列表。

Recording
Recording是实际上执行录制视频的对象,在活动录制视频时,提供暂停、恢复或停止录制的控件。如果在录制的过程中发生错误,则会初始化一个VideoRecordEvent.Finalized状态,所有的控制将进入空操作。
//创建Recording对象并启动视频录制
val recording = cameraController.startRecording(…)
在这里:startRecording根据录制得到的视频存储处理方式不同有三种形式:
(1)写入到文件

public Recording startRecording(
@NonNull FileOutputOptions outputOptions, //写入到文件的输出参数
@NonNull AudioConfig audioConfig, //音频的配置
@NonNull Executor executor,//将在其上运行事件侦听器的线程池
@NonNull Consumer listener)//处理视频录制的事件监听器

(2)根据文件描述写入到文件

public Recording startRecording(
@NonNull FileDescriptorOutputOptions outputOptions,//写入到文件描述的输出参数
@NonNull AudioConfig audioConfig,//音频的配置
@NonNull Executor executor,//将在其上运行事件侦听器的线程池
@NonNull Consumer listener)//处理视频录制的事件监听器

(3)写入到媒体库

public Recording startRecording(
@NonNull MediaStoreOutputOptions outputOptions,//写入到媒体库的输出参数
@NonNull AudioConfig audioConfig,//音频的配置
@NonNull Executor executor,//将在其上运行事件侦听器的线程池
@NonNull Consumer listener)//处理视频录制的事件监听器

四、结合Compose和CameraX实现视频的录制实例

1.在主活动中设置权限处理

class MainActivity : ComponentActivity() {companion object{//定义权限数组val CAMERAX_PERMISSIONS = arrayOf(android.Manifest.permission.CAMERA,			//请求相机android.Manifest.permission.RECORD_AUDIO)	//请求录制音频}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)//检查权限,若无就请求权限handlePermissions()setContent {val cameraController: LifecycleCameraController = remember {LifecycleCameraController(applicationContext).apply {setEnabledUseCases(CameraController.VIDEO_CAPTURE)}}VideoCaptureAppTheme {MainScreen(cameraController)}}}/*** 检查权限* @return Boolean*/private fun hasRequiredPermissions():Boolean = CAMERAX_PERMISSIONS.all{ContextCompat.checkSelfPermission(applicationContext,it) == PackageManager.PERMISSION_GRANTED}/*** Handle permissions* 处理权限的操作*/private fun handlePermissions(){if(!hasRequiredPermissions()){//请求权限ActivityCompat.requestPermissions(this, CAMERAX_PERMISSIONS,0)}}
}

运行结果如图2所示:
在这里插入图片描述
图2

2. 定义视频预览界面定义

利用view视图组件PreviewView来定义视频预览的界面,同时处理将LifecycleCameraController对象与当前的生命周期对象进行绑定。由当前生命周期拥有者LocalLifecycleOwner.current的生命周期lifecycle的状态将决定摄像机何时打开、启动、停止和关闭。

@Composable
fun VideoPreview(cameraController:LifecycleCameraController,modifier: Modifier = Modifier){val lifecycleOwner = LocalLifecycleOwner.currentAndroidView(factory={context: Context ->//预览视图PreviewView(context).apply{this.controller = cameraControllercameraController.bindToLifecycle(lifecycleOwner)}},modifier = modifier)
}

3. 定义主界面设置摄像头前后镜头切换

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MainScreen(cameraController: LifecycleCameraController){//设置前后摄像头的图标状态var cameraIcon by remember{mutableStateOf(Icons.Default.VideoCameraBack)}Scaffold {Box(modifier = Modifier.fillMaxSize().padding(it)) {//视频预览的界面VideoPreview(cameraController = cameraController, modifier = Modifier.fillMaxSize())//左上角的摄像头前后镜图标IconButton(onClick = {//照相机前后摄像头的切换if (cameraController.cameraSelector == CameraSelector.DEFAULT_FRONT_CAMERA) {//设置后镜头cameraController.cameraSelector = CameraSelector.DEFAULT_BACK_CAMERAcameraIcon = Icons.Default.VideoCameraBack} else {//设置前镜头cameraController.cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERAcameraIcon=Icons.Default.VideoCameraFront}}, modifier = Modifier.offset(16.dp, 16.dp)) {Icon(imageVector = cameraIcon,tint = Color.Green,contentDescription = "摄像头")}}}
}

运行结果如图3所示:
在这里插入图片描述
图3

4.在主活动中增加录制视频的处理

(1)将录制的视频保存到文件中

下列代码将录制视频采用下列录制的方式

public Recording startRecording(
@NonNull FileOutputOptions outputOptions, //写入到文件的输出参数
@NonNull AudioConfig audioConfig, //音频的配置
@NonNull Executor executor,//将在其上运行事件侦听器的线程池
@NonNull Consumer listener)//处理视频录制的事件监听器

这样的视频文件就会保存到当前应用包下的文件目录中,具体代码如下所示:

class MainActivity : ComponentActivity() {private var recording: Recording? = nullcompanion object{val CAMERAX_PERMISSIONS = arrayOf(android.Manifest.permission.CAMERA,//请求相机android.Manifest.permission.RECORD_AUDIO)//请求录制音频}@SuppressLint("UnsafeOptInUsageError")override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)//检查权限,若无就请求权限handlePermissions()setContent {val cameraController: LifecycleCameraController = remember {LifecycleCameraController(applicationContext).apply {setEnabledUseCases(CameraController.VIDEO_CAPTURE)}}VideoCaptureAppTheme {MainScreen(cameraController,this::recordVideo)}}}/*** 检查权限* @return Boolean*/private fun hasRequiredPermissions():Boolean = CAMERAX_PERMISSIONS.all{ContextCompat.checkSelfPermission(applicationContext,it) == PackageManager.PERMISSION_GRANTED}/*** Request permissions* 处理权限的操作*/private fun handlePermissions(){if(!hasRequiredPermissions()){//请求权限ActivityCompat.requestPermissions(this, CAMERAX_PERMISSIONS,0)}}/*** 录制视频* @param cmaeraController LifecycleCameraController*/@SuppressLint("MissingPermission", "UnsafeOptInUsageError")private fun recordVideo(cameraController:LifecycleCameraController){if(recording!=null){//停止录制视频recording?.stop()recording = nullreturn}if(!hasRequiredPermissions())//没有访问权限returnval outputFile = File(filesDir,"video.mp4")//执行视频录制recording = cameraController.startRecording(//输出文件配置FileOutputOptions.Builder(outputFile).build(),//启动语音AudioConfig.create(true),//在当前应用的上下文中创建事件监听器依附运行的线程池ContextCompat.getMainExecutor(applicationContext)){event:VideoRecordEvent->//监听器处理器when(event){is VideoRecordEvent.Finalize->{if(event.hasError()){recording?.close()recording = nullToast.makeText(applicationContext,"录制视频失败",Toast.LENGTH_LONG).show()}else{Toast.makeText(applicationContext,"录制视频成功",Toast.LENGTH_LONG).show()}}}}}
}@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MainScreen(cameraController: LifecycleCameraController,onRecordAction:(cameraController:LifecycleCameraController)->Unit){//设置前后摄像头的图标状态var cameraIcon by remember{mutableStateOf(Icons.Default.VideoCameraBack)}var selected by remember{mutableStateOf(false)}Scaffold(bottomBar = {BottomAppBar {NavigationBarItem(selected = selected,onClick = {onRecordAction(cameraController)},icon = {Icon(imageVector = Icons.Default.Videocam,contentDescription = "视频录制")})}}) {Box(modifier = Modifier.fillMaxSize().padding(it)) {VideoPreview(cameraController = cameraController, modifier = Modifier.fillMaxSize())IconButton(onClick = {//照相机前后摄像头的切换if (cameraController.cameraSelector == CameraSelector.DEFAULT_FRONT_CAMERA) {cameraController.cameraSelector = CameraSelector.DEFAULT_BACK_CAMERAcameraIcon = Icons.Default.VideoCameraBack} else {cameraController.cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERAcameraIcon=Icons.Default.VideoCameraFront}}, modifier = Modifier.offset(16.dp, 16.dp)) {Icon(imageVector = cameraIcon,tint = Color.Green,contentDescription = "摄像头")}}}
}

运行结果如图4所示:
在这里插入图片描述
图4
启动Device Explorer设备浏览器,在:data->data->对应应用的包名->files“目录下可以发现已经录制成功的视频文件video.mp4。
在这里插入图片描述
图5

(2)保存视频到媒体库

上述的代码需要检索并浏览录制的视频非常困难,并不是一个很好的方法。比较好的处理方式,是将录制的视频直接保存在媒体库中。通过将时间戳作为视频文件名,根据时间便可以非常方便的在移动终端的媒体库中检索和浏览录制的视频。则就需要在录制视频中采用如下的方式:

public Recording startRecording(
@NonNull MediaStoreOutputOptions outputOptions,//写入到媒体库的输出参数
@NonNull AudioConfig audioConfig,//音频的配置
@NonNull Executor executor,//将在其上运行事件侦听器的线程池
@NonNull Consumer listener)//处理视频录制的事件监听器

将上述的MainActivity中关于视频录制的私有方法recordVideo重新定义,具体代码如下:

class MainActivity : ComponentActivity() {private var recording: Recording? = nullcompanion object{val CAMERAX_PERMISSIONS = arrayOf(android.Manifest.permission.CAMERA,//请求相机android.Manifest.permission.RECORD_AUDIO)//请求录制音频}@SuppressLint("UnsafeOptInUsageError")override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)//检查权限,若无就请求权限handlePermissions()setContent {val cameraController: LifecycleCameraController = remember {LifecycleCameraController(applicationContext).apply {setEnabledUseCases(CameraController.VIDEO_CAPTURE)}}VideoCaptureAppTheme {MainScreen(cameraController,this::recordVideo)}}}/*** 检查权限* @return Boolean*/private fun hasRequiredPermissions():Boolean = CAMERAX_PERMISSIONS.all{ContextCompat.checkSelfPermission(applicationContext,it) == PackageManager.PERMISSION_GRANTED}/*** Request permissions* 处理权限的操作*/private fun handlePermissions(){if(!hasRequiredPermissions()){//请求权限ActivityCompat.requestPermissions(this, CAMERAX_PERMISSIONS,0)}}/*** 录制视频到文件* @param cmaeraController LifecycleCameraController*/@SuppressLint("MissingPermission", "UnsafeOptInUsageError")private fun recordVideo(cameraController:LifecycleCameraController){if(recording!=null){//停止录制视频recording?.stop()recording = nullreturn}if(!hasRequiredPermissions())//没有访问权限return//定义包含时间戳的视频文件名val name = SimpleDateFormat("yyyy-MM-dd hh:mm:ss",Locale.CHINA).format(System.currentTimeMillis())//定义关于一条视频记录的相关配置val contentValue = ContentValues().apply{put(MediaStore.MediaColumns.DISPLAY_NAME,name)put(MediaStore.MediaColumns.MIME_TYPE,"video/mp4")if(Build.VERSION.SDK_INT>Build.VERSION_CODES.P){put(MediaStore.Video.Media.RELATIVE_PATH,"Movies/CameraX-Video")}}//配置输出到媒体库的输出参数val mediaStoreOutputOptions = MediaStoreOutputOptions.Builder(contentResolver,MediaStore.Video.Media.EXTERNAL_CONTENT_URI).setContentValues(contentValue).build()//执行视频录制recording = cameraController.startRecording(//输出到媒体库的配置mediaStoreOutputOptions,//启动语音AudioConfig.create(true),//在当前应用的上下文中创建事件监听器依附运行的线程池ContextCompat.getMainExecutor(applicationContext)){event:VideoRecordEvent->//监听器处理器when(event){is VideoRecordEvent.Finalize->{if(event.hasError()){recording?.close()recording = nullToast.makeText(applicationContext,"录制视频失败",Toast.LENGTH_LONG).show()}else{Toast.makeText(applicationContext,"录制视频成功",Toast.LENGTH_LONG).show()}}}}}
}

运行录制视频后,在手机模拟器的图片库中可以发现刚刚录制的视频,运行结果如图6所示。
在这里插入图片描述
图6

五、设置WebCam为模拟器的摄像头

当然,模拟器的前后镜头可以自行设置webCam0,如图6所示。
在这里插入图片描述
图7
对于Camera Front和Camera Back可以任选其一设置为Webcam0,不能二者同时进行设置为WebCam0。通过这样的设置,可以在模拟器中启动WebCam,录制实时的视频。运行效果类似图8所示:
在这里插入图片描述
图8

六、参考文献

1.CameraX视频捕获架构

https://developer.android.google.cn/training/camerax/video-capture?hl=zh-cn

相关文章:

Android笔记(十三):结合JetPack Compose和CameraX实现视频的录制和存储

在“Android笔记&#xff08;八&#xff09;&#xff1a;基于CameraX库结合Compose和传统视图组件PreviewView实现照相机画面预览和照相功能”&#xff0c;文中介绍了拍照功能的实现&#xff0c;在本文中将介绍结合JetPack Compose和CameraX实现视频的录制。 新建一个项目 在项…...

【开题报告】基于SpringBoot的音乐鉴赏平台的设计与实现

1.研究背景与意义 音乐是人类文化的重要组成部分&#xff0c;具有广泛的影响力和吸引力。然而&#xff0c;随着数字化时代的到来&#xff0c;传统的音乐鉴赏方式面临一些挑战。因此&#xff0c;设计和开发一个基于Spring Boot的音乐鉴赏平台&#xff0c;能够满足用户对音乐欣赏…...

云原生 黑马Kubernetes教程(K8S教程)笔记——第一章 kubernetes介绍——Master集群控制节点、Node工作负载节点、Pod控制单元

参考文章&#xff1a;kubernetes介绍 文章目录 第一章 kubernetes介绍1.1 应用部署方式演变传统部署&#xff1a;互联网早期&#xff0c;会直接将应用程序部署在物理机上虚拟化部署&#xff1a;可以在一台物理机上运行多个虚拟机&#xff0c;每个虚拟机都是独立的一个环境&…...

ElasticSearch 安装(单机版本)

文章目录 ElasticSearch 安装&#xff08;单机版本&#xff09;环境配置下载安装包调整系统参数安装启动并验证 ElasticSearch 安装&#xff08;单机版本&#xff09; 此文档演示 ElasticSearch 的单机版本在 CentOS 7 环境下的安装方式以及相关的配置。 环境配置 Linux 主机一…...

读书笔记:《BackTrader 量化交易案例图解》

BackTrader 量化软件&#xff1a;https://github.com/mementum/backtrader -> bt 量化框架&#xff08;前身&#xff09;&#xff1a;https://github.com/pmorissette/bt-> ffn 量化框架&#xff08;前前身&#xff09;&#xff1a;https://github.com/pmorissette/ffn T…...

CentOS 7 免密密钥登陆sftp服务 —— 筑梦之路

为什么用sftp而不是ftp&#xff1f; sftp是使用ssh协议安全加密的文件传输协议&#xff0c;ftp在很多时候都是使用的明文传输&#xff0c;相对来说容易被抓包&#xff0c;存在安全隐患。 需求说明 1. 使用sftp代替ftp来做文件存储&#xff0c;锁定目录&#xff0c;不允许用户切…...

记一次 .NET 某券商论坛系统 卡死分析

一&#xff1a;背景 1. 讲故事 前几个月有位朋友找到我&#xff0c;说他们的的web程序没有响应了&#xff0c;而且监控发现线程数特别高&#xff0c;内存也特别大&#xff0c;让我帮忙看一下怎么回事&#xff0c;现在回过头来几经波折&#xff0c;回味价值太浓了。 二&#…...

DevExpress WinForms HeatMap组件,一个高度可自定义热图控件!

通过DevExpress WinForms可以为Windows Forms桌面平台提供的高度可定制的热图UI组件&#xff0c;体验DevExpress的不同之处。 DevExpress WinForms有180组件和UI库&#xff0c;能为Windows Forms平台创建具有影响力的业务解决方案。同时能完美构建流畅、美观且易于使用的应用程…...

振弦传感器表面应变计与振弦采集仪形成岩土工程监测的解决方案

振弦传感器表面应变计与振弦采集仪形成岩土工程监测的解决方案 振弦传感器表面应变计与振弦采集仪可以结合使用&#xff0c;形成岩土工程监测的解决方案。具体的方案包括以下几个步骤&#xff1a; 1. 安装振弦传感器表面应变计&#xff1a;首先需要在需要监测的岩土结构表面安…...

笔记本电脑没有声音?几招恢复声音流畅!

笔记本电脑已经成为我们日常生活和工作的重要工具&#xff0c;而其中的声音是其功能之一。然而&#xff0c;有时您可能会遇到笔记本电脑没有声音的问题&#xff0c;这可能是由多种原因引起的。在本文中&#xff0c;我们将深入探讨笔记本电脑没有声音的常见原因&#xff0c;并提…...

JavaScript学习_01——JavaScript简介

JavaScript简介 JavaScript介绍 JavaScript是一种轻量级的脚本语言。所谓“脚本语言”&#xff0c;指的是它不具备开发操作系统的能力&#xff0c;而是只用来编写控制其他大型应用程序的“脚本”。 JavaScript 是一种嵌入式&#xff08;embedded&#xff09;语言。它本身提供…...

11.10~11.15置信区间,均值、方差假设检验,正态,t,卡方,F分布,第一第二类错误

置信度&#xff0c;置信区间 给定一个置信度&#xff0c;就可以算出一个置信区间。 如果给的置信度越大&#xff0c;那么阿尔法就越小 给的置信度越小&#xff0c;那么α就越大&#xff0c;那么 考虑精确性&#xff0c;希望区间长度尽可能小&#xff0c;所以是取正态的中间…...

【洛谷 P2440】木材加工 题解(二分查找+循环)

木材加工 题目背景 要保护环境 题目描述 木材厂有 n n n 根原木&#xff0c;现在想把这些木头切割成 k k k 段长度均为 l l l 的小段木头&#xff08;木头有可能有剩余&#xff09;。 当然&#xff0c;我们希望得到的小段木头越长越好&#xff0c;请求出 l l l 的最大…...

反向传播详解BP

误差反向传播&#xff08;Back-propagation, BP&#xff09;算法的出现是神经网络发展的重大突破&#xff0c;也是现在众多深度学习训练方法的基础。该方法会计算神经网络中损失函数对各参数的梯度&#xff0c;配合优化方法更新参数&#xff0c;降低损失函数。 BP本来只指损失…...

2023.11.16-hive sql高阶函数lateral view,与行转列,列转行

目录 0.lateral view简介 1.行转列 需求1: 需求2: 2.列转行 解题思路: 0.lateral view简介 hive函数 lateral view 主要功能是将原本汇总在一条&#xff08;行&#xff09;的数据拆分成多条&#xff08;行&#xff09;成虚拟表&#xff0c;再与原表进行笛卡尔积&#xff0c…...

解决Jetson Xavier NX上Invalid CUDA ‘--device 0‘ requested等问题

解决Jetson Xavier NX上Invalid CUDA --device 0 requested等问题 问题1&#xff1a;AssertionError: Invalid CUDA --device 0 requested, use --device cpu or pass valid CUDA device(s)问题2&#xff1a; “Illegal instruction(cpre dumped)”错误记录python http局域网文…...

git push 报错 The requested URL returned error: 500

今天gitpush时报错The requested URL returned error: 500 看报错应该是本地和gitlab服务器之间通信的问题&#xff0c;登录gitlab网站查看 登录时报错无法通过ldapadmin认证&#xff0c;ldap服务器连接失败。 首先&#xff0c;登录ldap服务器&#xff0c;查看是否是ldap服务…...

应用软件安全编程--17预防基于 DOM 的 XSS

DOM型XSS从效果上来说也属于反射型XSS,由于形成的原因比较特殊所以进行单独划分。在网站页面中有许多页面的元素&#xff0c;当页面到达浏览器时浏览器会为页面创建一个顶级的Document object 文档对象&#xff0c;接着生成各个子文档对象&#xff0c;每个页面元素对应一个文档…...

【FastCAE源码阅读9】鼠标框选网格、节点的实现

一、VTK的框选支持类vtkInteractorStyleRubberBandPick FastCAE的鼠标事件交互类是PropPickerInteractionStyle&#xff0c;它扩展自vtkInteractorStyleRubberBandPick。vtkInteractorStyleRubberBandPick类可以实现鼠标框选物体&#xff0c;默认情况下按下键盘r键开启框选模式…...

【ArcGIS处理】行政区划与流域区划间转化

【ArcGIS处理】行政区划与流域区划间转化 引言数据准备1、行政区划数据2、流域区划数据 ArcGIS详细处理步骤Step1&#xff1a;统计行政区划下子流域面积1、创建批量处理模型2、添加批量裁剪处理3、添加计算面积 Step2&#xff1a;根据子流域面积占比均化得到各行政区固定值 参考…...

基于FPGA的PID算法学习———实现PID比例控制算法

基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容&#xff1a;参考网站&#xff1a; PID算法控制 PID即&#xff1a;Proportional&#xff08;比例&#xff09;、Integral&#xff08;积分&…...

Leetcode 3576. Transform Array to All Equal Elements

Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接&#xff1a;3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到&#xf…...

深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法

深入浅出&#xff1a;JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中&#xff0c;随机数的生成看似简单&#xff0c;却隐藏着许多玄机。无论是生成密码、加密密钥&#xff0c;还是创建安全令牌&#xff0c;随机数的质量直接关系到系统的安全性。Jav…...

【JVM】- 内存结构

引言 JVM&#xff1a;Java Virtual Machine 定义&#xff1a;Java虚拟机&#xff0c;Java二进制字节码的运行环境好处&#xff1a; 一次编写&#xff0c;到处运行自动内存管理&#xff0c;垃圾回收的功能数组下标越界检查&#xff08;会抛异常&#xff0c;不会覆盖到其他代码…...

高频面试之3Zookeeper

高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个&#xff1f;3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制&#xff08;过半机制&#xff0…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具

文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

ServerTrust 并非唯一

NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...

Axios请求超时重发机制

Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式&#xff1a; 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云

目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

Spring数据访问模块设计

前面我们已经完成了IoC和web模块的设计&#xff0c;聪明的码友立马就知道了&#xff0c;该到数据访问模块了&#xff0c;要不就这俩玩个6啊&#xff0c;查库势在必行&#xff0c;至此&#xff0c;它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据&#xff08;数据库、No…...