Android JNI C/C++调用Java方法

时间:2021-10-04 20:20:01

JNI环境的支持

Android JNI编程从Java方法来调用native方法是比较容易的,
因为Java本身就提供了native关键字作为索引,只要正确的对应Java方法和native方法的包名,做到这一步并不难.
而从C/C++方法调用Java方法则稍复杂一点,因为C/C++没有提供跨语言调用的直接支持,所以需要由Java的JNI运行环境来提供帮助.

每一个JNI native方法的声明一般是这样的:
extern "C"
JNIEXPORT jstring JNICALL
Java_com_sliver_cx_android_1test_MainActivity_stringFromJNI(JNIEnv *env,jobject);

一个个关键字的作用是:
1. extern "C" C/C++的声明关键字 表明这个jni方法是按照C的语法进行编译的.
2. JNIEXPORT 在jni的固件代码中这样声明
#define JNIEXPORT __attribute__ ((visibility ("default")))
visibility ("default")意味着GCC在生成动态库的时候 默认这个方法的符号是被隐藏的
这样做应该是为了除了这个apk外不让其他程序使用这个库的这个方法
3. Java_com_sliver_cx_android_1test_MainActivity_stringFromJNI
jni方法名:命名格式 包名_方法名 anroid_1test这个数字1似乎是ide自己加上的 实际上在调用FindClass寻找class时,不应该带有这个1.
4. JNIEnv *env,jobject
JNI方法的默认参数 env是JNI的运行环境 jobject是调用这个native方法的Java类

Java签名

Java方法是带有签名的,而C/C++ native方法要调用Java方法 需要知道Java方法的签名.
在terminal下:
首先编译出class文件
javac test.java
执行 javap -s test可以输出类test中方法的签名

签名对应规则 void call(); 对应 ()V
括号内是参数签名 括号后是返回值签名 V对应void

引申出的多个参数签名 void call(int a); 对应 (I)V
int call(int a, char b);对应 (IC)I

JNI api

1. FindClass()
函数原型
jclass FindClass(const char* name)
{
return functions->FindClass(this, name);
}
FindClass用来寻找在JNI环境中的Java Class
name为指定的包名
例如 FindClass("com/cx/test/Test"); // com/cx/test是包名 Test是类名

2. GetMethod
函数原型
jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)
{ return functions->GetMethodID(this, clazz, name, sig); }
GetMethod用来寻找JNI环境中的java类的方法
clazz为调用这个native方法的Java类
name为寻找的Java方法名
sig为Java方法的签名
例如 GetMethod(obj, "call", "()V");

应用举例

Java class:
public class Test {
public void call(int nb) {
System.out.println("cnt" + nb);
}
public native test_call();
}

调用 Java Class:
public class Call {
public static void main(String[] args) {
Test a = new Test();
a.test_call();
}
}

JNI cpp:
extern "C"
JNIEXPORT void JNICALL
Java_com_cx_test_Test_call(
JNIEnv *env,
jobject obj,
int cnt) {
jclass myclass = env->FindClass("com/cx/test/Test");
if (myclass == nullptr) {
LOGI("failed to find class");
return;
}

jmethodID myid = env->GetMethodID(myclass, "call", "(I)V");
if (myid == nullptr) {
LOGI("failed to get method id");
return;
}

env->CallVoidMethod(obj, myid, 5);
}