Android 使用CameraX实现预览、拍照、录制视频(Java版)

Android 官方关于相机的介绍如下:
https://developer.android.google.cn/media/camera/get-started-with-camera?hl=zh_cn

一、开始使用 Android 相机

Android相机一般包含前置摄像头和后置摄像头,使用相机可以开发一系列激动人心的应用,例如拍摄视频和图片以便与社交媒体分享,以及创建文档和二维码扫描等实用程序。

二、通过相机 intent实现拍照、录视频

如果只是使用相机进行拍照或录制视频等基本相机操作,则无需使用CameraX或Camera2,可以直接使用 Intent 方式。

2.1 使用相机应用拍照

通过调用Intent来拍照:

static final int REQUEST_IMAGE_CAPTURE = 1;private void dispatchTakePictureIntent() {Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);try {startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);} catch (ActivityNotFoundException e) {// display error state to the user}
}

2.2 使用相机应用录制视频

通过调用Intent来录制视频:

static final int REQUEST_VIDEO_CAPTURE = 1;private void dispatchTakeVideoIntent() {Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);if (takeVideoIntent.resolveActivity(getPackageManager()) != null) {startActivityForResult(takeVideoIntent, REQUEST_VIDEO_CAPTURE);}else {//display error state to the user}
}

清单文件中添加:

    <queries><intent><action android:name="android.media.action.IMAGE_CAPTURE" /></intent><intent><action android:name="android.media.action.VIDEO_CAPTURE" /></intent></queries>

使用Intent.resolveActivity方法时,需要传入一个PackageManager对象。如果Intent可以解析为一个Activity,该方法会返回一个ComponentName;否则返回null。
通过resolveActivity方法,可以检查视频录制页面是否存在,确保应用不会崩溃。

注:resolveActivity方法会提示在清单文件中添加标签,可以查看下面的文章:
Android <queries>声明的作用和配置方法

2.3 使用建议

对于基本的相机操作,请使用 Intent方式。否则,建议您使用 Camera2 和 CameraX 库来处理比基本图片或视频拍摄更复杂的任务。

如果要向 Android 应用中添加相机功能,您有以下三个主要选项:

  • CameraX
  • Camera2
  • Camera(已废弃)

对于大多数开发者,建议使用 CameraX。CameraX 是一个 Jetpack 库,支持绝大多数 Android 设备(Android 5.0 及更高版本),并提供围绕常见用例设计的一致高级别 API。CameraX 会为您解决设备兼容性问题,因此您无需向应用添加针对特定设备的代码。

CameraX 基于 Camera2 软件包构建而成。如果您需要低级别的相机控件来支持复杂的用例,Camera2 是一个不错的选择,但 API 比 CameraX 更复杂。您需要管理设备专属配置。与 CameraX 一样,Camera2 适用于 Android 5.0(API 级别 21)及更高版本。

原始 Android Camera 类已废弃。新应用应使用 CameraX(推荐)或 Camera2,而现有应用应进行迁移,以充分利用新功能,并避免不再兼容未来设备。

三、通过CameraX实现拍照、录视频

CameraX 是一个 Jetpack 库,旨在帮助您更轻松地开发相机应用。

如果您要开发新应用,我们建议您从 CameraX 开始。它提供了一个一致且易于使用的 API,该 API 适用于绝大多数 Android 设备,并向后兼容 Android 5.0(API 级别 21)。

CameraX官方接入步骤:
https://developer.android.google.cn/codelabs/camerax-getting-started?hl=zh-cn#2

3.1 请求必要的权限

<uses-feature android:name="android.hardware.camera.any" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"android:maxSdkVersion="28" />
    //https://github.com/Blankj/AndroidUtilCode  工具类implementation 'com.blankj:utilcodex:1.31.1'
     PermissionUtils.permission(Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.CAMERA,Manifest.permission.RECORD_AUDIO).callback(new PermissionUtils.SimpleCallback() {@Overridepublic void onGranted() {startActivity(new Intent(mContext, CameraXActivity.class));}@Overridepublic void onDenied() {ToastUtils.showShort("请开启相应权限,否则无法拍摄视频!");}}).request();

