05-调用API

上一篇: 04-JNI函数


        调用 API 允许软件供应商将 Java VM 加载到任意本地应用程序中。供应商可以提供支持 Java 的应用程序,而无需链接 Java VM 源代码。

5.1 概述

        下面的代码示例说明了如何使用调用 API 中的函数。在这个示例中,C++ 代码创建了一个 Java VM 并调用了一个名为 Main.test 的静态方法。为清晰起见,我们省略了错误检查。

#include <jni.h>       /* where everything is defined */
...
JavaVM *jvm;       /* denotes a Java VM */
JNIEnv *env;       /* pointer to native method interface */
JavaVMInitArgs vm_args; /* JDK/JRE 19 VM initialization arguments */
JavaVMOption* options = new JavaVMOption[1];
options[0].optionString = "-Djava.class.path=/usr/lib/java";
vm_args.version = JNI_VERSION_19;
vm_args.nOptions = 1;
vm_args.options = options;
vm_args.ignoreUnrecognized = false;/* load and initialize a Java VM, return a JNI interface* pointer in env */
JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
delete options;/* invoke the Main.test method using the JNI */
jclass cls = env->FindClass("Main");
jmethodID mid = env->GetStaticMethodID(cls, "test", "(I)V");
env->CallStaticVoidMethod(cls, mid, 100);/* We are done. */
jvm->DestroyJavaVM();

        本示例使用了 API 中的两个函数。调用 API 允许本地应用程序使用 JNI 接口指针访问虚拟机功能。

5.1.1 创建虚拟机

        JNI_CreateJavaVM() 函数加载并初始化 Java VM,并返回一个指向 JNI 接口指针的指针。调用 JNI_CreateJavaVM() 的线程被视为主线程,并连接到 Java VM

        注意:根据操作系统的不同,原始进程线程可能会受到特殊处理,从而影响其作为正常 Java 线程正常运行的能力(如堆栈大小受限和可以抛出 StackOverflowError )。强烈建议不要使用原始线程加载 Java VM,而应专门为此创建一个新线程

5.1.2 附加到虚拟机

        JNI 接口指针( JNIEnv )只在当前线程中有效。如果另一个线程需要访问 Java 虚拟机,它必须首先调用 AttachCurrentThread() 将自己附加到虚拟机并获取 JNI 接口指针。附加到虚拟机后,本地线程的工作方式与运行在本地方法中的普通 Java 线程无异,唯一的例外是在调用对调用者敏感的方法时没有 Java 调用者。本地线程会一直附着在虚拟机上,直到调用 DetachCurrentThread() 将自己分离。

        附加线程应有足够的堆栈空间来执行合理的工作。每个线程的堆栈空间分配取决于操作系统。例如,使用 pthreads 时,可以在 pthread_create 的 pthread_attr_t 参数中指定堆栈大小。

5.1.3 脱离虚拟机

        连接到虚拟机的本地线程必须在终止前调用 DetachCurrentThread() 来自行分离。如果调用栈上有 Java 方法,线程则无法自行退出。

5.1.4 终止虚拟机

        DestroyJavaVM() 功能可终止 Java 虚拟机。

        该函数会等到没有非守护进程线程执行时才实际终止虚拟机。非守护进程线程包括: Java 线程和附加的本地线程。等待的原因是非守护进程线程可能会占用系统资源,如锁或窗口。Java 应用程序或附加本地代码的程序员有责任在线程终止或分离前释放这些资源。虚拟机无法自动释放这些资源,因此会等待程序员在终止前释放这些资源

5.2 库和版本管理

        本地程序库可以与虚拟机动态链接或静态链接。库与虚拟机映像的结合方式取决于实现。必须使用 System.loadLibrary 或等效的 API 才能将库视为已加载,这既适用于动态链接的库,也适用于静态链接的库

        本地库一旦加载,所有类加载器都能看到它。因此,不同类加载器中的两个类可能会链接到同一个本地方法。这会导致两个问题:

                ①. 一个类可能会错误地与不同类加载器中同名类加载的本地库链接。

                ②. 本地方法很容易混合来自不同类加载器的类。这会破坏类加载器提供的名称空间分隔,并导致类型安全问题

        每个类加载器都管理自己的本地库。同一个 JNI 本地库不能加载到多个类加载器中。这样做会导致 UnsatisfiedLinkError 抛出。例如,当使用 System.loadLibrary 将一个本地库加载到两个类加载器时,就会抛出 UnsatisfiedLinkError 。这种方法的优点是:

                ①. 本地程序库中保留了基于类加载器的名称空间分隔。本地库不能轻易混合来自不同类加载器的类。

                ②. 此外,当相应的类加载器被垃圾回收时,本地库也可以被卸载

