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

news2024/9/22 15:29:34

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() {
            @Override
            public void run() {
                initOpenGL();
                // 生成OpenGL纹理ID
                int 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() {
            @Override
            public void run() {
                // 从资源中加载位图
                final Bitmap bitmap = BitmapFactory.decodeResource(getUnityContext().getResources(), R.drawable.wutiaowu);
                if (bitmap != null) {
                    // 绑定纹理ID
                    GLES20.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安卓共享纹理

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

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

相关文章

越权与逻辑漏洞

目录 越权漏洞 1、越权原理概述 2、越权分类 2.1、平行越权 2.2、垂直越权 3、越权防范&#xff1a; 逻辑漏洞 1、常见的逻辑漏洞 2、逻辑漏洞概述 3、逻辑漏洞防范&#xff1a; 越权漏洞 1、越权原理概述 如果使用A用户的权限去操作B用户的数据&#xff0c;A的权限…

静态路由技术

一、路由的概念 路由是指指导IP报文发送的路径信息。 二、路由表的结构 1、Destination/Mask:IP报文的接收方的IP地址及其子网掩码; 2、proto:协议(Static:静态路由协议,Direct:表示直连路由) 3、pref:优先级(数值和优先级成反比) 4、cost:路由开销(从源到目的…

jQuery下落撞击散乱动画

jQuery下落撞击散乱动画https://www.bootstrapmb.com/item/14767 在jQuery中实现一个下落撞击后散乱的动画效果&#xff0c;你可以结合CSS动画和jQuery的动画函数来完成。不过&#xff0c;由于jQuery本身并不直接支持复杂的物理效果&#xff08;如撞击后的散乱&#xff09;&a…

Nessus-M 暴力破解Nessus漏扫后台登录工具

项目地址:https://github.com/MartinxMax/Nessus-M Nessus-M Nessus漏洞扫描程序登录界面的暴力破解工具 帮助信息 $ python3 nessus-m.py -h 暴力破解 $ python3 nessus-m.py 192.168.101.156 8834 username.txt /usr/share/wordlists/rockyou.txt --protocol https

贪心系列专题篇二

增减字符串匹配 题目 思路 贪心策略&#xff1a;对于[0,n]&#xff0c;当遇到“I”时&#xff0c;把所剩的数中最小的拿来使用&#xff1b; 当遇到“D”时&#xff0c;把所剩的数中最大的拿来使用&#xff0c;最后还剩一个数&#xff0c;放末尾。 代码 class Solution { pu…

sbti科学碳目标倡议是什么

在科学界、工业界以及全球政策制定者的共同努力下&#xff0c;一个名为“科学碳目标倡议”&#xff08;Science Based Targets initiative&#xff0c;简称SBTi&#xff09;的全球性合作平台应运而生。这一倡议旨在推动企业和组织设定符合气候科学要求的减排目标&#xff0c;以…

Nginx 如何处理请求的限速?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01; 文章目录 Nginx 如何处理请求的限速一、为什么需要对请求进行限速&#xff08;一&#xff09;服务器过载&#xff08;二&#xff09;资源竞争&#xff08;三&#xff09;服…

数据接入开放协议-GRPC接入

协议定义 一、接入认证 message VerifyRequest { string authToken 1; // 接入管理分配的UUID string endpointName 2; // 定义的接入设备名 string endpointIdentify 3; // 接入设备的ID int64 leaseValue 4; // 租约时间,接入侧申明数据上送间隔最大时间&…

JWT令牌在项目中的实战操作

一.什么是JWT令牌&#xff1f; JWT&#xff0c;全称JSON Web Token&#xff0c;官网&#xff08;https://jwt.io/&#xff09;&#xff0c;定义了一种间接的&#xff0c;自包含的格式&#xff0c;用于在通信双方以json数据格式安全的传输信息。由于数字签名的存在&#xff0c;…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 卡牌游戏(200分) - 三语言AC题解(Python/Java/Cpp)

🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 🍿 最新华为OD机试D卷目录,全、新、准,题目覆盖率达 95% 以上,支持题目在线…

哪些工具能分析反向链接?

这里推荐两个工具&#xff0c;Ahrefs以及SEMrush&#xff0c;Ahrefs 是一个全面的SEO工具&#xff0c;特别擅长反向链接分析。它可以显示谁在链接到你的网站&#xff0c;以及这些链接的质量和数量。Ahrefs 提供详细的报告&#xff0c;包括每个反向链接的锚文本、来源网站的权重…

内部函数和外部函数(例子为C语言)

​​​​ 怎么来的&#xff1f; 函数本质上是全局的&#xff0c;因为定义一个函数的目的就是这个函数与其他函数之间相互调用&#xff0c;如果不声明的话&#xff0c;一个函数既可以被本文件中的其他函数调用&#xff0c;也可以被其他文件中的函数调用。但是可以指定某些函数…

php 存储复杂的json格式查询(如:经纬度)

在开发中&#xff0c;有时我们可能存了一些复杂json格式不知道怎么查。我这里提供给大家参考下&#xff1a; 一、先上表数据格式&#xff08;location字段的possiton经纬度以逗号分开的&#xff09; {"title":"澳海文澜府","position":"11…

JSON 文件存储

JSON 全称为&#xff1a; JavaScript Object Notation 也就是 javaScript 对象标记&#xff0c;通过对象和数组的组合来表示数据&#xff0c; 虽然构造简洁&#xff0c;但是结构化程度非常高&#xff0c; 是一种轻量级的数据交换格式 对象和数组 在 JavaScript 语言中&#…

MAT使用

概念 Shallow heap & Retained Heap Shallow Heap就是对象本身占用内存的大小。 Retained Heap就是当前对象被GC后&#xff0c;从Heap上总共能释放掉的内存(表示如果一个对象被释放掉&#xff0c;那会因为该对象的释放而减少引用进而被释放的所有的对象&#xff08;包括…

用Docker来辅助开发

现在好像用Docker是越来越多了。之前其实也看过docker的原理&#xff0c;大概就是cgroup那些&#xff0c;不过现在就不看原理了&#xff0c;不谈理论&#xff0c;只看实际中怎么用&#xff0c;解决眼前问题。 用docker来做开发&#xff0c;其实就是解决的编译环境的差异&#…

解决kkfileview 使用https预览问题记录

场景&#xff1a;项目使用了开源的kkfileview进行文件在线预览&#xff0c;部署方式使用的是docker&#xff0c;使用IP进行访问&#xff0c;但是http协议直接访问有漏洞告警&#xff0c;现在需要调整为https&#xff0c;且仍然需要使用IP访问。 kkfileview官网kkFileView - 在线…

大数据架构体系演进

传统离线大数据架构 ​ 21世纪初随着互联网时代的到来&#xff0c;数据量暴增&#xff0c;大数据时代到来。Hadoop生态群及衍生技术慢慢走向“舞台”&#xff0c;Hadoop是以HDFS为核心存储&#xff0c;以MapReduce&#xff08;简称MR&#xff09;为基本计算模型的批量数据处理…

重新盘点更新!靠谱且实用的8个AI绘画网站,国产之光

在从事AI绘画领域研究期间&#xff0c;结识了众多新朋友。越来越多人问AI工具就会在问国内用什么&#xff0c;国内什么好用&#xff0c;大家都知道了SD和MJ属于AI绘图领域的两尊大神&#xff0c;问国内其实背后是希望简简单单画图&#xff0c;能快捷方便体验&#xff0c;并且出…

深入理解Linux网络(八):内核如何发送网络包

深入理解Linux网络&#xff08;八&#xff09;&#xff1a;内核如何发送网络包 一、总览二、网卡启动准备三、ACCEPT 创建新 SOCKET四、开始发送数据send 系统调⽤实现传输层处理传输层拷贝传输层发送 网络层发送原理邻居⼦系统网络设备子系统软中断调度igb网卡驱动发送发送完成…