JNI动态注册

Android  NDK  2023年6月28日 am8:08发布1年前 (2023)更新 城堡大人
90 0 0

前言

之前其实写过,代码不见了,为了走一下流程,重新简单的写了一个。

正文

动态注册

动态注册java的Native方法,使得c/c++方法名可以和java的Native方法名可以不同。动态注册是将二者方法名关联起来,以后在修改Native方法名时,只需修改动态注册关联的方法名称即可。

  1. 优点: 灵活性高, 更改类名,包名或方法时, 只需对更改模块进行少量修改, 效率高

  2. 缺点: 对新手来说稍微有点难理解, 同时会由于搞错签名, 方法, 导致注册失败

实战

定义Hello.java
package com.biumall.dynamic.one;
public class Hello {
    // 1. load Hello.so
    static {
        System.loadLibrary("Hello");
    }
    // 2. define native hello()
    public static native String hello();
}

动态注册需要Java中native的Method(本地方法名)和Signature(签名)

对于Method(本地方法名),很简单就是上面hello。

当对于Signature(签名),需要通过一定的方式获取。

PS:自定义对象不行,需要自己看规则表《JNI之类型介绍

两种方式多需要先javac

#当前位置:BiuJniDynamic\src\main\java\com\biumall\dynamic\one
javac Hello.java

然后在进一步的获取需要的方法和签名

获取签名方式一

在javac基础上进行javap -s

#当前位置:BiuJniDynamic\src\main\java\com\biumall\dynamic\one
javap -s hello.class

命令行就会显示hello()方法的签名。

public static native java.lang.String hello();
  Signature: ()Ljava/lang/String;
获取签名方式二

方式跟静态注册一样,javac后,退回java所在目录,进行javah

#当前位置:BiuJniDynamic\src\main\java
javah com.biumall.dynamic.one.Hello

得到

com_biumall_dynamic_one_Hello.h

.h里面的方法上会有Method和Signature注释

/*
 * Class:     com_biumall_dynamic_one_Hello
 * Method:    hello
 * Signature: ()Ljava/lang/String;
 */

Hello.c

动态注册跟静态不一

系统初始化JNI在加载时,会调用JNI_OnLoad(),而卸载时会调用JNI_UnLoad();所以,我们可以通过重写JNI_OnLoad(),在JNI_OnLoad()中将函数注册到Android中,以便能通过Java访问。

下面是完整代码

#include <jni.h>
#include <android/log.h>
#include<stdio.h>

//LOG_TAG
#define LOG_TAG "from_dynamic_jni_"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

//获取数组大小
#define ARRAY_LENGTH(x) ((int)(sizeof(x) / sizeof((x)[0])))
//定义Hello.java类路径[包名+类名,只不过.换成了/]
//com.biumall.dynamic.one.Hello
#define DYNAMIC_CLASS "com/biumall/dynamic/one/Hello"

//native 方法
JNIEXPORT jstring JNICALL native_hello
  (JNIEnv * env , jclass jclazz){
    LOGE("native_hello()");
    return (*env)->NewStringUTF(env, "Hello World !!!! --- from JNI ");
};

//定义Java和JNI函数的绑定表
// 方法数组,分别为:(方法名[java层定义的],方法签名,函数指针[c层对应替换的方法])
// 可以通过javac和javap 获取 方法签名
JNINativeMethod method_table[]= {
        {"hello", "()Ljava/lang/String;", (void*) native_hello},
};

int registerNativeMethods(JNIEnv *env, const char *className, JNINativeMethod * methods, int methods_size){
    jclass clazz = NULL;
    //反射Java类
    clazz = (*env)->FindClass(env, className);
    if(NULL == clazz){
        return JNI_ERR;
    }
    int result = (*env)->RegisterNatives(env, clazz, methods, methods_size);
    LOGD("--------registerNativeMethods---------- result : %d", result);
    if(result < 0){
        return JNI_ERR;
    }
    return JNI_OK;
}

JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved){
    JNIEnv * env = NULL;
    int result = (*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_6);
    LOGD("--------JNI_OnLoad----------1 result : %d", result);
    if(result != JNI_OK){
    return JNI_ERR;
    }
    result = registerNativeMethods(env , DYNAMIC_CLASS, method_table, ARRAY_LENGTH(method_table));
    LOGD("--------JNI_OnLoad----------2 result : %d", result);
    if(result != JNI_OK){
    return JNI_ERR;
    }
    return JNI_VERSION_1_6;
}

上面大部分有注释,这里就不重复了。

至于其他的Application.mk,Android.mk,ndk-build和build.gradle配置跟静态注册一样,这里就懒得重复了。

可以看最近整理的《JNI静态注册

参考文章

  1. [NDK开发]Android JNI开发之动态注册

  2. [NDK开发]Android JNI 中新增JNI层日志打印

  3. [NDK开发]Android JNI 开发之静态注册

  4. JNI静态注册

 历史上的今天

  1. 2024: 丰子恺:人生三十即是秋(0条评论)
  2. 2020: Glide V4和V3 使用不同(0条评论)
  3. 2018: PhoneStatusBar启动分析(0条评论)
版权声明 1、 本站名称: 笔友城堡
2、 本站网址: https://www.biumall.com/
3、 本站部分文章来源于网络,仅供学习与参考,如有侵权,请留言

暂无评论

暂无评论...

随机推荐

jaudiotagger解析ID3第二版

前言上一个版本《jaudiotagger解析ID3信息》,功能可以,但性能一般,因此这里之前的基础上优化一个版本。PS:推荐看jaudiotagger源码,我这只是个人流水账。正文导入jaudiotagger.jar导入lib库/libs/jaudiotagger.jarbuil...

Android数据库查询记录

前言记录一下数据库查询,模糊查询等Java方法,方便自己查阅。正文查询Android多媒体下面是查询audio文件隐藏内容!付费阅读后才能查看!¥2 ¥3多个隐藏块只需支付一次付费阅读匹配查询数据查询可以分为精准匹配查询,也可以模糊匹配查询。like 模糊查询隐藏内容!付费阅读后...

周国平:时光村落里的往事

一人分两种,一种人有往事,另一种人没有往事。有往事的人爱生命,对时光流逝无比痛惜,因而怀着一种特别的爱意,把自己所经历的一切珍藏在心灵的谷仓里。世上什么不是往事呢?此刻我所看到、听到、经历到的一切,无不转瞬即逝,成为往事。所以,珍惜往事的人便满怀爱怜地注视一切,注视即将被收割的麦田,正在落叶的...

MediaPlayer播放不同来源的音频文件

前言对MediaPlayer很熟悉了,但也是熟悉部分的接口,加上记忆力不给力,总是忘记。这里简单记录一下MediaPlayer播放不同来源的音频文件的方法。正文隐藏内容!评论可看后才能查看!评论可看参考文章

余华:麦田里

我在南方长大成人,一年四季、一日三餐的食物都是大米,由于很少吃包子和饺子,这类食物就经常和节日有点关系了。小时候,当我看到做外科医生的父亲手里提着一块猪肉,捧着一袋面粉走回家时,我就知道这一天是什么日子了。在我小时候有很多节日,五月一日是劳动节,六月一日是儿童节,七月一日是建党节,八月一日是建军节,...

adb shell top 命令的简单使用

前言top命令提供了实时的对Android系统处理器的状态监视.它将显示系统中CPU最“敏感”的任务列表.该命令可以按CPU使用.内存使用和执行时间对任务进行排序.adb shell top 的简介top 可以带参数等,具体下面详细简介top 用法使用adb shell top --he...