JNI开发流程

本文详细介绍了JNI开发过程,包括Java调用C(Java call C)、C调用Java(C call Java)的步骤,以及如何生成.h文件、查看.class文件方法签名。在Java文件中声明native接口,通过javac -h命令生成头文件,然后在C代码中实现。C调用Java时,需利用反射并借助javap -s命令获取方法签名。总结了JNI开发的关键步骤,提供了一个示例工程地址。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JNI开发,可以实现Java与c/c++互相调用。

1.NDK开发中的静态库、动态库就是Linux中的概念
静态库:所有函数库编进目标代码,如果静态函数库改变类,则整个应用程序需要重新编译,名称对应libxxx.a
动态库:动态库不会编入目标代码,所以动态库改变不影响应用程序,名称对应libxxx.so

2.JNI注册方式有2种:静态注册、动态注册
静态注册:流程繁琐,c源文件中函数名要与java native接口一一对应(下文都是此注册方式);
动态注册:在c源文件中使用JNINativeMethod数组来声明java native与c中函数对应关系,还需要写一个JNI_OnLoad函数(android aosp源码中大量使用此注册方式)

1. Java call C

  1. 在java文件中声明native接口;
  2. 生成头文件(javac -h 命令);

使用此命令生成头文件 javac -h . JNI.java

  1. 拷贝.h文件到jni代码目录,并实现.c源文件;
  2. 配置CMakeLists.txt编译生成动态库(.so)/静态库(.a)。

demo工程如下:

  • JavaCallCJNI.java内定义native接口;
  • src/main/jni目录下进行c代码实现
    在这里插入图片描述
    CMakeList.txt内容如下:
cmake_minimum_required(VERSION 3.4.1)

add_library(
        # 生成的lib名称
        java_call_c
        # 动态库
        SHARED
        # 源文件
        src/main/jni/java_call_c.c)

find_library(

        log-lib

        log )

target_link_libraries(

        java_call_c

        ${log-lib})

build.gradle文件中,在android节点下配置cmake内容。

    externalNativeBuild{
        cmake{
            path "CMakeLists.txt"
        }
    }

so文件生成目录:build/intermediates/cmake
在这里插入图片描述

2. C call Java

  1. 在java文件中声明native接口;
  2. 生成头文件(javac -h 命令);
  3. 拷贝.h文件, 实现c源文件(反射调用java方法,javap -s命令看方法签名);
  4. 配置CMakeLists.txt编译生成动态库(.so)/静态库(.a)。

C调用java代码,工程结构与第1节基本一样,不同到是在c中用反射的方式调用java方法,CCallJavaJNI.java代码示例。

public class CCallJavaJNI {
    /**
     * java调用callbackAdd, c在此方法内部反射调用java的 add方法
     */
    public native void callbackAdd();

    public native void callbackGetMessage();

    public int add(int x, int y) {
        System.out.println("Invoke add: x= " + x + ", y= " + y);
        return x + y;
    }

    public void getMessage(String s) {
        System.out.println("Invoke getMessage: s= " + s);
    }

}

c_call_java.c中,用反射的方式调用java中的add方法,方法签名用javap -s命令查看。

JNIEXPORT void JNICALL Java_com_example_ccalljava_CCallJavaJNI_callbackAdd
        (JNIEnv *env, jobject jobj) {
    // 1.得到字节码
    // jclass      (*FindClass)(JNIEnv*, const char*);
    jclass jclazz = (*env)->FindClass(env, "com/example/ccalljava/CCallJavaJNI");
    // 2.获取方法
    // jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
    jmethodID jmid = (*env)->GetMethodID(env, jclazz, "add", "(II)I");
    // 3.对象实例化
    // jobject     (*AllocObject)(JNIEnv*, jclass);
    jobject jo = (*env)->AllocObject(env, jclazz);
    // 4.调用方法
    // jint        (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
    jint value = (*env)->CallIntMethod(env, jo, jmid, 10, 20);
};

3. 总结

3.1 生成.h文件

假如在JNI.java中定义了native方法,需要根据此.java文件生成对应的.h头文件,然后实现.c文件。
进入JNI.java文件目录,执行如下指令。(早期JDK版本有javah指令,后面JDK版本都是集成在javac指令集中)

javac -h . JNI.java

此命令先自动生成.class,然后再生成.h文件,如下所示
在这里插入图片描述

3.2 查看.class文件方法签名

C代码调用Java代码,需要用到反射,反射调用方法需要知道方法签名。
用javap命令,如下查看.class文件的方法签名。

javap -s JNI.class

示例查看CCallJavaJNI.class的方法签名,descriptor内容即为方法签名。

zhengweideMacBook-Pro:ccalljava zhengwei$ javap -s CCallJavaJNI.class
Compiled from "CCallJavaJNI.java"
public class com.example.ccalljava.CCallJavaJNI {
  public com.example.ccalljava.CCallJavaJNI();
    descriptor: ()V

  public native void callbackAdd();
    descriptor: ()V

  public native void callbackGetMessage();
    descriptor: ()V

  public int add(int, int);
    descriptor: (II)I

  public void getMessage(java.lang.String);
    descriptor: (Ljava/lang/String;)V
}

示例工程地址
git@github.com:justinworkshop/JniDemo.git

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值