5.2.1 支持静态链接库

        以下规则适用于静态链接库,这些示例中给出的静态链接库名为 "L":

                ①. 当且仅当库导出一个名为 JNI_OnLoad_L的函数时,其映像已与虚拟机结合的库 "L "才被定义为静态链接库。

                ②. 如果静态链接库 L 输出了一个名为 JNI_OnLoad_L 的函数和一个名为 JNI_OnLoad 的函数,那么 JNI_OnLoad 函数将被忽略。

                ③. 如果函数库 L 是静态链接的,那么在首次调用 System.loadLibrary("L") 或等效 API 时,将调用一个 JNI_OnLoad_L 函数,其参数和预期返回值与为 JNI_OnLoad函数指定的参数和预期返回值相同。

                ④. 静态链接的库 L 将禁止动态加载同名库

                ⑤. 当包含静态链接本地程序库 L 的类加载器被垃圾回收时,如果程序库导出了 JNI_OnUnload_L函数,虚拟机将调用该函数。

                ⑥. 如果静态链接库 L 输出了一个名为 JNI_OnUnload_L 的函数和一个名为 JNI_OnUnload 的函数,那么 JNI_OnUnload 函数将被忽略。

        程序员还可以调用 JNI 函数 RegisterNatives() 来注册与类相关的本地方法。 RegisterNatives() 函数对静态链接函数特别有用

        如果动态链接库定义了 JNI_OnLoad_L 和/或JNI_OnUnload_L函数,这些函数将被忽略。

5.2.2 库钩子函数的生命周期

        为了便于版本控制和资源管理,JNI 库可以定义加载和卸载函数钩子。这些函数的命名取决于库是动态链接还是静态链接

5.2.3 JNI_OnLoad

/*** @brief 动态链接库定义的可选函数。*        虚拟机在加载本地库时调用 JNI_OnLoad (例如通过 System.loadLibrary )* * @param vm 指向当前虚拟机结构的指针* @param reserved 未使用的指针* @return jint 返回所需的 JNI_VERSION 常量(另见 GetVersion )*/
jint JNI_OnLoad(JavaVM *vm, void *reserved);

        为了使用在 JNI API 的某个版本中定义的函数, JNI_OnLoad 必须返回一个至少定义了该版本的常量。例如,希望使用 JDK 1.4 引入的1 个函数的库至少需要返回 JNI_VERSION_1_4 个常量。如果本地库没有导出 JNI_OnLoad 个函数,虚拟机就会认为该库只需要 JNI 版本 JNI_VERSION_1_1 如果虚拟机无法识别 JNI_OnLoad 所返回的版本号,虚拟机将卸载该库,就像从未加载过该库一样

        从包含本地方法实现的动态链接本地库导出。

5.2.4 JNI_OnUnload

/*** @brief 动态链接库定义的可选函数。*        当包含本地库的类加载器被垃圾回收时,虚拟机会调用 JNI_OnUnload 。** @param vm 指向当前虚拟机结构的指针* @param reserved 未使用的指针*/
void JNI_OnUnload(JavaVM *vm, void *reserved);

        该函数可用于执行清理操作。由于该函数是在未知上下文(如来自 finalizer)中调用的,因此程序员在使用 Java VM 服务时应保持谨慎,避免任意回调 Java 服务

        从包含本地方法实现的动态链接本地库导出。

5.2.5 JNI_OnLoad_L

/*** @brief 静态链接库必须定义的强制函数** @param vm 指向当前虚拟机结构的指针* @param reserved 未使用的指针* @return jint 返回所需的 JNI_VERSION 常量(另见 GetVersion )。返回的最小版本至少为 JNI_VERSION_1_8* @since JDK/JRE 1.8*/
jint JNI_Onload_<L>(JavaVM *vm, void *reserved);

        如果一个名为 "L "的库是静态链接的,那么在首次调用 System.loadLibrary("L") 或等效 API 时,将调用一个 JNI_OnLoad_L 函数,其参数和预期返回值与为 JNI_OnLoad 函数指定的参数和预期返回值相同。 JNI_OnLoad_L 必须返回本地库所需的 JNI 版本。该版本必须是 JNI_VERSION_1_8 或更高版本。如果虚拟机无法识别 JNI_OnLoad_L 所返回的版本号,虚拟机将把该库当作从未加载过。

