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

Unity 之 【Android Unity 共享纹理】之 Android 共享图片给 Unity 显示

Unity 之  【Android Unity 共享纹理】之 Android 共享图片给 Unity 显示

目录

Unity 之  【Android Unity 共享纹理】之 Android 共享图片给 Unity 显示

一、简单介绍

二、共享纹理

1、共享纹理的原理

2、共享纹理涉及到的关键知识点

3、什么可以实现共享

不能实现共享的情况

4、共享纹理中的注意事项

三、注意事项

四、效果预览

五、简单实现 Android 共享图片给 Unity 显示 案例

1、案例环境

2、Android 端

3、Unity 端

六、关键代码


一、简单介绍

Unity 是一个功能强大的跨平台游戏引擎,广泛用于开发视频游戏和其他实时3D互动内容,如模拟器和虚拟现实应用。

游戏引擎:

  • Unity:Unity Technologies 开发的跨平台游戏引擎,支持2D和3D图形、物理引擎、音频、视频、网络和多平台发布。
  •  跨平台支持:Unity 支持在多个平台上发布,包括 Windows、macOS、Linux、iOS、Android、WebGL、PlayStation、Xbox、Switch 等。

开发环境:

  • Unity Editor:用于创建和管理 Unity 项目的集成开发环境(IDE)。开发者可以在其中创建场景、设计关卡、编写代码和调试游戏。
  • 场景(Scene):Unity 中的基本构建块,一个场景可以被视为一个关卡或一个游戏中的独立部分。

编程语言:

  • C#:主要编程语言。Unity 使用 C# 脚本来控制游戏对象和实现游戏逻辑。
  • UnityScript(已弃用):曾经支持的 JavaScript 变种,但已经被弃用。

Unity 进阶开发涉及更复杂的技术和更深入的知识,以创建高性能、复杂和专业的游戏和应用程序。以下是一些 Unity 进阶开发的关键领域和技术:

设计模式:

  • 学习和应用常见的设计模式(如单例模式、工厂模式、观察者模式)以便更好地组织和管理代码。

异步编程:

  • 使用 C# 的 async 和 await 关键字进行异步编程,以提高应用的响应速度和性能。

依赖注入:

  • 使用依赖注入(如 Zenject)来管理对象的依赖关系,减少耦合,提高代码的可测试性和可维护性。

二、共享纹理

共享纹理指的是在不同的图形处理环境(例如 Unity 和 Android 原生代码)之间共享图像数据,而不需要进行图像数据的复制。这样可以大大提升性能,减少内存使用,因为避免了重复加载和处理同一图像数据的开销。

1、共享纹理的原理

共享纹理的基本原理是通过创建一个共享的图形上下文(context),并在不同的渲染环境中使用同一个纹理对象。主要涉及以下几个步骤:

  1. 创建共享的EGL上下文

    • EGL(Embedded-System Graphics Library)用于管理图形上下文和绘图表面。通过EGL,可以创建一个共享的上下文,使得不同的线程可以访问同一个纹理。
  2. 生成OpenGL纹理

    • 在共享的EGL上下文中生成一个OpenGL纹理对象。
  3. 在Unity中使用该纹理

    • 在Unity中通过Texture2D.CreateExternalTexture方法创建一个引用共享纹理的Texture2D对象,从而在Unity的渲染环境中使用该纹理。

2、共享纹理涉及到的关键知识点

  1. EGLContext

    • EGLContext是EGL用于管理OpenGL ES图形上下文的对象。通过创建共享的EGLContext,可以在不同的线程之间共享OpenGL资源。
  2. EGLSurface

    • EGLSurface是EGL用于表示绘图表面的对象。可以是窗口表面、Pbuffer(像素缓冲区)表面或屏幕外渲染表面。
  3. OpenGL ES

    • OpenGL ES是OpenGL的一个子集,专门用于嵌入式系统(如移动设备)。在共享纹理的过程中,OpenGL ES提供了创建和操作纹理的API。
  4. Texture2D.CreateExternalTexture

    • Unity中的方法,用于创建一个引用外部纹理的Texture2D对象。这是将共享的OpenGL纹理引入Unity渲染环境的关键步骤。

3、什么可以实现共享

  • OpenGL纹理:可以通过共享的EGLContext在不同的线程或进程中共享。
  • Pbuffer Surface:可以用于共享离屏渲染的纹理。
不能实现共享的情况
  • 不同设备之间:共享纹理通常仅限于同一设备上的不同进程或线程之间。
  • 非OpenGL资源:非OpenGL的图形资源,如CPU内存中的图像数据,不能直接通过EGL共享。

