zoukankan      html  css  js  c++  java
  • Android项目中实现native调用

    转载自搜狗测试公众号,本人学习使用,侵权删

    最近小编在做公司输入法项目中java与native交互部分的测试,先简单学习了java代码调用native代码的实现原理,本次与大家一起分享jni协议,了解java关联C/C++代码的调用原则。

    JNI是Java Native Interface的缩写,能够提供API实现Java和Native语言(主要是C/C++)的通信,JNI提供两种方式实现Java对native代码的调用:静态关联和动态关联。

    静态关联

    静态关联的实现过程是通过经过特定规则命名的jni函数名来遍历java和jni函数之间的关联。具体分三步实现:

    1、java代码中声明native函数;

    2、通过javah生成native函数的jni形式;

    3、在jni代码中实现native函数。

    示例如下:

    1、实现一段java代码JNIUtils.java:

    package com.example.administrator.myapplication;

    public class JNIUtils {    
       static{        System.loadLibrary("native-lib");    }    
       public static native String sayHiFromJNI(); }

    JNIUtils.java代码包名为com.example.administrator.myapplication,声明了native函数名为sayHiFromJNI()。

    2、通过javah生成native函数的jni形式

    在代码的src/main/java目录下通过terminal端输入命令:javah -d ../jni com.example.administrator.myapplication.JNIUtils。通过Javah命令能够生成java类对应的头文件,命令-d表示生成一个目录,习惯上我们会将jni相关代码存放在java同级目录下的jni文件夹中(../jni),最后的com.example.administrator.myapplication.JNIUtils就是我们的JNIUtils完整类名了。

    执行后jni目录下会生成一个com.example.administrator.myapplication.JNIUtils.h文件,如下:

    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    /* Header for class com_example_administrator_myapplication_JNIUtils */

    #ifndef _Included_com_example_administrator_myapplication_JNIUtils
    #define _Included_com_example_administrator_myapplication_JNIUtils
    #ifdef __cplusplus
    extern "C" {
    #endif/* * Class:     com_example_administrator_myapplication_JNIUtils * Method:    sayHiFromJNI * Signature: ()Ljava/lang/String; */
    JNIEXPORT jstring JNICALL Java_com_example_administrator_myapplication_JNIUtils_sayHiFromJNI  (JNIEnv *, jclass);

    #ifdef __cplusplus
    }
    #endif
    #endif

    3、在jni代码中实现native函数

    在jni目录中新建cpp文件,命名JNIHi.cpp,在cpp文件中include "com_example_administrator_myapplication_JNIUtils.h"实现native函数的功能即可,在JNIUtils.java文件中我们定义了public static native String sayHiFromJNI();函数,因此在JNIHi.cpp中需要实现具体逻辑。

    代码如下:

    #include "com_example_administrator_myapplication_JNIUtils.h"

    JNIEXPORT jstring JNICALL Java_com_example_administrator_myapplication_JNIUtils_sayHiFromJNI        
           (JNIEnv *env, jclass jclass){    
       return env->NewStringUTF("Hi From JNI!!!");
    }

    如此便实现了JNIUtils.java代码中对C++代码JNIHi.cpp中函数的调用。

    动态关联

    静态关联的方法简单易学,但是是不是有人觉得函数名这么长,规范是否太繁琐,那么我们还有更简单的方式:动态关联。

    动态方式的主要实现原理是通过RegisterNatives函数把C/C++中的方法映射到Java中。

    1、编写java代码JNIUtils.java,与静态关联相同

    package com.example.administrator.myapplication;

    public class JNIUtils {    
       static{        System.loadLibrary("native-lib");    }    
       public static native String sayHiFromJNI(); }

    上述函数中我们使用System.loadLibrary("native-lib")方法加载so库的时候,Java虚拟机就会找到JNI_OnLoad函数并调用,该函数前面有三个关键字分别是JNIEXPORT,JNICALL ,jint。其中JNIEXPORT和JNICALL是两个宏定义,用于指定该函数是JNI函数,通过该函数能够实现java与native的动态关联,以代码示例。

    2、编写native关联代码JNIHi.cpp

    代码示例:

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

    using namespace std;
    #ifdef __cplusplus
    extern "C" {
    #endif

    static const char *className = "com/example/administrator/myapplication/JNIUtils";

    JNIEXPORT jstring JNICALL sayHiFromJNI(JNIEnv *env,jobject obj) {    
       return env->NewStringUTF("Hi From JNI!!!"); }

    static JNINativeMethod gJni_Methods_table[] = {        {"sayHiFromJNI", "()Ljava/lang/String;", (void*)sayHiFromJNI}, };

    JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved){    JNIEnv* env = NULL;    jint result = -1;    
       if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {        
           return result;    }    jclass clazz = (env)->FindClass( className);    
       if (clazz == NULL){        
           return -1;    }    
       
       if ((env)->RegisterNatives(clazz, gJni_Methods_table, sizeof(gJni_Methods_table) / sizeof(gJni_Methods_table[0])) < 0)    {        
           return -1;    }    
       
       return JNI_VERSION_1_4; }

    #ifdef __cplusplus
    }
    #endif

    通过代码阅读,我们发现JNI_OnLoad函数的实现主要包含两步:第一、vm->GetEnv()函数获取JNIEnv结构体指针,该指针指向一个函数表,对应JNI函数,我们可以通过这些JNI函数实现JNI编程;第二、RegisterNatives()函数实现native方法的注册,其中主要应用了一个静态变量JNINativeMethod类型的数组,它代表了native方法。JNINativeMethod结构被定义在jni.h中,Java与JNI可以通过该结构建立联系,如此Java虚拟机就可以用相应的函数映射表来调用相应的函数,而不需要通过函数名来查找需要调用的函数了。

    小结

    简而言之,静态关联:先由Java声明本地方法,然后通过JNI实现方法的定义。动态关联:先通过JNI_OnLoad实现本地方法,然后直接在Java中调用。两种方法各有优缺点,大家根据自己的代码习惯选择合适的方式就好。

  • 相关阅读:
    160922、配置:spring通过profile或@profile配置不同的环境(测试、开发、生产)
    160921、React入门教程第一课--从零开始构建项目
    160920、springmvc上传图片不生成临时文件
    160919、使用AOP与注解记录Java日志
    160918、BigDecimal运算
    160914、ionic指令简单布局
    Oracle 修改文件所有者
    Oracle 新增删除账户
    Oralce 账户被锁后的解决办法
    Linux Oracle 转换编码格式
  • 原文地址:https://www.cnblogs.com/Yanss/p/10182792.html
Copyright © 2011-2022 走看看