5.2.6 JNI_OnUnload_L

/*** @brief 静态链接库定义的可选函数。*        当包含静态链接本地程序库 "L "的类加载器被垃圾回收时,如果程序库导出了 JNI_OnUnload_L 函数,虚拟机将调用该函数。* @param vm 指向当前虚拟机结构的指针* @param reserved 未使用的指针*/
void JNI_OnUnload_<L>(JavaVM *vm, void *reserved);

        该函数可用于执行清理操作。由于该函数是在未知上下文(如来自 finalizer)中调用的,因此程序员在使用 Java VM 服务时应保持谨慎,避免任意回调 Java 服务。

        从包含本地方法实现的静态链接本地库中导出。

信息说明:

        加载本地库的行为是使 Java 虚拟机和运行时了解并注册该库及其本地入口点的完整过程。请注意,仅仅执行操作系统级别的操作来加载本地库(如在 UNIX(R) 系统上执行 dlopen 操作)并不能完全实现这一目标本机函数通常由 Java 类加载器调用,以执行对主机操作系统的调用,将库加载到内存中,并返回本机库的句柄。该句柄将被存储并用于后续本地库入口点的搜索。一旦句柄成功返回,Java 本地类加载器将完成加载过程,并注册该库

5.3 调用 API 函数

        JavaVM 类型是指向调用 API 函数表的指针。下面的代码示例显示了该函数表:

typedef const struct JNIInvokeInterface *JavaVM;const struct JNIInvokeInterface ... = {NULL,NULL,NULL,DestroyJavaVM,AttachCurrentThread,DetachCurrentThread,GetEnv,AttachCurrentThreadAsDaemon
};

        请注意, JNI_GetDefaultJavaVMInitArgs() JNI_GetCreatedJavaVMs()JNI_CreateJavaVM() 这三个调用 API 函数不在 JavaVM 函数表中这些函数可以在没有预先存在的 JavaVM 结构的情况下使用

5.3.1 JNI_GetDefaultJavaVMInitArgs

/*** @brief 返回 Java 虚拟机的默认配置。*        调用此函数前,本地代码必须将 vm_args->version 字段设置为它期望虚拟机支持的 JNI 版本。*        此函数返回后,vm_args->version 将被设置为 VM 实际支持的 JNI 版本。** @param vm_args 指向 JavaVMInitArgs 结构的指针,缺省参数填入该结构中,不能是 NULL 结构* @return jint 如果请求的版本受支持,则返回 JNI_OK ;如果请求的版本不受支持,则返回 JNI 错误代码(负数)。*/
jint JNI_GetDefaultJavaVMInitArgs(void *vm_args);

        从实现 Java 虚拟机的本地库中导出。

5.3.2 JNI_GetCreatedJavaVMs

/*** @brief 返回已创建的所有 Java 虚拟机。*        虚拟机的指针将按照创建顺序写入缓冲区 vmBuf 。最多写入1 个条目。*        创建的虚拟机总数将在 \*nVMs 中返回。** @param vmBuf 指向放置虚拟机结构的缓冲区的指针,不得为 NULL* @param bufLen 缓冲区的长度* @param nVMs 整数指针。可为 NULL 值* @return jint 成功时返回 JNI_OK ;失败时返回合适的 JNI 错误代码(负数)。*/
jint JNI_GetCreatedJavaVMs(JavaVM **vmBuf, jsize bufLen, jsize *nVMs);

5.3.3 JNI_CreateJavaVM

/*** @brief 加载并初始化 Java VM。*        当前线程将连接到 Java VM 并成为主线程。将 p_env 参数设置为主线程的 JNI 接口指针。** @param p_vm 指针,指向放置生成的虚拟机结构的位置。不能为 NULL* @param p_env 指向主线程 JNI 接口指针位置的指针。不能为 NULL* @param vm_args Java VM 初始化参数。不能为 NULL* @return jint 成功时返回 JNI_OK ;失败时返回合适的 JNI 错误代码(负数)。*/
jint JNI_CreateJavaVM(JavaVM **p_vm, void **p_env, void *vm_args);

        不支持在单个进程中创建多个虚拟机

        JNI_CreateJavaVM 的第二个参数始终是指向 JNIEnv * 的指针,而第三个参数是指向 JavaVMInitArgs 结构的指针,该结构使用选项字符串对任意虚拟机启动选项进行编码

