JNI(在C程序中运行Java类)

Author Avatar
罗炜光 9月 29, 2016
  • 在其它设备中阅读本文章

C/C++代码中调用Java代码的几种典型情况

  • 需要在C/C++编写的本地应用程序中访问用Java语言编写的代码或代码库
  • 希望在C/C++编写的本地应用程序中使用标准Java类库
  • 当需要把已有的C/C++程序与Java程序组织链接在一起时,使用Invocation API,可以将它们组织成一个完整的程序

应用示例步骤

  • 使用Invocation API加载Java虚拟机
  • 加载Java类
  • 执行被加载类的方法
  • 销毁Java虚拟机

应用示例代码

InvocationApiTest.java

public class InvocationApiTest
{
    public static void main(String[] args) 
    {
        System.out.println(args[0]);
    }
}

invocationApi.c

#include <jni.h>

int main()
{
    JNIEnv *env;
    JavaVM *vm;
    JavaVMInitArgs vm_args;
    JavaVMOption options[1];
    jint res;
    jclass cls;
    jmethodID mid;
    jstring jstr;
    jclass stringClass;
    jobjectArray args;

    //生成Java虚拟机选项
    options[0].optionString = "-Djava.class.path=.";
    vm_args.version = 0x00010002;
    vm_args.options = options;
    vm_args.nOptions = 1;
    vm_args.ignoreUnrecognized = JNI_TRUE;


    //生成Java虚拟机
    res = JNI_CreateJavaVM(&vm,(void**)&env,&vm_args);

    //查找并加载类
    cls = (*env)->FindClass(env,"InvocationApiTest");

    //获取main()方法的ID
    mid = (*env)->GetStaticMethodID(env,cls,"main","([Ljava/lang/String;)V");

    //生成字符串对象,用作main()方法的参数
    jstr = (*env)->NewStringUTF(env,"Hello Invocation API!!");
    stringClass = (*env)->FindClass(env,"java/lang/String");
    args = (*env)->NewObjectArray(env,1,stringClass,jstr);

    // //调用main()方法
    (*env)->CallStaticVoidMethod(env,cls,mid,args);

    //销毁Java虚拟机
    (*vm)->DestroyJavaVM(vm);

}

编译命令

gcc invocationApi.c "C:\Program Files\Java\jdk1.8.0_73\lib\jvm.lib"  -lstdc++ -o invocationApi.exe

运行命令

invocationApi.exe

结果

Hello Invocation API!!

创建虚拟机

在生成Java虚拟机选项时,使用的JavaVMInitArgs与JavaVMOption结构体,它们定义在jni.h头文件中

typedef struct JavaVMOption {
    char *optionString;
    void *extraInfo;
} JavaVMOption;

typedef struct JavaVMInitArgs {
    jint version;//版本号设置
    jint nOptions;//JavaVMOption结构体数组元素个数
    JavaVMOption *options;//JavaVMOption结构体的地址
    jboolean ignoreUnrecognized;//虚拟中读到错误设置是否忽略错误继续执行
} JavaVMInitArgs;

JavaVMOption结构体用来指定Java虚拟机的选项值,其形式为
-D<property>=<value>

JNI_CreateJavaVM

  • 形式:
    jint JNI_CreateJavaVM(JavaVM **vm,JNIEnv **env,void *vm_args)

  • 说明:
    装载并初始化Java虚拟机

  • 参数:

    • vm JavaVM指针的地址
    • env JNI接口指针的地址
    • vm_args 传递给Java虚拟机的参数
  • 返回值:
    成功,返回0,失败,返回负值

创建数组

NewObjectArray

  • 形式
    jarray NewObjectArray(JNIEnv *env,jsize length,jclass elementClass,jobject initalElement)

  • 说明:
    生成由elementClass对象组成的数组。数组元素个数由length指定,initalElement参数用来初始化对象数组

  • 参数:

    • env JNI接口指针
    • length 数组元素个数
    • elementClass 数组元素对象的类型
    • initialElement 数组初始化值
  • 返回值:
    若成功,则返回数组引用,失败,则返回NULL

字符串转换

NewStringUTF

  • 形式:
    jstring NewStringUTF(JNIEnv *env,const char *bytes)

  • 说明:
    将UTF-8形式的C字符串转换成java.lang.String对象

  • 参数:

    • env JNI接口指针
    • bytes 待生成String对象的C字符串的地址
  • 返回值:
    成功,返回String对象的jstring类型的引用,失败,返回NULL

GetStringUTFChars

  • 形式:
    const jbyte*GetStringUTFChars(JNIEnv *env,jstring string,jboolean *isCopy)

  • 说明:
    将Java字符串对象转换成UTF-8字符串(C字符串),并返回指针

  • 参数:

    • env JNI接口指针
    • string Java字符串对象
    • isCopy 当String对象中的字符串被转换成UTF-8字符串,被复制到内存,且指针被返回时,*isCopy 设置为JNI_TRUE,否则设置为JNI_FALSE

参考资料

Amdroid框架揭秘