目录

以下总结了Android开发中C语言调用java方法的几种情况:

1、非静态native方法调用所在对象中的方法

2、C语言调用java有基础类型返回值的情况

3、C语言调用java有类对象返回值的情况

4、非静态native方法调用本对象中的静态方法

5、非静态native方法调非本对象内的方法 使用NewObject实现

6、非静态native方法调非本对象内的方法 使用AllocObject实现

7、静态\非静态native方法调本对象\非本对象内方法(都需要在底层构造目标java方法相应的类对象)


先总结一下,C语言调用java方法的特点:

1、C语言调用java中方法的语法类似于java中的反射

2、java中的对象映射在C语言中都用jobject表示

以下总结了Android开发中C语言调用java方法的几种情况:

1、非静态native方法调用所在对象中的方法

静态native方法和非静态native方法的区别:

// 非静态java方法映射到C语言中的头文件
JNIEXPORT void JNICALL Java_com_example_jni_CTransferJava_transferJavaFun
  (JNIEnv *, jobject);

// 静态java方法映射到C语言中的头文件
JNIEXPORT void JNICALL Java_com_example_jni_CTransferJava_staticTransferJavaFun
  (JNIEnv *, jclass);

由于调用非静态native方法需要先创建相应的对象,所以头文件中第二个参数传递的值为所创建的对象,而调用静态native方法无需创建响应的对象,故第二个参数传递的值为响应的类。

C语言部分的代码:

extern "C" JNIEXPORT jint JNICALL
Java_com_example_jni_CTransferJava_transferJavaFun(JNIEnv *jniEnv, jobject object) {

    // 找到相应Class
    // 注意:方法路径要用 ”/“ 不能使用 ”.“
    jclass clazz = jniEnv->FindClass("com/example/jni/CTransferJava");

    // 找到相应的方法
    // 
    // 第二个参数:方法名
    // 第三个参数:方法签名
    jmethodID jmethodId = jniEnv->GetMethodID(clazz, "callFun", "()V");

    // 调用java方法
    jniEnv->CallVoidMethod(object, jmethodId);
}

 如何获得java对应的方法签名?

使用javap命令行获取,例子如下

javap -classpath F:\JNI\app\build\intermediates\javac\debug\compileDebugJavaWithJavac\classes -s com.example.jni.CTransferJava

图中红框均为方法签名。如要获取详细javap的用法,使用javap -help获取

 2、C语言调用java有基础类型返回值的情况

extern "C" JNIEXPORT jint JNICALL
Java_com_example_jni_CTransferJava_transferJavaFunReturnInt(JNIEnv *jniEnv, jobject object) {
    
    jclass clazz = jniEnv->FindClass("com/example/jni/CTransferJava");

    jmethodID jmethodId = jniEnv->GetMethodID(clazz, "callFunReturnInt", "()I");

    // 不同的返回值 调用相应的CallxxMethod方法
    return jniEnv->CallIntMethod(object, jmethodId);
}

3、C语言调用java有类对象返回值的情况

extern "C" JNIEXPORT jobject JNICALL
Java_com_example_jni_CTransferJava_transferJavaFunReturnObject(JNIEnv *jniEnv, jobject object) {

    jclass clazz = jniEnv->FindClass("com/example/jni/CTransferJava");

    jmethodID jmethodId = jniEnv->GetMethodID(clazz, "callFunReturnObject",
                                              "()Lcom/example/jni/CTransferTextClass;");
  
    return jniEnv->CallObjectMethod(object, jmethodId);
}
public native CTransferTextClass transferJavaFunReturnObject();

java中不同的类对象在C语言中统一使用jobject表示,尽管native transferJavaFunReturnObject()方法的返回值是CTransferTextClass,但映射为C方法时,也使用jobject表示。

public CTransferTextClass callFunReturnObject() {
        return new CTransferTextClass();
    }

C语言调用的java方法的返回值为CTransferTextClass,但是CallObjectMethod返回的也是jobject,而不是具体某个类对象,使用时也无需强转。

注意:当返回值为类时的方法签名,是为()L类名;,不要漏了";"

4、非静态native方法调用本对象中的静态方法

extern "C" JNIEXPORT void JNICALL
Java_com_example_jni_CTransferJava_transferJavaStatic(JNIEnv *jniEnv, jobject) {
    jclass clazz = jniEnv->FindClass("com/example/jni/CTransferJava");

    // 区别1:获取方法id不同
    jmethodID jmethodId = jniEnv->GetStaticMethodID(clazz, "callStaticFun", "()V");

    // 区别2:调用java方法不同
    jniEnv->CallStaticVoidMethod(clazz, jmethodId);
}

5、非静态native方法调非本对象内的方法 使用NewObject实现

extern "C" JNIEXPORT void JNICALL
Java_com_example_jni_CTransferJava_transferJavaFunOutside(JNIEnv *jniEnv, jobject object) {
    jclass clazz = jniEnv->FindClass("com/example/jni/CTransferJavaOutside");

    jmethodID jmethodId = jniEnv->GetMethodID(clazz, "callFun", "()V");

    // 找出构造方法
    jmethodID init = jniEnv->GetMethodID(clazz, "<init>", "(I)V");

    // 构造要调用java方法对应的类对象
    jobject clazzobj = jniEnv->NewObject(clazz, init, 2);

    // 使用目标java方法对应的类对象调用相应的java方法
    jniEnv->CallVoidMethod(clazzobj, jmethodId);
}
NewObject和AllocObject的区别

共同点:两个方法都是用于构建一个新的类对象

不同点:AllocObject仅仅构建一个新的类对象(仅仅为类对象分配内存空间而已),不初始成员变量,也不调用构造方法

               NewObject需要指明调用的构造方法,构建一个新的类对象,并初始化成员变量,调用指定的构造方法

 构造方法的方法名使用<init>代替

6、非静态native方法调非本对象内的方法 使用AllocObject实现

extern "C" JNIEXPORT void JNICALL
Java_com_example_jni_CTransferJava_transferJavaFunOutside_11(JNIEnv *jniEnv, jobject) {
    jclass clazz = jniEnv->FindClass("com/example/jni/CTransferJavaOutside");

    jmethodID jmethodId = jniEnv->GetMethodID(clazz, "callFun", "()V");

    // 区别于NewObject
    jobject clazzobj = jniEnv->AllocObject(clazz);
    jniEnv->CallVoidMethod(clazzobj, jmethodId);
}

 7、静态\非静态native方法调本对象\非本对象内方法(都需要在底层构造目标java方法相应的类对象)

extern "C" JNIEXPORT void JNICALL
Java_com_example_jni_CTransferJava_staticTransferJavaFun(JNIEnv *jniEnv, jclass clazz) {

     // 如果非本对象的java方法需要先找出所在类
     // 示例为在本对象,故省去找出所在类的步骤

    jmethodID jmethodId = jniEnv->GetMethodID(clazz, "callFun", "()V");

    // 视情况选择使用NewObject或者AllocObject
    jobject clazzobj = jniEnv->AllocObject(clazz);
    jniEnv->CallVoidMethod(clazzobj, jmethodId);
}

可供参考Github(为本人的学习例子)

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