3.2 实现摄像头 Preview 预览

    private VideoCapture<Recorder> videoCapture;private ImageCapture imageCapture;private Recording recording;/*** 开启摄像头*/public void startCamera() {//创建 ProcessCameraProvider 的实例。这用于将相机的生命周期绑定到生命周期所有者。这消除了打开和关闭相机的任务,因为 CameraX 具有生命周期感知能力。ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(mContext);//向 cameraProviderFuture 添加监听器。添加 Runnable 作为一个参数。添加 ContextCompat.getMainExecutor() 作为第二个参数。这将返回一个在主线程上运行的 Executor。cameraProviderFuture.addListener(new Runnable() {@Overridepublic void run() {// 在 Runnable 中,添加 ProcessCameraProvider。它用于将相机的生命周期绑定到应用进程中的 LifecycleOwnertry {ProcessCameraProvider cameraProvider = cameraProviderFuture.get();// 初始化 Preview 对象,在其上调用 build,从取景器中获取 Surface 提供程序,然后在预览上进行设置。Preview preview = new Preview.Builder().build();preview.setSurfaceProvider(mViewBinding.viewFinder.getSurfaceProvider());//创建 ImageCapture 用例imageCapture = new ImageCapture.Builder().build();//创建 VideoCapture 用例Recorder recorder = new Recorder.Builder().setQualitySelector(QualitySelector.from(Quality.HIGHEST,FallbackStrategy.higherQualityOrLowerThan(Quality.SD))).build();videoCapture = VideoCapture.withOutput(recorder);// 创建 CameraSelector 对象,然后选择 DEFAULT_BACK_CAMERA(后置摄像头)CameraSelector cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA;try {//创建一个 try 代码块。在此块内,确保没有任何内容绑定到 cameraProvidercameraProvider.unbindAll();// 然后将 cameraSelector 和预览对象绑定到 cameraProvidercameraProvider.bindToLifecycle(CameraXActivity.this, cameraSelector, preview, imageCapture, videoCapture);} catch (Exception e) {LogUtils.e("Use case binding failed", e.getMessage());}} catch (Exception e) {LogUtils.e(e.getMessage());}}}, ContextCompat.getMainExecutor(mContext));}

3.3 实现拍照

    /*** 拍照*/private void takePhoto() {// 首先,获取对 ImageCapture 用例的引用。如果用例为 null,请退出函数。如果在设置图片拍摄之前点按“photo”按钮,它将为 null。如果没有 return 语句,应用会在该用例为 null 时崩溃。if (imageCapture == null) {return;}//接下来,创建用于保存图片的 MediaStore 内容值。请使用时间戳,确保 MediaStore 中的显示名是唯一的。String name = TimeUtil.getCurrentTimeName();ContentValues contentValues = new ContentValues();contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, name);contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/CameraX-Image");}// 创建一个 OutputFileOptions 对象。在该对象中,您可以指定所需的输出内容。我们希望将输出保存在 MediaStore 中,以便其他应用可以显示它,因此,请添加我们的 MediaStore 条目。ImageCapture.OutputFileOptions outputOptions = new ImageCapture.OutputFileOptions.Builder(getContentResolver(),MediaStore.Images.Media.EXTERNAL_CONTENT_URI,contentValues).build();// 对 imageCapture 对象调用 takePicture()。传入 outputOptions、执行器和保存图片时使用的回调。imageCapture.takePicture(outputOptions,ContextCompat.getMainExecutor(this),new ImageCapture.OnImageSavedCallback() {@Overridepublic void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {//照片拍摄成功!将照片保存到我们之前创建的文件中,显示消息框,让用户知道照片已拍摄成功,并输出日志语句。String msg = "Photo capture succeeded: " + outputFileResults.getSavedUri();ToastUtils.showShort(name + "\n" + msg);LogUtils.i(msg);}@Overridepublic void onError(@NonNull ImageCaptureException exception) {//如果图片拍摄失败或保存图片失败,请添加错误情况以记录失败。LogUtils.e("Photo capture failed:" + exception.getMessage(), exception);}});}

3.4 实现录制视频

/*** 录制视频*/private void captureVideo() {//检查是否已创建 VideoCapture 用例:如果尚未创建,则不执行任何操作。if (videoCapture == null) {return;}// 在 CameraX 完成请求操作之前,停用按钮;在后续步骤中,它会在我们的已注册的 VideoRecordListener 内重新启用。mViewBinding.videoCaptureButton.setEnabled(false);//如果有正在进行的录制操作,请将其停止并释放当前的 recording。当所捕获的视频文件可供我们的应用使用时,我们会收到通知。Recording curRecording = recording;if (curRecording != null) {curRecording.stop();recording = null;return;}//为了开始录制,我们会创建一个新的录制会话。首先,我们创建预定的 MediaStore 视频内容对象,将系统时间戳作为显示名(以便我们可以捕获多个视频)。String name = TimeUtil.getCurrentTimeName();ContentValues contentValues = new ContentValues();contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, name);contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4");//使用外部内容选项创建 MediaStoreOutputOptions.Builder。MediaStoreOutputOptions mediaStoreOutputOptions = new MediaStoreOutputOptions.Builder(getContentResolver(), MediaStore.Video.Media.EXTERNAL_CONTENT_URI).setContentValues(contentValues).build();//检测录音权限if (ActivityCompat.checkSelfPermission(this,Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {return;}//将输出选项配置为 VideoCapture<Recorder> 的 Recorder 并启用录音:recording = videoCapture.getOutput().prepareRecording(mContext, mediaStoreOutputOptions).withAudioEnabled().start(ContextCompat.getMainExecutor(this),new Consumer<VideoRecordEvent>() {@Overridepublic void accept(VideoRecordEvent videoRecordEvent) {if (videoRecordEvent instanceof VideoRecordEvent.Start) {//当相机设备开始请求录制时mViewBinding.videoCaptureButton.setText(R.string.stop_capture);mViewBinding.videoCaptureButton.setEnabled(true);} else if (videoRecordEvent instanceof VideoRecordEvent.Finalize) {//完成录制后,用消息框通知用户,并将“Stop Capture”按钮切换回“Start Capture”,然后重新启用它:if (!((VideoRecordEvent.Finalize) videoRecordEvent).hasError()) {String msg = "Video capture succeeded: " + ((VideoRecordEvent.Finalize) videoRecordEvent).getOutputResults().getOutputUri();ToastUtils.showShort(msg);LogUtils.e(msg);} else {if (recording != null) {recording.close();recording = null;LogUtils.e("Video capture ends with error: " + ((VideoRecordEvent.Finalize) videoRecordEvent).getError());}}mViewBinding.videoCaptureButton.setText(R.string.start_capture);mViewBinding.videoCaptureButton.setEnabled(true);}}});}

3.5 完整代码

添加 CameraX 依赖项:

dependencies {def camerax_version = "1.5.0-alpha01"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}"
}

CameraX 需要一些属于 Java 8 的方法,因此我们需要相应地设置编译选项。在 android 代码块的末尾,紧跟在 buildTypes 之后,添加以下代码:

compileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility JavaVersion.VERSION_1_8
}
    buildFeatures {viewBinding = truebuildConfig = true}
    <!--CameraX--><string name="take_photo">拍照</string><string name="start_capture">开始录制</string><string name="stop_capture">结束录制</string><string name="camerax_tip">请开启相应权限,否则无法拍摄视频!</string>

activity_camera_x.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="match_parent"><androidx.camera.view.PreviewViewandroid:id="@+id/viewFinder"android:layout_width="match_parent"android:layout_height="match_parent" /><Buttonandroid:id="@+id/image_capture_button"android:layout_width="110dp"android:layout_height="110dp"android:layout_marginBottom="50dp"android:layout_marginEnd="50dp"android:elevation="2dp"android:text="@string/take_photo"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintEnd_toStartOf="@id/vertical_centerline" /><Buttonandroid:id="@+id/video_capture_button"android:layout_width="110dp"android:layout_height="110dp"android:layout_marginBottom="50dp"android:layout_marginStart="50dp"android:elevation="2dp"android:text="@string/start_capture"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintStart_toEndOf="@id/vertical_centerline" /><androidx.constraintlayout.widget.Guidelineandroid:id="@+id/vertical_centerline"android:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="vertical"app:layout_constraintGuide_percent=".50" /></androidx.constraintlayout.widget.ConstraintLayout>
public class CameraXActivity extends BaseVBActivity<ActivityCameraXBinding> {private VideoCapture<Recorder> videoCapture;private ImageCapture imageCapture;private Recording recording;@Overrideprotected void initView() {startCamera();}/*** 开启摄像头*/public void startCamera() {//创建 ProcessCameraProvider 的实例。这用于将相机的生命周期绑定到生命周期所有者。这消除了打开和关闭相机的任务,因为 CameraX 具有生命周期感知能力。ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(mContext);//向 cameraProviderFuture 添加监听器。添加 Runnable 作为一个参数。添加 ContextCompat.getMainExecutor() 作为第二个参数。这将返回一个在主线程上运行的 Executor。cameraProviderFuture.addListener(new Runnable() {@Overridepublic void run() {// 在 Runnable 中,添加 ProcessCameraProvider。它用于将相机的生命周期绑定到应用进程中的 LifecycleOwnertry {ProcessCameraProvider cameraProvider = cameraProviderFuture.get();// 初始化 Preview 对象,在其上调用 build,从取景器中获取 Surface 提供程序,然后在预览上进行设置。Preview preview = new Preview.Builder().build();preview.setSurfaceProvider(mViewBinding.viewFinder.getSurfaceProvider());//创建 ImageCapture 用例imageCapture = new ImageCapture.Builder().build();//创建 VideoCapture 用例Recorder recorder = new Recorder.Builder().setQualitySelector(QualitySelector.from(Quality.HIGHEST,FallbackStrategy.higherQualityOrLowerThan(Quality.SD))).build();videoCapture = VideoCapture.withOutput(recorder);// 创建 CameraSelector 对象,然后选择 DEFAULT_BACK_CAMERA(后置摄像头)CameraSelector cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA;try {//创建一个 try 代码块。在此块内,确保没有任何内容绑定到 cameraProvidercameraProvider.unbindAll();// 然后将 cameraSelector 和预览对象绑定到 cameraProvidercameraProvider.bindToLifecycle(CameraXActivity.this, cameraSelector, preview, imageCapture, videoCapture);} catch (Exception e) {LogUtils.e("Use case binding failed", e.getMessage());}} catch (Exception e) {LogUtils.e(e.getMessage());}}}, ContextCompat.getMainExecutor(mContext));}/*** 设置点击事件*/@Overrideprotected void setOnClick() {mViewBinding.imageCaptureButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {ToastUtils.showShort("拍照");takePhoto();}});mViewBinding.videoCaptureButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {ToastUtils.showShort("录制视频");captureVideo();}});}/*** 拍照*/private void takePhoto() {// 首先,获取对 ImageCapture 用例的引用。如果用例为 null,请退出函数。如果在设置图片拍摄之前点按“photo”按钮,它将为 null。如果没有 return 语句,应用会在该用例为 null 时崩溃。if (imageCapture == null) {return;}//接下来,创建用于保存图片的 MediaStore 内容值。请使用时间戳,确保 MediaStore 中的显示名是唯一的。String name = TimeUtil.getCurrentTimeName();ContentValues contentValues = new ContentValues();contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, name);contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/CameraX-Image");}// 创建一个 OutputFileOptions 对象。在该对象中,您可以指定所需的输出内容。我们希望将输出保存在 MediaStore 中,以便其他应用可以显示它,因此,请添加我们的 MediaStore 条目。ImageCapture.OutputFileOptions outputOptions = new ImageCapture.OutputFileOptions.Builder(getContentResolver(),MediaStore.Images.Media.EXTERNAL_CONTENT_URI,contentValues).build();// 对 imageCapture 对象调用 takePicture()。传入 outputOptions、执行器和保存图片时使用的回调。imageCapture.takePicture(outputOptions,ContextCompat.getMainExecutor(this),new ImageCapture.OnImageSavedCallback() {@Overridepublic void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {//照片拍摄成功!将照片保存到我们之前创建的文件中,显示消息框,让用户知道照片已拍摄成功,并输出日志语句。String msg = "Photo capture succeeded: " + outputFileResults.getSavedUri();ToastUtils.showShort(name + "\n" + msg);LogUtils.i(msg);}@Overridepublic void onError(@NonNull ImageCaptureException exception) {//如果图片拍摄失败或保存图片失败,请添加错误情况以记录失败。LogUtils.e("Photo capture failed:" + exception.getMessage(), exception);}});}/*** 录制视频*/private void captureVideo() {//检查是否已创建 VideoCapture 用例:如果尚未创建,则不执行任何操作。if (videoCapture == null) {return;}// 在 CameraX 完成请求操作之前,停用按钮;在后续步骤中,它会在我们的已注册的 VideoRecordListener 内重新启用。mViewBinding.videoCaptureButton.setEnabled(false);//如果有正在进行的录制操作,请将其停止并释放当前的 recording。当所捕获的视频文件可供我们的应用使用时,我们会收到通知。Recording curRecording = recording;if (curRecording != null) {curRecording.stop();recording = null;return;}//为了开始录制,我们会创建一个新的录制会话。首先,我们创建预定的 MediaStore 视频内容对象,将系统时间戳作为显示名(以便我们可以捕获多个视频)。String name = TimeUtil.getCurrentTimeName();ContentValues contentValues = new ContentValues();contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, name);contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4");//使用外部内容选项创建 MediaStoreOutputOptions.Builder。MediaStoreOutputOptions mediaStoreOutputOptions = new MediaStoreOutputOptions.Builder(getContentResolver(), MediaStore.Video.Media.EXTERNAL_CONTENT_URI).setContentValues(contentValues).build();//检测录音权限if (ActivityCompat.checkSelfPermission(this,Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {return;}//将输出选项配置为 VideoCapture<Recorder> 的 Recorder 并启用录音:recording = videoCapture.getOutput().prepareRecording(mContext, mediaStoreOutputOptions).withAudioEnabled().start(ContextCompat.getMainExecutor(this),new Consumer<VideoRecordEvent>() {@Overridepublic void accept(VideoRecordEvent videoRecordEvent) {if (videoRecordEvent instanceof VideoRecordEvent.Start) {//当相机设备开始请求录制时mViewBinding.videoCaptureButton.setText(R.string.stop_capture);mViewBinding.videoCaptureButton.setEnabled(true);} else if (videoRecordEvent instanceof VideoRecordEvent.Finalize) {//完成录制后,用消息框通知用户,并将“Stop Capture”按钮切换回“Start Capture”,然后重新启用它:if (!((VideoRecordEvent.Finalize) videoRecordEvent).hasError()) {String msg = "Video capture succeeded: " + ((VideoRecordEvent.Finalize) videoRecordEvent).getOutputResults().getOutputUri();ToastUtils.showShort(msg);LogUtils.e(msg);} else {if (recording != null) {recording.close();recording = null;LogUtils.e("Video capture ends with error: " + ((VideoRecordEvent.Finalize) videoRecordEvent).getError());}}mViewBinding.videoCaptureButton.setText(R.string.start_capture);mViewBinding.videoCaptureButton.setEnabled(true);}}});}
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

面向对象

一、Scala包 1、基本语法 package 包名 2、Scala 包的三大作用&#xff08;和 Java 一样&#xff09; &#xff08;1&#xff09;区分相同名字的类 &#xff08;2&#xff09;当类很多时&#xff0c;可以很好的管理类 &#xff08;3&#xff09;控制访问范围 二、包说明 1、说明…

MySQL多表联查

一、数据库表结构 假设我们有两个表&#xff1a;users 和 orders&#xff0c;users 表存储用户信息&#xff0c;orders 表存储订单信息&#xff0c;一个用户可以有多个订单&#xff0c;通过 user_id 关联两个表。 users 表 字段名数据类型描述user_idINT用户 ID&#xff0c;…

如何迁移AxureCloud到新服务器?

前言 常有朋友遇到在更换服务器时或者本地AxureCloud迁移到服务器上时&#xff0c;如何正确迁移AxureCloud&#xff0c;让原有的原型可以正常访问呢&#xff1f; 事前准备 Navicat&#xff1a;用于迁移数据库。 AxureCloud&#xff1a;和原安装版本一致。 MySQL&#xff1…

CSGO 皮肤交易平台后端 (Spring Boot) 代码结构与示例

csgo-market/ ├── pom.xml (or build.gradle) └── src/└── main/├── java/│ └── com/│ └── yourcompany/│ └── csgomarket/│ ├── CsgomarketApplication.java # Spring Boot 启动类│ ├── conf…

mac Python多版本第三方库的安装路径

终端查看python版本是 3.12&#xff0c;但是pycharm使用的python版本是 3.9 终端正常安装包以后&#xff0c;pycharm都可以正常使用&#xff0c;但是将 pycharm的python换成 3.12 版本&#xff0c;之前安装的库都没有了 通过终端查看安装库的位置&#xff0c;确实是安装到py…

Java常用异步方式总结

使用建议 完整代码见https://gitee.com/pinetree-cpu/parent-demon 提供了postMan调试json文件于security-demo/src/main/resources/test_file/java-async.postman_collection.json 可导入postMan中进行调试 Java异步方式以及使用场景 继承Thread类 新建三个类继承Thread&…

【VUE3】Pinia

目录 0前言 1 手动添加Pinia 2 创建与使用仓库&#xff08;Setup Store 组合式&#xff09; 2.1 创建仓库 2.2 使用仓库数据 2.3 解构响应式数据 3 持久化插件 0前言 官网&#xff1a;Pinia | The intuitive store for Vue.js 1 手动添加Pinia 上手之后&#xff0c;可…

JVM 每个区域分别存储什么数据?

JVM&#xff08;Java Virtual Machine&#xff09;的运行时数据区&#xff08;Runtime Data Areas&#xff09;被划分为几个不同的区域&#xff0c;每个区域都有其特定的用途和存储的数据类型。以下是 JVM 各个区域存储数据的详细说明&#xff1a; 1. 程序计数器 (Program Cou…

C++中shared_ptr 是线程安全的吗?

在 C 中&#xff0c;shared_ptr 的线程安全性和实现原理可以通过以下方式通俗理解&#xff1a; 1. shared_ptr 是线程安全的吗&#xff1f; 答案&#xff1a;部分安全&#xff0c;需分场景&#xff01; 安全的操作&#xff1a; 引用计数的增减&#xff1a;多个线程同时复制或销…

什么是 CSSD?

文章目录 一、什么是 CSSD&#xff1f;CSSD 的职责 二、CSSD 是如何工作的&#xff1f;三、CSSD 为什么会重启节点&#xff1f;情况一&#xff1a;网络和存储都断联&#xff08;失联&#xff09;情况二&#xff1a;收到其他节点对自己的踢出通知&#xff08;外部 fencing&#…

arm64平台下linux访问寄存器

通用寄存器 示例&#xff1a;读取寄存器值 // 用户态程序或内核代码中均可使用 unsigned long reg_value; asm volatile ("mov %0, x10" // 将X10的值保存到reg_value变量: "r" (reg_value) ); printk("X10 0x%lx\n", reg_value);示例&…

超级好用的小软件,连接电脑和手机。

将手机变成电脑摄像头的高效工具Iriun Webcam是一款多平台软件&#xff0c;能够将手机摄像头变成电脑的摄像头&#xff0c;通过简单的设置即可实现视频会议、直播、录制等功能。它支持Windows、Mac和Linux系统&#xff0c;同时兼容iOS和Android手机&#xff0c;操作简单&#x…

Mysql MIC高可用集群搭建

1、介绍 MySQL InnoDB Cluster&#xff08;MIC&#xff09;是基于 MySQL Group Replication&#xff08;MGR&#xff09;的高可用性解决方案&#xff0c;结合 MySQL Shell 和 MySQL Router&#xff0c;提供自动故障转移和读写分离功能&#xff0c;非常适合生产环境 2、部署 …

PERL开发环境搭建>>Windows,Linux,Mac OS

特点 简单 快速 perl解释器直接对源代码程序解释执行,是一个解释性的语言, 不需要编译器和链接器来运行代码>>速度快 灵活 借鉴了C/C, Basic, Pascal, awk, sed等多种语言, 定位于实用性语言,既具备了脚本语言的所有功能,也添加了高级语言功能 开源.免费 没有&qu…

ubuntu改用户权限

在 Linux 系统中&#xff0c;赋予普通用户 sudo 权限可以让他们执行一些需要 root 权限的命令&#xff0c;而不需要频繁切换到 root 用户。以下是具体步骤&#xff1a; 创建用户(useradd和adduser两种方式) 首先&#xff0c;需要创建一个新的用户。可以使用 adduser 或 usera…

蓝桥杯 web 学海无涯(axios、ecahrts)版本二

答案&#xff1a; // TODO: 待补充代码// 初始化图表的数据&#xff0c;设置周视图的初始数据 option.series[0].data [180, 274, 253, 324, 277, 240, 332, 378, 101]; // 周数据&#xff08;每周的总学习时长&#xff09; option.xAxis.data ["2月第1周", "…

Java 大视界 -- Java 大数据在智慧文旅虚拟场景构建与沉浸式体验增强中的技术支撑(168)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

API vs 网页抓取:获取数据的最佳方式

引言 在当今数字化时代&#xff0c;对于企业、研究人员以及开发人员而言&#xff0c;获取准确且及时的数据是大多数项目成功的关键因素。目前&#xff0c;收集网页数据主要有两种常用方法&#xff0c;即使用 API&#xff08;应用程序接口&#xff09;和网页抓取。然而&#xf…

车载以太网网络测试-25【SOME/IP-报文格式-1】

目录 1 摘要2 SOME/IP-报文格式2.1 **Service ID / 16 bits**2.2 **Method ID / Event ID / 16 bits**2.3 **Length / 32 bits**2.4 **Client ID / 16 bits**2.5 Session ID / 16 bits2.6 Protocol Version / 8 bits2.7 Interface Version / 8 bits2.8 Message Type / 8 bits2.…

Python数据可视化-第3章-图表辅助元素的定制

环境 开发工具 VSCode库的版本 numpy1.26.4 matplotlib3.10.1 ipympl0.9.7教材 本书为《Python数据可视化》一书的配套内容&#xff0c;本章为第3章-图表辅助元素的定制 本章主要介绍了图表辅助元素的定制&#xff0c;包括认识常用的辅助元素、设置坐标轴的标签、设置刻度范…