typedef struct JavaVMInitArgs {jint version;jint nOptions;JavaVMOption *options;jboolean ignoreUnrecognized;
} JavaVMInitArgs;

        options 字段是以下类型的数组:

typedef struct JavaVMOption {char *optionString;  /* the option as a string in the default platform encoding */void *extraInfo;
} JavaVMOption;

        数组的大小由 JavaVMInitArgs 中的 nOptions 字段表示。如果 ignoreUnrecognized 为 JNI_TRUE , JNI_CreateJavaVM 会忽略所有以 " -X "或 " _ "开头的未识别选项字符串。如果 ignoreUnrecognized 为 JNI_FALSE , JNI_CreateJavaVM 在遇到任何未识别的选项字符串时立即返回 JNI_ERR 。所有 Java 虚拟机都必须识别以下一组标准选项:

        与模块相关的选项 --add-reads 、 --add-exports 、 --add-opens 、 --add-modules 、 --limit-modules 、 --module-path 、 --patch-module 和 --upgrade-module-path 必须以选项字符串形式传递,使用 "option=value "格式,而不是 "option value "格式(注意,"option "和 "value "之间必须有 " = "。)例如,要将 java.management/sun.management 输出为 ALL-UNNAMED ,必须传递选项字符串 "--add-exports=java.management/sun.management=ALL-UNNAMED" 。

        此外,每个虚拟机实现都可能支持自己的一套非标准选项字符串非标准选项名称必须以 " -X "或下划线(" _ ")开头。例如,JDK/JRE 支持 -Xms 和 -Xmx 选项,允许程序员指定初始堆大小和最大堆大小。以 " -X "开头的选项可以通过 " java "命令行访问。

        下面是在 JDK/JRE 中创建 Java VM 的示例代码:

JavaVMInitArgs vm_args;
JavaVMOption options[3];options[0].optionString = "-Djava.class.path=c:\myclasses"; /* user classes */
options[1].optionString = "-Djava.library.path=c:\mylibs";  /* set native library path */
options[2].optionString = "-verbose:jni";                   /* print JNI-related messages */vm_args.version = JNI_VERSION_1_2;
vm_args.options = options;
vm_args.nOptions = 3;
vm_args.ignoreUnrecognized = TRUE;/* Note that in the JDK/JRE, there is no longer any need to call* JNI_GetDefaultJavaVMInitArgs.*/
res = JNI_CreateJavaVM(&vm, (void **)&env, &vm_args);
if (res < 0) ...

5.3.4 DestroyJavaVM

/*** @brief 终止 Java 虚拟机的运行,尽力释放虚拟机资源。*        JavaVM 接口函数表中的索引 3** @param vm 将被销毁的 Java 虚拟机。不能为 NULL* @return jint 成功时返回 JNI_OK ;失败时返回合适的 JNI 错误代码(负数)*/
jint DestroyJavaVM(JavaVM *vm);

        任何线程,无论是否已连接,都可以调用此函数。如果当前线程尚未附加,则首先附加该线程。如果当前线程已被附加,那么如果它的调用栈中有任何 Java 方法,则会出错

        该函数等待所有非守护进程线程(如果当前线程为非守护进程线程,则不包括该线程)都已终止,然后启动关闭序列(请参阅 java.lang.Runtime)。关闭序列结束时,Java VM 将终止,导致任何仍在执行 Java 代码的线程停止执行该代码,并释放其能够释放的任何相关 VM 资源。此时,当前线程不再依附于 Java VM,此函数返回给调用者。

        请注意,Java VM 终止时仍在执行本地代码的任何线程都将继续执行该代码;但如果它们试图恢复执行 Java 代码,则将停止执行。这包括守护进程线程和关闭序列启动后启动的任何非守护进程线程。守护进程线程和非守护进程线程仅对附加的本地线程有意义;非附加的本地线程不受 Java VM 终止的影响。

5.3.5 AttachCurrentThread

