其实在AS帮助我们创建的工程里,就已经向我们阐述了java如何调用native方法。
1、加载so库,选择合适的时机加载需要使用的so库,例如在静态代码块中直接加载。
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
2、调用Java中通过native声明的方法,最终会调用到native中的C/C++代码。
public void sayHello() {
Log.d(TAG, "Hello I am Java method.");
}
extern "C"
JNIEXPORT void JNICALL
Java_com_jdqm_ndktutorials_MainActivity_callJavaMethod(JNIEnv *env, jobject thiz) {
jclass jclazz = env->FindClass("com/jdqm/ndktutorials/MainActivity");
jmethodID jmethodId = env->GetMethodID(jclazz, "sayHello", "()V");
env->CallVoidMethod(thiz, jmethodId);
}
这个例子是在native层调用了Java的实例方法seyHello(),分3步:
- 找到jclass;
- 找到jclass中对应的方法id;
- 调用方法
public static void staticSayHello() {
Log.d(TAG, "Hello I am Java static method.");
}
extern "C"
JNIEXPORT void JNICALL
Java_com_jdqm_ndktutorials_MainActivity_callJavaStaticMethod(JNIEnv *env, jclass clazz) {
jclass jclazz = env->FindClass("com/jdqm/ndktutorials/MainActivity");
jmethodID jmethodId = env->GetStaticMethodID(jclazz, "staticSayHello", "()V");
env->CallStaticVoidMethod(jclazz, jmethodId);
}
步骤其实和实例方法差不多,只不过调用env中的方法时,多了Static。值得一提的是,静态native方法JNIEnv *env, jclass clazz
第二个参数是jclass,对应的是声明native方法的Class对象,而实例方法则是jobject,对应的是声明native方法类的实例对象。
留意其中一个函数
jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* sig)
这个函数的三个参数分别是:Class对象,方法名,方法签名。其中方法签名是用来辅助找到方法,因为Java中方法可以重载,也就是说单单通过方法名无法定位到具体的方法。这里的方法签名包含两个部分:参数列表,返回值,早期可以用命令javap来辅助查看方法签名,熟悉规律了之后其实就不在需要了。
基本格式
(参数1类型;参数2类型;...参数N;)返回值类型
当参数为引用类型的时候,参数类型的根式为"L包名",其中包名的.(点)要换成"/",比如String是Ljava/lang/String,Bundle是(Landroid/os/Bundle。
Java类型 | 类型标识 |
---|---|
boolean | Z |
byte | B |
char | C |
short | S |
int | I |
long | J |
float | F |
double | D |
这个其实很好记的,除了boolean和long,其他都是首字母大写,主要这两个被占用了,B被byte占用,L与引用类型“L包名”冲突。如果返回值是void,对应的签名是V。另外有一个特殊的类型:数组
Java类型 | 类型标识 |
---|---|
int[] | [I |
String[] | [Ljava/lang/String; |