如何在jni中注册native函数,有几种注册方式?

Java如何调用c、c++语言?

 

注册JNI函数的两种方法

静态方法

这种方法我们比较常见,但比较麻烦,大致流程如下:

  1. 先创建Java类,声明Native方法,编译成.class文件。
  2. 使用Javah命令生成C/C++的头文件,例如:javah -jni com.devilwwj.jnidemo.TestJNI,则会生成一个以.h为后缀的文件com_devilwwj_jnidemo_TestJNI.h。
  3. 创建.h对应的源文件,然后实现对应的native方法

 

这种方法的弊端:

  1. 需要编译所有声明了native函数的Java类,每个所生成的class文件都得用javah命令生成一个头文件。
  2. javah生成的JNI层函数名特别长,书写起来很不方便
  3. 初次调用native函数时要根据函数名字搜索对应的JNI层函数来建立关联关系,这样会影响运行效率

 

动态注册

我们知道Java Native函数和JNI函数时一一对应的,JNI中就有一个叫JNINativeMethod的结构体来保存这个对应关系,实现动态注册方就需要用到这个结构体。举个例子,你就一下子明白了:

1.声明native方法还是一样的:

public class JavaHello {
    public static native String hello();
}

​​​​​​2.创建jni目录,然后在该目录创建hello.c文件,如下:

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <jni.h>
#include <assert.h>

/**
 * 定义native方法
 */
JNIEXPORT jstring JNICALL native_hello(JNIEnv *env, jclass clazz)
{
    printf("hello in c native code./n");
    return (*env)->NewStringUTF(env, "hello world returned.");
}

// 指定要注册的类
#define JNIREG_CLASS "com/devilwwj/library/JavaHello"

// 定义一个JNINativeMethod数组,其中的成员就是Java代码中对应的native方法
static JNINativeMethod gMethods[] = {
    { "hello", "()Ljava/lang/String;", (void*)native_hello},
};

static int registerNativeMethods(JNIEnv* env, const char* className,
JNINativeMethod* gMethods, int numMethods) {
    jclass clazz;
    clazz = (*env)->FindClass(env, className);
    if (clazz == NULL) {
        return JNI_FALSE;
    }
    if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
        return JNI_FALSE;
    }
    return JNI_TRUE;
}

/***
 * 注册native方法
 */
static int registerNatives(JNIEnv* env) {
    if (!registerNativeMethods(env, JNIREG_CLASS, gMethods, sizeof(gMethods) / sizeof(gMethods[0]))) {
        return JNI_FALSE;
    }
    return JNI_TRUE;
}

/**
 * 如果要实现动态注册,这个方法一定要实现
 * 动态注册工作在这里进行
 */
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
    JNIEnv* env = NULL;
    jint result = -1;

    if ((*vm)-> GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        return -1;
    }
    assert(env != NULL);

    if (!registerNatives(env)) { //注册
        return -1;
    }
    result = JNI_VERSION_1_4;

    return result;

}

先仔细看一下上面的代码,看起来好像多了一些代码,稍微解释下,如果要实现动态注册就必须实现JNI_OnLoad方法,这个是JNI的一个入口函数,我们在Java层通过System.loadLibrary加载完动态库后,

  1. 紧接着就会去查找一个叫JNI_OnLoad的方法。如果有,就会调用它,而动态注册的工作就是在这里完成的。在这里我们会去拿到JNI中一个很重要的结构体JNIEnv,env指向的就是这个结构体,通过env指针可以找到指定类名的类,并且调用JNIEnv的RegisterNatives方法来完成注册native方法和JNI函数的对应关系。
  2. 我们在上面看到声明了一个JNINativeMethod数组,这个数组就是用来定义我们在Java代码中声明的native方法,我们可以在jni.h文件中查看这个结构体的声明:
typedef struct {
    const char* name;
    const char* signature;
    void*       fnPtr;
} JNINativeMethod;

结构体成员变量分别对应的是Java中的native方法的名字,如本文的hello;Java函数的签名信息、JNI层对应函数的函数指针。

 