/*** @brief 将当前线程作为非守护进程线程附加到 Java 虚拟机。在 p_env 参数中返回一个 JNI 接口指针*        JavaVM 接口函数表中的索引 4** @param vm 当前线程将连接的虚拟机,必须不是 NULL* @param p_env 指向当前线程的 JNI 接口指针所在位置的指针。不能为 NULL* @param thr_args 可以是 NULL 或指向 JavaVMAttachArgs 结构的指针,用于指定附加信息* @return jint 成功时返回 JNI_OK ;失败时返回合适的 JNI 错误代码(负数)*/
jint AttachCurrentThread(JavaVM *vm, void **p_env, void *thr_args);

        尝试附加已附加的线程时,只会在 p_env 参数中返回其现有的 JNI 接口指针。调用此方法后,已附加线程的守护进程状态将保持不变

        一个本地线程不能同时连接到两个 Java 虚拟机

        当线程连接到虚拟机时,上下文类加载器就是引导加载器。

        thr_args:可以是 NULL 或指向 JavaVMAttachArgs 结构的指针,用于指定附加信息:

typedef struct JavaVMAttachArgs {jint version;char *name;    /* the name of the thread as a modified UTF-8 string, or NULL */jobject group; /* global ref of a ThreadGroup object, or NULL */
} JavaVMAttachArgs

5.3.6 AttachCurrentThreadAsDaemon

/*** @brief 将当前线程作为守护线程附加到 Java VM。在 p_env 参数中返回一个 JNI 接口指针*        JavaVM 接口函数表中的索引 7** @param vm 当前线程将连接的虚拟机实例。不能为 NULL* @param p_env 指向当前线程 JNIEnv 接口指针所在位置的指针。它不能是 NULL* @param thr_args 可以是 NULL 或指向 JavaVMAttachArgs 结构的指针,用于指定附加信息* @return jint 成功时返回 JNI_OK ;失败时返回合适的 JNI 错误代码(负数)*/
jint AttachCurrentThreadAsDaemon(JavaVM *vm, void **p_env, void *thr_args);

        尝试附加已附加的线程时,只会在 p_env 参数中返回其现有的 JNI 接口指针。调用此方法后,已附加线程的守护进程状态将保持不变。

        一个本地线程不能同时连接到两个 Java 虚拟机。

        当线程连接到虚拟机时,上下文类加载器就是引导加载器。

        thr_args :可以是 NULL 或指向 JavaVMAttachArgs 结构的指针,用于指定附加信息:

typedef struct JavaVMAttachArgs {jint version;char *name;    /* the name of the thread as a modified UTF-8 string, or NULL */jobject group; /* global ref of a ThreadGroup object, or NULL */
} JavaVMAttachArgs

5.3.7 DetachCurrentThread

/*** @brief 将当前线程从 Java VM 中分离。如果调用栈中有 Java 方法,则线程无法脱离。*        JavaVM 接口函数表中的索引 5** @param vm 当前线程将脱离的虚拟机。必须不是 NULL* @return jint 成功时返回 JNI_OK ;失败时返回合适的 JNI 错误代码(负数)*/
jint DetachCurrentThread(JavaVM *vm);

        该线程仍持有的任何 Java 监视器都会被释放(不过在正确编写的程序中,所有监视器都会在此之前被释放)。现在,该线程被视为已终止,不再存活;所有等待该线程死亡的 Java 线程都会收到通知

        主线程可以从虚拟机中分离出来。

        试图分离未连接的线程是不可能的

        如果调用 DetachCurrentThread 时有异常等待处理,虚拟机可以选择报告该异常的存在。

5.3.8 GetEnv

/*** @brief JavaVM 接口函数表中的索引 6** @param vm 将从中检索接口的虚拟机实例。不得为 NULL* @param p_env 指向当前线程的 JNI 接口指针所在位置的指针。不能为 NULL* @param version 请求的 JNI 版本* @return jint 如果当前线程未连接到虚拟机,则将 *env 设为 NULL ,并返回 JNI_EDETACHED 。*              如果不支持指定的版本,则将 *env 设置为 NULL ,并返回 JNI_EVERSION 。*              否则,将 *env 设置为相应的接口,并返回 JNI_OK*/
jint GetEnv(JavaVM *vm, void **p_env, jint version);

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

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

相关文章

想开发苹果群控软件?先了解这些代码!

随着智能设备的普及&#xff0c;群控软件的需求日益增加&#xff0c;特别是针对苹果设备的群控软件&#xff0c;因其出色的性能和广泛的用户基础&#xff0c;受到了开发者们的青睐。 然而&#xff0c;开发一款功能强大的苹果群控软件并非易事&#xff0c;需要深入了解苹果的开…

配电网数字化转型全面推进:《关于新形势下配电网高质量发展的指导意见》

