Android JNI开发笔记三:静态注册和动态注册

JNI的开发中,Java层的方法和C/C++层的函数之间的对应关系是通过注册来实现的,要不然它怎么知道java的方法到了c/c++去找哪个对应的方法呢?JNI的方法注册分为静态注册和动态注册。

3.1 静态注册

静态注册使用Java_PACKAGENAME_CLASSNAME_METHODNAME 来进行与java方法的匹配

一般步骤如下:

1)编写java类,假如是JniTest.java

2)在命令行下输入 javac JniTest.java 生成JniTest.class文件

3) JniTest.class 目录下 通过 javah xxx.JniTest(全类名)生成 xxx_JniTest.h 头文件

4)编写xxx_JniTest.c 源文件,并拷贝xxx_JniTest.h 下的函数,并实现这些函数,且在其中添加jni.h头文件;

5)编写 cmakelist.txt 文件,编译生成动态/静态链接库

例如,下面就是静态注册的cpp文件的大概的模板:

#include <jni.h>
#include <string>
#include <iostream>

extern "C" JNIEXPORT jstring JNICALL
Java_com_demo_ndk_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

extern "C" JNIEXPORT jstring JNICALL
Java_com_demo_ndk_MainActivity_sayHello(
        JNIEnv *env,
        jclass clazz,
        jstring str) {
    const char* helloText = env->GetStringUTFChars(str, JNI_FALSE);
    std::string str1 = "I receive your words:";
    std::string result = str1 + helloText;
    env->ReleaseStringUTFChars(str, helloText);
    return env->NewStringUTF(result.c_str());
}

extern "C" JNIEXPORT jstring JNICALL是固定的,其中extern "c"的是意思是告诉编译器按照C语言的方式去编译函数,这是为了C/C++混合编程,JNICALL是表示这是一个被JNI调用的函数,Java+方法的全路径名就是静态注册的方法名。

JNI的方法有两个固定参数:

1. 如果java层是普通方法,前面的两个参数固定为(JNIEnv *env, jobject thiz)

2. 如果java层是静态方法,前面的两个参数固定为 (JNIEnv *env, jclass clazz)

JNIEnv表示是JNI的上下文环境,它里面封装了很多JNI的方法,jobject表示的java层调用的这个对象的this指针,jclass表示的java的类,因为静态方法不属于某个对象,而是属于类。

3.2. 动态注册
我们在上面代码中使用的 Java_PACKAGENAME_CLASSNAME_METHODNAME 来进行与java方法的匹配,这
种方式我们称之为静态注册。而动态注册则意味着方法名可以不用这么长了,在android aosp源码中就大量的使用了动态注册的形式

//Java: 
native void dynamicNative(); 
native String dynamicNative(int i); 

//C++:
 void dynamicNative1(JNIEnv *env, jobject jobj){ 
   LOGE("dynamicNative1 动态注册"); 
 }
 jstring dynamicNative2(JNIEnv *env, jobject jobj,jint i){ 
   return env->NewStringUTF("我是动态注册的dynamicNative2方法"); 
 }
 
 //需要动态注册的方法数组
 static const JNINativeMethod mMethods[] = {
	 {"dynamicNative","()V", (void *)dynamicNative1}, 
	 {"dynamicNative", "(I)Ljava/lang/String;", (jstring *)dynamicNative2} 
 };
 
 //需要动态注册native方法的类名 
 static const char* mClassName = "com/dongnao/jnitest/MainActivity"; 
 jint JNI_OnLoad(JavaVM* vm, void* reserved){ 
	 JNIEnv* env = NULL;
	 //获得 JniEnv 
	 int r = vm->GetEnv((void**) &env, JNI_VERSION_1_4); 
	 if( r != JNI_OK){ 
	     return -1; 
	 }
	 jclass mainActivityCls = env->FindClass( mClassName);
	 // 注册 如果小于0则注册失败
	 r = env->RegisterNatives(mainActivityCls,mMethods,2);
	 if(r != JNI_OK ) { 
	    return -1; 
	 }
	 return JNI_VERSION_1_4; 
 }

JNI_OnLoad方法是在我们调用System.loadLibrary的时候调用的,这相当于在加载so库的时候,把我们的java方法和JNI层的方法做了一个映射,这样就不需要写一串很长的方法名了,使用也灵活。

3.3 system.load()/system.loadLibrary() 区别

最后说一下 system.load()/system.loadLibrary()的区别

System.load
System.load 参数必须为库文件的绝对路径,可以是任意路径,例如: System.load("C:\Documents and
Settings\TestJNI.dll"); //Windows
System.load("/usr/lib/TestJNI.so"); //Linux


System.loadLibrary
System.loadLibrary 参数为库文件名,不包含库文件的扩展名。
System.loadLibrary ("TestJNI"); //加载Windows下的TestJNI.dll本地库
System.loadLibrary ("TestJNI"); //加载Linux下的libTestJNI.so本地库

发布了27 篇原创文章 · 获赞 24 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/shving/article/details/102773884