一、前言
上一篇我们讲了NDK开发的最简单的一个入门流程,且写了一个实例。实例中java的native方法与C/C++代码函数是通过Java_<包名>_<类名>_<方法名>这种方式对应的,称为静态注册。在上一篇的例子中,我们是通过javah -jni命令来自动生成.h头文件的,自动帮我们写好了方法名,所以虽然函数名很长(手写的话容易写错),但是自动生产然后copy到C++类中,倒也没什么麻烦的。但是若是我们需要调用之前就早已写好的C++代码,方法名肯定就不满足静态注册的规则了,就需要用到动态注册JNI的方法了。这篇文章将介绍动态注册JNI的方式以及同时多个JNI时的处理方式(这里一个静态注册,一个动态注册),NDK开发流程以及静态注册请查看文章:NDK开发 从入门到放弃(一、基本流程入门了解)
二、实例
同样,我们先写一个本地方法的Java类。
public class JNIDynamicUtils {
/**
* 调用C++代码的方法,返回对应的字符串
* @return
*/
public static native String getHelloStringFromJNI();
/**
* 加载so库或jni库
*/
static {
System.loadLibrary("JNI_DYNAMIC_ANDROID_TEST");
}
}
在静态注册的时候,我们会对该类做javah -jni
操作,生产对应的.h的头文件,在cpp文件中使用头文件中自动给我们生成好的C++方法名,因为此处我们要使用动态注册的方式,则不需要做这个操作了。我们直接在jni目录下新建jnidynamicutils.cpp
文件,假设我们已经有了一个C++代码的native_hello
函数,该函数返回一个字符串(Java与C++之间的类型对应与字符串操作将在下一节讲解),我们现在希望当我们调用JNIDynamicUtils
的getHelloStringFromJNI
方法时调用这个native_hello
函数,则需要在JNI_OnLoad
方法内进行动态注册绑定,相关注释见代码。
#include <stdio.h>
#include <jni.h>
#include <stdlib.h>
//C++层 native函数
jstring native_hello(JNIEnv *env, jclass clz) {
return env->NewStringUTF("Hello java, this is C++. ---jni");
}
/**
* JNINativeMethod由三部分组成,可添加多组对应:
* (1)Java中的函数名;
* (2)函数签名,格式为(输入参数类型)返回值类型;
* ()Ljava/lang/String; ()表示无参,Ljava/lang/String;表示返回String,在对象类名(包括包名,‘/’间隔)前面加L,分号结尾
* (3)native函数名
*/
static JNINativeMethod gMethods[] = { {
"getHelloStringFromJNI", "()Ljava/lang/String;", (void *) native_hello } };
//System.loadLibrary过程中会自动调用JNI_OnLoad,在此进行动态注册
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
JNIEnv *env = NULL;
jint result = JNI_FALSE;
//获取env指针
if (jvm->GetEnv((void**) &env, JNI_VERSION_1_6) != JNI_OK) {
return result;
}
if (env == NULL) {
return result;
}
//获取类引用,写类的全路径(包名+类名)。FindClass等JNI函数将在后面讲解
jclass clazz = env->FindClass("***/***/JNIDynamicUtils");
if (clazz == NULL) {
return result;
}
//注册方法
if (env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) < 0) {
return result;
}
//成功
result = JNI_VERSION_1_6;
return result;
}
这里给出另一静态注册的相关代码,但不再解释(JNIStaticUtils.java 文件与 jnistaticutils.cpp文件,jnistaticutils.h文件就不给出了,只是申明了一个函数名而已)。
public class JNIStaticUtils {
/**
* 调用C++代码的方法,返回对应的字符串
* @return
*/
public static native String getStringFromJNI();
/**
* 加载so库或jni库
*/
static {
System.loadLibrary("JNI_STATIC_ANDROID_TEST");
}
}
#include <stdio.h>
#include <jni.h>
#include <stdlib.h>
#include "jnistaticutils.h"
JNIEXPORT jstring JNICALL Java_<包名>_JNIStaticUtils_getStringFromJNI(JNIEnv *env, jclass clazz) {
return env->NewStringUTF("this is string from jni.");
}
同上篇文章讲过的,需要Android.mk和Application.mk文件,Android.mk的代码如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := JNI_STATIC_ANDROID_TEST
LOCAL_SRC_FILES =: jnistaticutils.cpp
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := JNI_DYNAMIC_ANDROID_TEST
LOCAL_SRC_FILES =: jnidynamicutils.cpp
include $(BUILD_SHARED_LIBRARY)
然后我们在jni目录下可以任意选择一个文件右键来执行ndk-build操作,在main/libs目录下生成了libJNI_DYNAMIC_ANDROID_TEST.so文件与libJNI_STATIC_ANDROID_TEST.so文件,我们复制去jniLibs目录下。
我们在Activity中分别调用测试(点击对应的按钮,分别执行不同的操作,将C++返回的字符串在界面上累加显示出来,代码简单,就不贴了):