近日&#xff0c;国家发展改革委、国家能源局印发了《关于新形势下配电网高质量发展的指导意见》&#xff08;以下简称《意见》&#xff09;&#xff0c;到2030年&#xff0c;基本完成配电网柔性化、智能化、数字化转型&#xff0c;实现主配微网多级协同、海量资源聚合互动、多…

基于SpringBoot+Vue+ElementUI+Mybatis前后端分离管理系统超详细教程(四)——前后端数据交互

经过前面几个章节的学习我们掌握了&#xff1a; 1、如何使用Vue快速搭建前端工程化项目&#xff0c;并结合elementUI优化了界面&#xff1b; 基于SpringBootVueElementUIMybatis前后端分离管理系统超详细教程&#xff08;一&#xff09; 基于SpringBootVueElementUIMybatis前后…

有没有能用蓝牙的游泳耳机?6招解决选购难题,瞄准好货!

在数字化时代&#xff0c;我们越来越依赖各种电子设备来提升生活质量。对于喜欢运动的朋友来说&#xff0c;耳机已经成为他们必不可少的装备之一。无论是跑步、健身还是游泳&#xff0c;耳机都能为我们提供美妙的音乐和清晰的语音通话&#xff0c;让我们的运动体验更加丰富多彩…

使用ffmpeg提取视频中的音频并保存为单声道wav

1 原始视频信息 通过ffmpeg -i命令查看视频基本信息 $ ffmpeg -i C0439.MP4 ffmpeg version 6.1-essentials_build-www.gyan.dev Copyright (c) 2000-2023 the FFmpeg developersbuilt with gcc 12.2.0 (Rev10, Built by MSYS2 project)configuration: --enable-gpl --enable…

根据xlsx文件第一列的网址爬虫

seleniumXpath 在与该ipynb文件同文件下新增一个111.xlsx&#xff0c;第一列放一堆需要爬虫的同样式网页 然后使用seleniumXpath爬虫 from selenium import webdriver from selenium.webdriver.common.by import By import openpyxl import timedef crawl_data(driver, url)…

骨传导蓝牙立体声耳机哪个好?骨传导耳机选购前不可忽视的几个要点

骨传导蓝牙立体声耳机是一种创新的蓝牙耳机设备&#xff0c;它通过将声音转换为特定频率的振动&#xff0c;直接通过颅骨传递到内耳&#xff0c;绕过了外耳道。这种耳机设计为一般为挂耳式&#xff0c;它无需插入耳道&#xff0c;所以不会对耳膜造成损伤&#xff0c;同时也避免…

通过sqoop把hive数据到mysql,脚本提示成功,mysql对应的表中没有数

1、脚本执行日志显示脚本执行成功&#xff0c;读写数量不为0 2、手动往Mysql对应表中写入数据十几秒后被自动删除了 问题原因&#xff1a; 建表时引擎用错了&#xff0c;如下图所示 正常情况下应该用InnoDB

借助工具优化开发流程,提升开发体验

背景 最近在做一个demo&#xff0c;只有一个html页面&#xff0c;需要开启一个https web server&#xff0c;以此来实现在浏览器中访问。 改造前 改造前&#xff0c;每次修改文件保存后都要刷新一下浏览器。 如果只是短时间&#xff0c;每次修改后手动刷新浏览器也还行。主要…

smplx pkl格式可视化

smplx pkl格式可视化 import glob import os import pickleimport torch import numpy as npfrom smplpytorch.pytorch.smpl_layer import SMPL_Layer from display_utils import display_model, display_model_continuousfrom matplotlib import pyplot as plt from matplotl…

构建cef基本框架及构建过程中的参数说明

文章目录 准备源码版本编译版本结构编译过程写了好多CEF的内容了,发现一个最初的CEF helloworld的过程都没有写,也就是如何搭建这个CEF框架。今天把这个过程记录一下。 准备源码版本 在度娘上搜cef源码,一般得到的是https://bitbucket.org/chromiumembedded/cef/这个网址,…

RocketMQ快速入门_2. rocketmq 的应用场景、与其他mq的差异

0. 引言 之前我们讲解过rabbitMQ&#xff0c;本期我们将进入吞吐量更加强大的rocketMQ的学习。 1. 基础概念 如果你是刚接触MQ的同学&#xff0c;还不清楚消息队列的基础概念的&#xff0c;可以参考我之前这篇文章&#xff1a; https://wu55555.blog.csdn.net/article/deta…