NDK开发 从入门到放弃(二:动态注册JNI、多JNI调用)

来源:互联网 发布:知乎 霍顿 孙杨 编辑:程序博客网 时间:2024/05/24 02:27

一、前言

上一篇我们讲了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++之间的类型对应与字符串操作将在下一节讲解),我们现在希望当我们调用JNIDynamicUtilsgetHelloStringFromJNI方法时调用这个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_TESTLOCAL_SRC_FILES =: jnistaticutils.cppinclude $(BUILD_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := JNI_DYNAMIC_ANDROID_TESTLOCAL_SRC_FILES =: jnidynamicutils.cppinclude $(BUILD_SHARED_LIBRARY)

然后我们在jni目录下可以任意选择一个文件右键来执行ndk-build操作,在main/libs目录下生成了libJNI_DYNAMIC_ANDROID_TEST.so文件与libJNI_STATIC_ANDROID_TEST.so文件,我们复制去jniLibs目录下。
我们在Activity中分别调用测试(点击对应的按钮,分别执行不同的操作,将C++返回的字符串在界面上累加显示出来,代码简单,就不贴了):
这里写图片描述

0 0