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

一、前言

上一篇我们讲了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_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++返回的字符串在界面上累加显示出来,代码简单,就不贴了):
这里写图片描述

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值