惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
爱范儿
爱范儿
J
Java Code Geeks
L
LangChain Blog
V
V2EX
大猫的无限游戏
大猫的无限游戏
S
SegmentFault 最新的问题
博客园 - Franky
Microsoft Azure Blog
Microsoft Azure Blog
Jina AI
Jina AI
Blog — PlanetScale
Blog — PlanetScale
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
The Cloudflare Blog
博客园 - 司徒正美
B
Blog
G
Google Developers Blog
Stack Overflow Blog
Stack Overflow Blog
罗磊的独立博客
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Apple Machine Learning Research
Apple Machine Learning Research
Engineering at Meta
Engineering at Meta
MyScale Blog
MyScale Blog
有赞技术团队
有赞技术团队
Hugging Face - Blog
Hugging Face - Blog

博客园 - 星辰之力

网站工具 Vue项目用于Ios和Android端开发 Android assets文件夹之位置放置和作用 轻松搭建Xposed Hook cordov vue项目中调用手机原生api Android 直接修改dex破解 Android 逆向实战篇(加密数据包破解) Android JNI中C调用Java方法 JNI C反射调用java方法 JNI NDK (AndroidStudio+CMake )实现C C++调用Java代码流程 Jni 线程JNIEnv,JavaVM,JNI_OnLoad(GetEnv返回NULL?FindClass返回NULL?) Jni OnLoad()和OnUnload() Android JNI之C/C++层调用JAVA Android NDK编译之undefined reference to 'JNI_CreateJavaVM' Android 开创java世界(JNI Invocation API) Android JNI c/c++调用java 无需新建虚拟机 cmake:善用find_package()提高效率暨查找JNI支持 如何解决用CMake未定义引用`JNI_CreateJavaVM'? [Linker error] undefined reference to `_imp__JNI_CreateJavaVM@12'
Android Studio NDK JNI动态注册本地方法
星辰之力 · 2019-03-27 · via 博客园 - 星辰之力

概述

可能大家觉得javah生成的函数名又臭又长,不太好看。这里可以提供另外一种方法来动态注册c++函数,让其根Java中的native方法关联起来。

实现

这里通过JNIEnv的Resisternatives方法完成方法的注册。相关方法介绍:

//方法映射描述结构体
typedef struct {
    const char* name;//Java方法名
    const char* signature;//方法签名
    void*       fnPtr;//C++ 方法指针
} JNINativeMethod;

//这是JNIEnv提供的注册本地方法
//clazz:方法对应的class
//methods:对应的方法数组指针
//nMethods:有几个方法
//返回值:注册成功返回JNI_OK
jint RegisterNatives(jclass clazz, const JNINativeMethod* methods,
        jint nMethods);

//当本地库被加载时VM调用JNI_OnLoad(例如,通过系统调用LoadLibrary)。JNI_OnLoad必须返回由本地库所需的JNI版本。
//为了使用任何新的JNI函数,一个本地库必须导出JNI_OnLoad函数并返回JNI_VERSION_1_2或更高的版本。
//如果本地库不导出JNI_OnLoad功能,VM假定库只需要JNI_VERSION_1_1版本。
//如果虚拟机不认JNI_OnLoad返回的版本号,本地库不能加载。
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved);

看了上面的函数,我们可以知道在loadLibrary的时候会首先调用JNI_OnLoad。因此打算在JNI_OnLoad中完成方法注册:

/filename:my.cpp
// Created by wastrel on 2016/9/8.
//
#include <stddef.h>
#include "jni.h"

//返回一个字符串
JNIEXPORT jstring JNICALL native_hello(JNIEnv *env, jclass clazz) {
    return env->NewStringUTF("Hello from C++");
}
//求两个int的值
JNIEXPORT jint JNICALL native_add(JNIEnv *env, jobject object, jint a, jint b) {
    return a + b;
}
//方法数组,JNINativeMethod的第一个参数是Java中的方法名,第二个参数是函数签名,第三个参数是对应的方法指针。
//Java方法的签名一定要与对应的C++方法参数类型一致,否则注册方法可能失败。
static JNINativeMethod method_table[] = {
        {"native_hello", "()Ljava/lang/String;", (void *) native_hello},
        {"native_add",   "(II)I",                (void *) native_add}
};

JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    //OnLoad方法是没有JNIEnv参数的,需要通过vm获取。
    JNIEnv *env = NULL;
    if (vm->AttachCurrentThread(&env, NULL) == JNI_OK) {
       //获取对应声明native方法的Java类
      jclass  clazz = env->FindClass("com/example/registerjni/HelloJNI");
        if (clazz == NULL) {
            return JNI_FALSE;
        }
        //注册方法,成功返回正确的JNIVERSION。
        if (env->RegisterNatives(clazz, method_table, sizeof(method_table)/ sizeof(method_table[0]))==JNI_OK) {
            return JNI_VERSION_1_4;
        }
    }
    return JNI_FALSE;
}

对应的native声明Java文件:

package com.example.registerjni;

/**
 * Created by wastrel on 2016/9/8.
 */
public class HelloJNI {
    static {
        System.loadLibrary("helloJNI");
    }

    public native static String native_hello();
    public native int native_add(int a,int b);
}
注意:如果你的方法声明了static,那么对应的第二个参数应该是jclass类型。如果你清楚他的实际类型,即便你写成jobject也不会引起程序错误,因为jclass本身也是jobject。但还是建议写为正确的方式,这样可以显得清晰一些。

注意:C++和Java有所不同,如果把static JNINativeMethod method_table 写在开头,你编译的时候会提示找不到函数指针,这是因为自上而下编译的原因。所以应该把定义写在实现方法后面,或者用一个头文件来完成函数的定义。

后记

这只是一种建立Native方法与Java方法的方式,如果没有特殊的需要,不建议使用这种方法来注册。因为使用这种优雅的注册方法,你必须确保你的函数方法和签名的正确性。这可能会增加出错的概率。