4、共享纹理中的注意事项

  1. 同步问题

    • 在多线程环境中使用共享纹理时,需要注意同步问题,避免不同线程同时对同一纹理进行读写操作,导致数据竞争或一致性问题。
  2. 上下文管理

    • 确保正确管理EGLContext和EGLSurface的生命周期。在创建和销毁上下文时,需要注意资源的正确释放。
  3. 性能优化

    • 共享纹理可以减少数据复制,提高性能,但仍需注意渲染流程中的其他瓶颈,进行整体的性能优化。
  4. 兼容性

    • 不同设备和操作系统版本可能对EGL和OpenGL ES的支持有所不同。在实现共享纹理时,需要考虑设备兼容性问题,确保在目标设备上正常运行。

三、注意事项

1、一定要注意 Unity 端设置的 多线程渲染 关闭掉,以及 移除 Vulkan 配置,不然会报错,创建共享失败,共享id 会是 0

2、共享图片的可能会翻转,注意合理调整

四、效果预览

五、简单实现 Android 共享图片给 Unity 显示 案例

1、案例环境

  • Windows 10
  • Android Studio Dolphin | 2021.3.1 Patch 1
  • Unity 2021.3.16f

2、Android 端

该案例中使用的OpenGL和EGL函数主要用于设置OpenGL环境,创建和管理纹理。EGL14GLES20是Android的OpenGL ES 1.4和2.0的API。这段代码是Android原生代码与Unity集成的一个例子,允许Unity访问和操作OpenGL纹理。

该类封装了OpenGL ES 2.0的初始化和纹理管理,其主要包括下面参数与函数:

  • 1、成员变量:

    • mSharedEglContext: 共享的EGL上下文,从Unity线程获取。
    • mSharedEglConfig: 共享的EGL配置。
    • mTextureID: OpenGL纹理ID。
    • mTextureWidthmTextureHeight: 纹理的宽度和高度。
    • mRenderThread: 一个执行服务,用于在单独的线程上执行渲染任务。
  • 2、构造函数 (public NativeAndroidApp()): 初始化一个单线程的执行服务,用于执行OpenGL的初始化和渲染任务。

  • 3、setupOpenGL: 这个方法被Unity调用,用于设置OpenGL环境。它执行以下步骤:

    • 获取当前Unity线程的EGL上下文和显示。
    • 获取Unity绘制线程的EGL配置。
    • 在渲染线程上初始化OpenGL环境,并生成OpenGL纹理ID。
  • 4、initOpenGL: 私有方法,用于初始化OpenGL环境。它包括:

    • 获取默认的EGL显示。
    • 初始化EGL,获取版本信息。
    • 创建EGL上下文,指定OpenGL ES 2.0。
    • 创建一个像素缓冲区表面(PbufferSurface)。
    • 将EGL上下文和表面设置为当前。
  • 5、getStreamTextureID, getStreamTextureWidth, getStreamTextureHeight: 这些公共方法被Unity调用,用于获取纹理ID和尺寸信息。

  • 6、updateTexture: 这个方法被Unity调用以更新纹理内容。虽然这个方法的实现在代码片段中没有给出,但它可能涉及将新的图像数据绑定到OpenGL纹理,并使用glTexImage2D等函数更新纹理。

1)打开 Android  Studio ,创建一个 Android Library

2) 然后,添加一张测试图片,来作为共享

3)然后创建建一个脚本,编写对应函数实现共享纹理图片的功能

4)接着 ‘Build-Make Module 'xxxxxx'’ 打包成 aar

3、Unity 端

实现Unity与Android原生代码之间的纹理共享。它通过调用Android端的Java代码来获取和更新纹理数据,并在Unity中显示这些纹理。以下是脚本的主要功能和关键函数的说明,脚本通过与Android原生层交互,实现了Unity与Android之间的纹理共享:

  • 1、成员变量:

    • mGLTexCtrl: AndroidJavaObject 类型,用于调用Java层的方法。
    • mTexture2D: Texture2D 类型,Unity中的纹理对象,用于在Unity中显示纹理。
    • mTextureId: 存储从Java层获取的纹理ID,用于创建外部纹理。
    • mWidthmHeight: 存储纹理的宽度和高度。
  • 2、Awake函数: 在Unity对象初始化时调用,用于实例化AndroidJavaObject并调用Java层的setupOpenGL方法来初始化OpenGL环境。

  • 3、Start函数: 在Unity对象启动时调用,用于绑定纹理到UI组件RawImage

  • 4、BindTexture函数:

    • 调用Java层的方法获取纹理ID、宽度和高度。
    • 使用获取到的纹理ID创建Unity的Texture2D对象,格式为RGBA32,不具有Mipmaps,不使用Readable设置。
    • 将创建的纹理赋值给RawImage组件的texture属性,以便在UI上显示。
  • 5、Update函数: 每帧调用,用于调用UpddateTextureate方法更新纹理数据。

  • 6、UpddateTextureate函数:

    • 调用Java层的updateTexture方法来更新纹理数据。
    • 这个方法应该在每帧或者在特定条件下调用,以确保纹理内容是最新的。
  • 7、OnDestroy函数: 当Unity对象被销毁时调用,用于释放纹理资源,避免内存泄漏。

1)创建一个 Unity 工程,创建文件夹 Plugins-Android ,把之前的生成的 aar 添加到文件夹中