区别:

  1. 静态注册
    优点: 理解和使用方式简单, 属于傻瓜式操作, 使用相关工具按流程操作就行, 出错率低
    缺点: 当需要更改类名,包名或者方法时, 需要按照之前方法重新生成头文件, 灵活性不高
  2. 动态注册
    优点: 灵活性高, 更改类名,包名或方法时, 只需对更改模块进行少量修改, 效率高
    缺点: 对新手来说稍微有点难理解, 同时会由于搞错签名, 方法, 导致注册失败

 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在 Android Native JNI,常用的获取 DNS 的方式有以下几种: 1. 使用系统函数 使用 `getaddrinfo()` 系统函数可以获取主机名对应的 IP 地址和端口号,其 `hints.ai_family` 参数可以指定协议簇,`AI_ADDRCONFIG` 表示只返回 IPv4 或 IPv6 地址,`AI_CANONNAME` 表示返回规范的主机名。 以下是获取主机名对应的 IPv4 和 IPv6 地址的代码: ```c++ #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> void getHostIP(const char *hostname, char *ipaddr) { struct addrinfo hints, *res, *p; int status; char ipstr[INET6_ADDRSTRLEN]; memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version hints.ai_socktype = SOCK_STREAM; if ((status = getaddrinfo(hostname, NULL, &hints, &res)) != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status)); return; } for (p = res; p != NULL; p = p->ai_next) { void *addr; char *ipver; // get the pointer to the address itself, // different fields in IPv4 and IPv6: if (p->ai_family == AF_INET) { // IPv4 struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr; addr = &(ipv4->sin_addr); ipver = "IPv4"; } else { // IPv6 struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr; addr = &(ipv6->sin6_addr); ipver = "IPv6"; } // convert the IP to a string and print it: inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr); printf("%s: %s\n", ipver, ipstr); strcpy(ipaddr, ipstr); } freeaddrinfo(res); // free the linked list } ``` 2. 使用系统属性 Android 系统的 `/system/etc/resolv.conf` 文件保存了 DNS 服务器的 IP 地址,可以通过读取系统属性 `net.dns{1,2,3,4}` 获取 DNS 服务器的 IP 地址。 以下是读取 DNS 服务器 IP 地址的代码: ```c++ #include <stdio.h> #include <stdlib.h> #include <string.h> #define DNS_PROP_NAME "net.dns" void getDnsServers(char *dns_servers[], int max_count) { char prop_name[16]; char prop_value[PROP_VALUE_MAX]; int i; for (i = 1; i <= max_count; i++) { sprintf(prop_name, "%s%d", DNS_PROP_NAME, i); if (property_get(prop_name, prop_value, NULL) > 0) { dns_servers[i - 1] = strdup(prop_value); printf("DNS Server %d: %s\n", i, dns_servers[i - 1]); } } } ``` 3. 使用第三方库 可以使用第三方库如 `libcurl` 等来获取 DNS 信息。 以下是使用 `libcurl` 获取主机名对应的 IPv4 和 IPv6 地址的代码: ```c++ #include <stdio.h> #include <string.h> #include <curl/curl.h> #define MAX_IP_LEN 64 struct MemoryStruct { char *memory; size_t size; }; static size_t writeMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) { size_t realsize = size * nmemb; struct MemoryStruct *mem = (struct MemoryStruct *) userp; mem->memory = (char *) realloc(mem->memory, mem->size + realsize + 1); if (mem->memory == NULL) { printf("writeMemoryCallback: realloc failed\n"); return 0; } memcpy(&(mem->memory[mem->size]), contents, realsize); mem->size += realsize; mem->memory[mem->size] = 0; return realsize; } void getHostIP(const char *hostname, char *ipaddr) { CURL *curl; CURLcode res; struct MemoryStruct chunk; chunk.memory = (char *) malloc(1); chunk.size = 0; curl = curl_easy_init(); if (!curl) { printf("getHostIP: curl_easy_init failed\n"); return; } curl_easy_setopt(curl, CURLOPT_URL, "https://dns.google/resolve?name=example.com&type=A"); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeMemoryCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &chunk); res = curl_easy_perform(curl); if (res != CURLE_OK) { printf("getHostIP: curl_easy_perform failed: %s\n", curl_easy_strerror(res)); return; } printf("getHostIP: response: %s\n", chunk.memory); // parse response to get IP address // ... strcpy(ipaddr, "192.168.1.1"); // example IP address free(chunk.memory); curl_easy_cleanup(curl); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sun cat

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值