我目前正在开发一个混合 java/C 项目。 因为我喜欢尽可能减少运行时障碍,所以我倾向于将资源(图片)集成到我的二进制文件中,从而消除诸如“找不到图片”之类的错误,这些错误可能在用户移动我的二进制文件时发生。 我知道我可以将 java 文件包含到我的 C 可执行文件中(例如通过 C23 #embed 或 xxd)。通常,在现代机器上,数 MB 的二进制文件不再是问题。 然后我可以通过(程序开头的一个简单的 foreach 循环)加载我的 C 程序中的类:
jclass DefineClass(JNIEnv *env, const char *name, jobject loader,
const jbyte *buf, jsize bufLen);
我的问题是:如何对整个 jar 文件执行此操作?我想包括,例如我的二进制文件中的selenium API?是否足以解压缩每个 jar 文件,将其包含在二进制文件中,并定义它们? 有什么我可能遗漏的障碍吗?
编辑:我的切入点在 C 端。事实上,整个应用程序不包含任何 Java 代码,它只是通过 JNI 使用 Selenium 的一些 API。
作为一种完全不同的方法,为什么不滥用 JAR 底层的 ZIP 文件格式呢? ZIP 文件将其元数据存储在文件末尾,这意味着您可以简单地将 JAR 文件附加到二进制文件并将其添加到自己的类路径中。
举个简单的例子,以 Main.java 为例:
class Main {
public static void main(String[] args) {
System.out.println("Hello from java");
}
}
和jni.cpp:
#include <jni.h>
#include <string>
int main(int argc, char **argv) {
JavaVM *jvm; /* denotes a Java VM */
JNIEnv *env; /* pointer to native method interface */
JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */
JavaVMOption* options = new JavaVMOption[1];
std::string option = "-Djava.class.path=";
option += argv[0];
options[0].optionString = const_cast<char *>(option.c_str());
vm_args.version = JNI_VERSION_1_6;
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, "main", "([Ljava/lang/String;)V");
env->CallStaticVoidMethod(cls, mid);
/* We are done. */
jvm->DestroyJavaVM();
}
然后你可以这样做:
$ make jni
$ javac Main.java
$ jar cf code.jar Main.java
$ cat jni code.jar > jni-combined
$ ./jni-combined
Hello from java