2)在场景中添加一个 RawImage ,用来显示共享的纹理图片

3)创建一个脚本,调用 Android 端封装暴露的纹理共享接口

4)把创建的脚本挂载到场景中,对应赋值 RawImage

5)把平台切换到 Android ,对应关闭 设置中的多线程渲染,以及 移除 Vulkan

6、打包,在设备上运行,效果如下

7)可能你会发现图片反了,你可以Android端修改,也可以 Unity端修改,这里不在赘述,最后调整效果如下

六、关键代码

1、TextureShareDemo.java

package com.ffalcon.unitytexturesharemodule;import static android.opengl.GLES20.glGetError;import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.EGL14;
import android.opengl.EGLConfig;
import android.opengl.EGLContext;
import android.opengl.EGLDisplay;
import android.opengl.EGLSurface;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import android.util.Log;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** 共享纹理 Demo*/
public class TextureShareDemo {/*** 用于日志输出的标签*/private static final String TAG = "[TextureShareDemo] ";/*** 共享的EGL上下文,从Unity线程获取*/private EGLContext mSharedEglContext;/*** 共享的EGL配置*/private EGLConfig mSharedEglConfig;/*** OpenGL纹理ID*/private int mTextureID;/*** 纹理宽度*/private int mTextureWidth;/*** 纹理高度*/private int mTextureHeight;/*** 用于OpenGL渲染操作的线程池*/private ExecutorService mRenderThread;/*** 构造函数,初始化线程池*/public TextureShareDemo() {mRenderThread = Executors.newSingleThreadExecutor();}/*** 由Unity调用,用于设置OpenGL环境* 此方法在Unity线程执行*/public void setupOpenGL() {Log.d(TAG, "setupOpenGL called by Unity");// 获取Unity线程当前的EGL上下文和显示mSharedEglContext = EGL14.eglGetCurrentContext();EGLDisplay sharedEglDisplay = EGL14.eglGetCurrentDisplay();// 获取Unity绘制线程的EGL配置int[] numEglConfigs = new int[1];EGLConfig[] eglConfigs = new EGLConfig[1];if (!EGL14.eglGetConfigs(sharedEglDisplay, eglConfigs, 0, eglConfigs.length, numEglConfigs, 0)) {Log.e(TAG, "eglGetConfigs failed");return;}mSharedEglConfig = eglConfigs[0];// 在渲染线程上执行OpenGL初始化和纹理创建mRenderThread.execute(new Runnable() {@Overridepublic void run() {initOpenGL();// 生成OpenGL纹理IDint textures[] = new int[1];GLES20.glGenTextures(1, textures, 0);if (textures[0] == 0) {Log.e(TAG, "glGenTextures failed");return;}mTextureID = textures[0];// 这里设置的纹理尺寸是示例值mTextureWidth = 670;mTextureHeight = 670;}});}/*** 初始化OpenGL环境*/private void initOpenGL() {Log.d(TAG, "initOpenGL: ");EGLDisplay mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);int[] version = new int[2];if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) {Log.e(TAG, "Failed to initialize EGL.");return;}// 创建EGL上下文,指定OpenGL ES 3版本int[] eglContextAttribList = new int[]{EGL14.EGL_CONTEXT_CLIENT_VERSION, 3,EGL14.EGL_NONE};EGLContext mEglContext = EGL14.eglCreateContext(mEGLDisplay, mSharedEglConfig, mSharedEglContext, eglContextAttribList, 0);if (mEglContext == EGL14.EGL_NO_CONTEXT) {Log.e(TAG, "Failed to create EGL context.");return;}// 创建像素缓冲区表面int[] surfaceAttribList = {EGL14.EGL_WIDTH, 64,EGL14.EGL_HEIGHT, 64,EGL14.EGL_NONE};EGLSurface mEglSurface = EGL14.eglCreatePbufferSurface(mEGLDisplay, mSharedEglConfig, surfaceAttribList, 0);if (mEglSurface == EGL14.EGL_NO_SURFACE) {Log.e(TAG, "Failed to create EGL surface.");return;}// 设置EGL上下文和表面为当前if (!EGL14.eglMakeCurrent(mEGLDisplay, mEglSurface, mEglSurface, mEglContext)) {Log.e(TAG, "Failed to make EGL context current.");return;}GLES20.glFlush();// 检查OpenGL是否有错误发生int error = glGetError();if (error != GLES20.GL_NO_ERROR) {Log.e(TAG, "OpenGL error: " + error);}}/*** 获取纹理ID* @return 纹理ID*/public int getStreamTextureID() {Log.d(TAG, "getStreamTextureID: mTextureID = " + mTextureID);return mTextureID;}/*** 获取纹理宽度* @return 纹理宽度*/public int getStreamTextureWidth() {Log.d(TAG, "getStreamTextureWidth: mTextureWidth = " + mTextureWidth);return mTextureWidth;}/*** 获取纹理高度* @return 纹理高度*/public int getStreamTextureHeight() {Log.d(TAG, "getStreamTextureHeight: mTextureHeight = " + mTextureHeight);return mTextureHeight;}/*** 更新纹理内容* 此方法应在渲染线程中调用*/public void updateTexture() {mRenderThread.execute(new Runnable() {@Overridepublic void run() {// 从资源中加载位图final Bitmap bitmap = BitmapFactory.decodeResource(getUnityContext().getResources(), R.drawable.wutiaowu);if (bitmap != null) {// 绑定纹理IDGLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureID);// 设置纹理参数GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);// 将位图数据上传到纹理GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);// 解绑纹理GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);// 回收位图占用的内存bitmap.recycle();} else {// 如果位图加载失败,记录错误日志android.util.Log.e(TAG, "Failed to load bitmap.");}}});}// region 获取Unity的Activity和Context/*** 设置一个 Activity 参数*/protected static Activity unityActivity;/*** 通过反射获取Unity的Activity* @return Unity的Activity*/protected static Activity getActivity() {// 反射获取当前Activity,如果尚未获取则尝试通过UnityPlayer类获取if (null == unityActivity) {try {Class<?> classtype = Class.forName("com.unity3d.player.UnityPlayer");unityActivity = (Activity) classtype.getDeclaredField("currentActivity").get(classtype);} catch (ClassNotFoundException | IllegalAccessException | NoSuchFieldException e) {e.printStackTrace();}}return unityActivity;}/*** 获取Unity的上下文Context* @return Unity的上下文Context*/protected static Context getUnityContext(){return getActivity().getApplicationContext();}//endregion}

2、TextureShareDemo.cs

using System;
using UnityEngine;
using UnityEngine.UI;/// <summary>
/// Android Unity 共享纹理 demo
/// </summary>
public class TextureShareDemo : MonoBehaviour
{// 用于日志输出的标签const string TAG = "[UnityNativeTexture] ";// AndroidJavaObject对象,用于与Java代码交互private AndroidJavaObject mGLTexCtrl;// Unity中的Texture2D对象,用于显示纹理private Texture2D mTexture2D;// 纹理ID,用于创建外部纹理private int mTextureId = 0;// 纹理的宽度private int mWidth;// 纹理的高度private int mHeight;// Unity UI组件RawImage,用于显示纹理public RawImage RawImage;/// <summary>/// 在Unity初始化时调用/// </summary>void Awake(){// 创建AndroidJavaObject实例,与Java层的TextureShareDemo类交互// 这里的包名和类名应与实际Java类名一致mGLTexCtrl = new AndroidJavaObject("com.ffalcon.unitytexturesharemodule.TextureShareDemo");// 调用Java层的方法以初始化OpenGL上下文mGLTexCtrl.Call("setupOpenGL");}/// <summary>/// 在Unity开始时调用/// </summary>void Start(){// 绑定纹理到RawImage组件BindTexture();}/// <summary>/// 绑定纹理的方法/// </summary>void BindTexture(){Debug.Log(TAG + "BindTexture : ");// 从Java层获取纹理ID、宽度和高度mTextureId = mGLTexCtrl.Call<int>("getStreamTextureID");mWidth = mGLTexCtrl.Call<int>("getStreamTextureWidth");mHeight = mGLTexCtrl.Call<int>("getStreamTextureHeight");// 检查纹理ID是否有效if (mTextureId != 0){Debug.Log(TAG + "BindTexture : mTextureId = " + mTextureId);// 使用获取到的纹理ID创建外部纹理mTexture2D = Texture2D.CreateExternalTexture(mWidth, mHeight, TextureFormat.RGBA32, false, false, (IntPtr)mTextureId);// 将创建的纹理赋值给RawImage组件的texture属性RawImage.texture = mTexture2D;}else{// 如果纹理ID无效,记录错误日志Debug.LogError(TAG + "BindTexture : Failed to get valid texture ID from Android.");}}/// <summary>/// 每帧调用的Update方法/// </summary>private void Update(){// 调用UpdateTextureate方法来更新纹理数据UpddateTextureate();}/// <summary>/// 更新纹理数据的方法/// </summary>void UpddateTextureate(){// 这里应该是一个拼写错误,正确的方法名应该是UpdateTexture// 调用Java层的方法来更新纹理数据if (mGLTexCtrl != null && mTextureId != 0){Debug.Log(TAG + "Update : mGLTexCtrl.Call(\"updateTexture\")  mTextureId = " + mTextureId);mGLTexCtrl.Call("updateTexture");}}/// <summary>/// 当脚本对象被销毁时调用/// </summary>void OnDestroy(){// 确保在销毁时释放纹理资源if (mTexture2D != null){// 释放纹理资源mTexture2D = null;}}
}

参考文献:Unity安卓共享纹理

相关文章:

Unity 之 【Android Unity 共享纹理】之 Android 共享图片给 Unity 显示

Unity 之 【Android Unity 共享纹理】之 Android 共享图片给 Unity 显示 目录 Unity 之 【Android Unity 共享纹理】之 Android 共享图片给 Unity 显示 一、简单介绍 二、共享纹理 1、共享纹理的原理 2、共享纹理涉及到的关键知识点 3、什么可以实现共享 不能实现共享…...

Go语言的数据结构

数据结构 数组 支持多维数组&#xff0c;属于值类型&#xff0c;支持range遍历 例子&#xff1a;随机生成长度为10整数数组 package main import ("fmt""math/rand" ) // 赋值 随机获取100以内的整数 func RandomArrays() {var array [10]int //声明var…...

python_在sqlite中创建表并写入表头

python_在sqlite中创建表并写入表头 import sqlite3def write_title_to_sqlite(tableName,titleList,dataTypeGroupsList,database_path):conn sqlite3.connect(database_path)# 创建游标cursor conn.cursor()#MEMO 长文本#create_table_bodycreate_table_body "序号 …...

1.c#(winform)编程环境安装

目录 安装vs创建应用帮助查看器安装与使用&#xff08; msdn&#xff09; 安装vs 安装什么版本看个人心情&#xff0c;或者公司开发需求需要 而本栏全程使用vs2022进行开发c#&#xff0c;着重讲解winform桌面应用开发 使用***.net framework***开发 那先去官网安装企业版的vs…...

图中的最短环

2608. 图中的最短环 现有一个含 n 个顶点的 双向 图&#xff0c;每个顶点按从 0 到 n - 1 标记。图中的边由二维整数数组 edges 表示&#xff0c;其中 edges[i] [ui, vi] 表示顶点 ui 和 vi 之间存在一条边。每对顶点最多通过一条边连接&#xff0c;并且不存在与自身相连的顶…...

安装依赖 npm install idealTree:lib: sill idealTree buildDeps 卡着不动

我一直怀疑是网络问题&#xff0c;因为等了很久也能安装成功&#xff0c;就是时间比较长&#xff0c;直到现在完全受不了了&#xff0c;决定好好整治下这个问题&#xff01; 1、执行命令 npm config get userconfig 查看配置文件所在位置&#xff0c;将其删除。 2、执行 n…...

LLMs之Llama 3.1:Llama 3.1的简介、安装和使用方法、案例应用之详细攻略

LLMs之Llama 3.1&#xff1a;Llama 3.1的简介、安装和使用方法、案例应用之详细攻略 导读&#xff1a;2024年7月23日&#xff0c;Meta重磅推出Llama 3.1。本篇文章主要提到了Meta推出的Llama 3.1自然语言生成模型。 背景和痛点 >> 过去开源的大型语言模型在能力和性能上一…...

如何实现一个大模型在回答问题时同时提供相关内容链接

通义生成 为了让大模型在回答问题时能够提供相关内容链接&#xff0c;通常采用的方法是结合检索增强生成&#xff08;Retrieval-Augmented Generation, RAG&#xff09;的技术。这种方法可以让大模型在生成答案的同时&#xff0c;从外部知识源中检索相关信息&#xff0c;并将这…...

<数据集>玉米地杂草识别数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;9900张 标注数量(xml文件个数)&#xff1a;9900 标注数量(txt文件个数)&#xff1a;9900 标注类别数&#xff1a;2 标注类别名称&#xff1a;[Maize, Weed] 序号类别名称图片数框数1Maize8439125142Weed959231048…...

vue3中动态添加form表单校验

<template><div><div v-for"(formData, index) in forms" :key"index"><u-form :model"formData" :rules"rules" ref"formRefs"><u-form-item label"用户名" prop"username"…...

Java面试八股之什么是声明式事务管理,spring怎么实现声明式事务管理?

什么是声明式事务管理&#xff0c;spring怎么实现声明式事务管理&#xff1f; 声明式事务管理是一种编程范式&#xff0c;它允许开发人员通过声明性的配置或注解&#xff0c;而不是硬编码事务处理逻辑&#xff0c;来指定哪些方法或类应该在其上下文中执行事务。这种方法将事务…...

springboot 缓存预热的几种方案

缓存预热是指在 Spring Boot 项目启动时&#xff0c;预先将数据加载到缓存系统&#xff08;如 Redis&#xff09;中的一种机制。 这里我给大家总结几个缓存预热的方案。 方案1&#xff1a;使用启动监听事件实现缓存预热 可以使用 ApplicationListener 监听 ContextRefreshed…...

谷粒商城实战笔记-62-商品服务-API-品牌管理-OSS整合测试

文章目录 一&#xff0c;Java中上传文件到阿里云OSS1&#xff0c;整合阿里云OSS2&#xff0c;测试上传文件 二&#xff0c;Java中整合阿里云OSS服务指南引言准备工作1. 注册阿里云账号2. 获取Access Key3. 添加依赖 实现OSS客户端1. 初始化OSSClient2. 创建Bucket3. 上传文件4.…...

linux c 递归锁的介绍

递归锁的递归特性确实只是对于持有锁的线程。当一个线程获取了递归锁后&#xff0c;它可以多次重复获取该锁&#xff0c;而不会导致自身阻塞或死锁。这是递归锁的重要特点&#xff0c;它允许同一个线程在已经持有锁的情况下&#xff0c;再次获取相同的锁。 然而&#xff0c;对…...

React好用的组件库有哪些

React好用的组件库有很多&#xff0c;它们各自具有不同的特点和优势&#xff0c;适用于不同的开发场景和需求。以下是一些受欢迎的React组件库及其特点&#xff1a; Material-UI&#xff08;现更名为MUI&#xff09; 特点&#xff1a;这是一个开源的React组件库&#xff0c;实…...

简单快捷!Yarn的安装与使用指南

Yarn 是由 Facebook (现 Meta) 开发的包管理工具。 今天&#xff0c;我将介绍如何使用 Yarn。 目录 Yarn 的官方网站 关于安装 版本确认 开始一个新项目&#xff08;创建 package.json 文件&#xff09; 安装软件包 升级包 运行脚本 执行包的命令 卸载包 总结 Yarn 的…...

【Django】前端技术-网页样式表CSS

文章目录 一、申明规则CSS的导入方式行内样式内部样式外部样式 二、CSS的选择器1. 基本选择器标签选择器&#xff1a; 选择一类标签 标签{}类选择器 class&#xff1a; 选择所有class属性一致的表情&#xff0c;跨标签.类名{}ID选择器&#xff1a;全局唯一 #id名{} 2.层次选择器…...

openssl req 详解

一、openssl req 该命令用于创建和处理PKCS#10格式的证书请求&#xff08;certificate requests CSRs&#xff09;&#xff0c;也可以用来创建自签名证书&#xff08; self-signed certificates&#xff09;来当作根证书&#xff08;root CAs&#xff09;使用 -new 该选项用来…...

mysql各种锁总结

mysql全局锁 读锁&#xff08;共享锁&#xff09; 阻止其他用户更新&#xff0c;但允许他们读取数据。 写锁&#xff08;排他锁&#xff09; 阻止其他用户读取和更新数据。 全局锁场景&#xff1a;进行数据库备份 数据库备份 背景&#xff1a;备份数据肯定要保证数据一致…...

SpringSecurity--DelegatingFilterProxy工作流程

什么是 DelegatingFilterProxy&#xff1f; DelegatingFilterProxy 是 Spring 提供的一个特殊的过滤器&#xff0c;它起到了桥梁的作用&#xff0c;可以让你在 Spring 容器中管理 Servlet 容器中的过滤器。 为什么需要 DelegatingFilterProxy&#xff1f; 通常情况下&#x…...

GitHub每日最火火火项目(7.27)

1. 项目名称&#xff1a;meta - llama / llama3 项目介绍&#xff1a;这是 Meta Llama 3 的官方 GitHub 站点。目前尚不清楚该项目的具体功能和特点&#xff0c;但从名称推测&#xff0c;可能与 Llama 3 模型相关&#xff0c;或许涉及到模型的开发、训练或应用等方面。 项目地…...

git 学习总结

文章目录 一、 git 基础操作1、工作区2、暂存区3、本地仓库4、远程仓库 二、git 的本质三、分支git 命令总结 作者: baron 一、 git 基础操作 如图所示 git 总共有几个区域 工作区, 暂存区, 本地仓库, 远程仓库. 1、工作区 存放项目代码的地方&#xff0c;他有两种状态 Unm…...

《如何找到自己想做的事》

Arouse Enthusiasm, Give Scope to Skill, Explore The Essence *摘其两纸 我喜欢打篮球&#xff0c;并不是我真的喜欢这项运动&#xff0c;而是我喜欢团队竞技。我喜欢看书&#xff0c;并不是我真喜欢阅读&#xff0c;而是我想要了解世界运行逻辑。寻找热爱&#xff0c;探寻本…...

Vue中el的两种写法

大家好我是前端寄术区博主PleaSure乐事。今天了解到了Vue当中有关el的两种写法&#xff0c;记录下来与大家分享&#xff0c;希望对大家有所帮助。 方法一 解释 第一种方法我们直接用new创建并初始化一个新的 Vue 实例&#xff0c;并定义了 Vue 实例的数据对象&#xff0c;在给…...

ELK安装(Elasticsearch+Logstash+Kibana+Filebeat)

一、简介 1.1、软件简介 ELK其实是Elasticsearch&#xff0c;Logstash 和 Kibana三个产品的首字母缩写&#xff0c;这三款都是开源产品。 1.1.1、Elasticsearch简介 Elasticsearch 是一个分布式、高扩展、高实时的搜索与数据分析引擎。它能很方便的使大量数据具有搜索、分析…...

VScode使用Github Copilot插件时出现read ECONNREST问题的解决方法

文章目录 read ECONNREST查看是否仍是 Copilot 会员查看控制台输出网络连接问题浏览器设置问题笔者的话 read ECONNREST 最近使用 Copilot 时一直出现 read ECONNREST 问题&#xff0c;这个表示连接被对方重置了&#xff0c;就是说在读取数据时连接被关闭。 我首先怀疑是不是…...

充电桩浪涌保护方案—保障充电设施安全稳定运行的关键

在当今新能源汽车蓬勃发展的时代&#xff0c;充电桩作为电动汽车的“加油站”&#xff0c;其重要性不言而喻。然而&#xff0c;由于其复杂的电气环境和暴露于户外的特点&#xff0c;充电桩容易受到浪涌的影响。浪涌可能来自雷电、电网故障、大功率设备的启停等&#xff0c;对充…...

Python包管理工具pip

1、安装pip cmd管理员模式打开控制台 python -m pip install --upgrade pip 2、添加pip环境变量 pip 路径 C:\Users\1\AppData\Local\Programs\Python\Python312\Scripts...

最全国内13家DNS分享 解决网页被恶意跳转或无法打开问题

腾讯 DNS (DNSPod) 腾讯 DNS 是由 DNSPod 提供的公共免费 DNS 服务。DNSPod 已被腾讯收购&#xff0c;现在属于腾讯公司所有。该 DNS 服务稳定性和连通性良好&#xff0c;经测试在海外也可以使用。 DNSPod 提供了 IPv4、IPv6 DNS 和 DoT/DoH 服务。 IPv4 地址: 119.29.29.29…...

最新站长工具箱源码,拥有几百个功能,安装教程

最新站长工具箱源码&#xff0c;拥有几百个功能&#xff0c;安装教程 在 Docker 上运行 docker run -e LAFREGIONCN -e APPLANGzh_CN --name my-miaoda -v ~/.miaoda-docker:/root/.miaoda -d -p 0.0.0.0:39899:39899 codegentoolbox/laftools-linux-x64:latestNOTE: 默认端…...

【算法/训练】:动态规划(线性DP)

一、路径类 1. 字母收集 思路&#xff1a; 1、预处理 对输入的字符矩阵我们按照要求将其转换为数字分数&#xff0c;由于只能往下和往右走&#xff0c;因此走到&#xff08;i&#xff0c;j&#xff09;的位置要就是从&#xff08;i - 1&#xff0c; j&#xff09;往下走&#…...

计算巨头 Azure、AWS 和 GCP 的比较

云计算领域由三大主要参与者主导&#xff1a;Microsoft Azure、Amazon Web Services (AWS) 和 Google Cloud Platform (GCP)。每个平台都为希望利用云提供基础设施、平台服务等的企业提供强大的功能。在本文中&#xff0c;我们将深入探讨这些平台之间的差异&#xff0c;重点关注…...

Thinkphp5跨域问题常见的处理方法

在ThinkPHP5中&#xff0c;处理跨域问题通常涉及配置中间件或直接在控制器中设置响应头。以下是几种常见的解决跨域问题的方法&#xff1a; 1. 使用中间件处理跨域 你可以创建一个中间件来专门处理跨域请求。这个中间件会检查请求的来源&#xff0c;并设置相应的响应头来允许…...

Matlab编程资源库(9)数据插值与曲线拟合

一、一维数据插值 在MATLAB中&#xff0c;实现这些插值的函数是interp1&#xff0c;其调用格式为&#xff1a; Y1interp1(X,Y,X1,method) 函数根据X,Y的值&#xff0c;计算函数在X1处的值。X,Y是两个等长的已知向量&#xff0c;分别描述采样点和样本值&#xff0c;X1是一个向量…...

matplotlib的科研绘图辅助

matplotlib的科研绘图辅助 趁着暑假&#xff0c;与和鲸科技合作了一个python绘图的教程&#xff0c;作为暑期夏令营的一小部分&#xff0c;主要内容是介绍如何使用matplotlib、pandas、seaborn和plotnine进行医学科研绘图&#xff0c;感兴趣的可以通过如下地址进行访问&#x…...

C++内存管理(候捷)第五讲 笔记

GNU C对allocators的描述 new_allocator 和malloc_allocator&#xff0c;它们都没有特别的动作&#xff0c;无非底部调用operator new和malloc。它们没有用内存池 区别&#xff1a;::operator new是可重载的 智能型的allocator&#xff0c;使用内存池&#xff0c;分一大块然后…...

谷粒商城实战笔记-63-商品服务-API-品牌管理-OSS获取服务端签名

文章目录 一&#xff0c;创建第三方服务模块thrid-party1&#xff0c;创建一个名为gulimall-third-party的模块2&#xff0c;nacos上创建third-party命名空间&#xff0c;用来管理这个服务的所有配置3&#xff0c;配置pom文件4&#xff0c;配置文件5&#xff0c;单元测试6&…...

详细介绍BIO、NIO、IO多路复用(select、poll、epoll)

BIO、NIO、IO多路复用 BIO(Blocking IO)NIO(Non-blocking IO) 同步非阻塞IOIO多路复用selectpollepoll Redis的IO多路复用 BIO(Blocking IO) 最基础的IO模型&#xff0c;当进行IO操作时&#xff0c;线程会被阻塞&#xff0c;直到操作完成。 比如read和write&#xff0c;通常IO…...

昇思25天学习打卡营第11天|xiaoyushao

今天分享ResNet50迁移学习。 在实际应用场景中&#xff0c;由于训练数据集不足&#xff0c;所以很少有人会从头开始训练整个网络。普遍的做法是&#xff0c;在一个非常大的基础数据集上训练得到一个预训练模型&#xff0c;然后使用该模型来初始化网络的权重参数或作为固定特征提…...

为什么样本方差(sample variance)的分母是 n-1?

样本均值与样本方差的定义 首先来看一下均值&#xff0c;方差&#xff0c;样本均值与样本方差的定义 总体均值的定义&#xff1a; μ 1 n ∑ i 1 n X i \mu\frac{1}{n}\sum_{i1}^{n} X_i μn1​i1∑n​Xi​ 也就是将总体中所有的样本值加总除以个数&#xff0c;也可以叫做总…...

编解码器架构

一、定义 0、机器翻译是序列转换模型的一个核心问题&#xff0c; 其输入和输出都是长度可变的序列。 为了处理这种类型的输入和输出&#xff0c; 我们设计一个包含两个主要组件的架构&#xff1a; 第一个组件是一个编码器&#xff08;encoder&#xff09;&#xff1a; 它接受一…...

追问试面试系列:JVM运行时数据区

hi 欢迎来到追问试面试系列之JVM运行时数据区,在面试中出现频率非常高,并且其中还存在一些误导性的面试,一定要注意。 什么误导性呢?面试中,有的面试官本来是想问JVM运行时数据区,不过提问时难免有些让你觉得很不爽。比如:你说说java内存模型,还比如说说JVM内存模型,…...

React Native在移动端落地实践

在移动互联网产品迅猛发展的今天&#xff0c;技术的不断创新使得企业越来越注重降低成本、提升效率。为了在有限的开发资源下迅速推出高质量、用户体验好的产品&#xff0c;以实现公司发展&#xff0c;业界催生了许多移动端跨平台解决方案。这些方案不仅简化了开发流程&#xf…...

《操作系统》(学习笔记)(王道)

一、计算机系统概述 1.1 操作系统的基本概念 1.1.1 操作系统的概念 1.1.2 操作系统的特征 1.1.3 操作系统的目标和功能 1.2 操作系统的发展与分类 1.2.1 手工操作阶段&#xff08;此阶段无操作系统&#xff09; 1.2.2 批处理阶段&#xff08;操作系统开始出现&#xff0…...

LabVIEW学习-LabVIEW处理带分隔符的字符串从而获取数据

带分隔符的字符串很好处理&#xff0c;只需要使用"分隔符字符串至一维字符串数组"函数或者"一维字符串数组至分隔符字符串"函数就可以很轻松地处理带分隔符地字符串。 这两个函数所在的位置为&#xff1a; 函数选板->字符串->附加字符串函数->分…...

freesql简单使用操作mysql数据库

参考&#xff1a;freesql中文官网指南 | FreeSql 官方文档 这两天准备做一个测试程序&#xff0c;往一个系统的数据表插入一批模拟设备数据&#xff0c;然后还要模拟设备终端发送数据包&#xff0c;看看系统的承压能力。 因为系统使用的第三方框架中用到了freesql&#xff0c…...

使用Java和Spring Retry实现重试机制

使用Java和Spring Retry实现重试机制 大家好&#xff0c;我是微赚淘客系统3.0的小编&#xff0c;是个冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;今天&#xff0c;我们将探讨如何在Java中使用Spring Retry来实现重试机制。重试机制在处理临时性故障和提高系统稳…...

Linux Vim教程(十):自定义配置与插件管理

目录 1. 概述 2. Vim 配置文件 2.1 .vimrc 文件 2.2 .gvimrc 文件 3. 自定义配置 3.1 自定义快捷键 3.2 自动命令 3.3 函数定义 4. 插件管理 4.1 插件管理工具 4.1.1 安装 vim-plug 4.1.2 配置 vim-plug 4.1.3 安装插件 4.2 常用插件 4.2.1 NERDTree 4.2.2 Fzf…...

代理协议解析:如何根据需求选择HTTP、HTTPS或SOCKS5?

代理IP协议是一种网络代理技术&#xff0c;可以实现隐藏客户端IP地址、加速网站访问、过滤网络内容、访问内网资源等功能。常用的IP代理协议主要有Socks5代理、HTTP代理、HTTPS代理这三种。代理IP协议主要用于分组交换计算机通信网络的互联系统中使用&#xff0c;只负责数据的路…...

Verilog语言和C语言的本质区别是什么?

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「C语言的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01; 用老石的一句话其实很好说…...