美文网首页Android NDK 开发
NDK开发系列(一)——JNI

NDK开发系列(一)——JNI

作者: 耳_总 | 来源:发表于2017-11-30 16:26 被阅读24次

    最近会整理Android下NDK开发的一系列文章,从最基础的开始,由于本人水平有限,本系列文章为自己的总结,有什么错误的地方欢迎指正,另外,讲的也不会很深入,属于入门级别。

    什么是JNI?

    JNI是java native interface的缩写,意思是java本地接口。是java和C/C++相互调用的桥梁,JNI属于java的范畴,是JDK的一部分。Android基于java,当然 JNI也适用于Android,那什么是NDK呢?NDK是Android的本地开发工具包,在JNI的基础上,扩展了一些Android平台的API,还有一些C/C++编译工具,具体都放在SDK的ndk-build下面。

    问什么要用JNI
    • 提高性能
    • 为了安全考虑

    首先,在写学JNI之前要学C/C++,我也写了一系列文章,可以查看。这里讲的是在eclipse的java工程下使用JNI,打好基础。

    第一个hello world
    • 编写native方法
      要从java方法调用C/C++层,java申明的方法必须是native的。
    package com.xc;
    
    public class JNITest {
    
        public static void main(String[] args) {
        
        }   
    
        public native static String getStringFromC();
    }
    

    这里定义了一个native方法getStringFromC()视图从c层返回一个字符串

    • 生成native方法对应的头文件
      eclipse 环境下 cd 到你当前工程目录下的bin文件,里面有编译好的class文件。执行命令:

    javah xxx.xxx.xxx.JniTest

    必须是完成包名+类名
    比如我的上述代码中的工程:

    javah com.xc.JniTest

    然后在bin目录下面会生成与一个头文件。


    image.png

    注意:这里的java类名一定要命名规范,否则会出现.h方法没有一个方法定义。打开头文件看下里面的内容:

    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    /* Header for class com_xc_JniTest */
    
    #ifndef _Included_com_xc_JniTest
    #define _Included_com_xc_JniTest
    #ifdef __cplusplus
    extern "C" {
    #endif
    /*
     * Class:     com_xc_JniTest
     * Method:    getStringFromC
     * Signature: ()Ljava/lang/String;
     */
    JNIEXPORT jstring JNICALL Java_com_xc_JniTest_getStringFromC
      (JNIEnv *, jclass);
    
    #ifdef __cplusplus
    }
    #endif
    #endif
    

    关键代码:

    #include <jni.h>
    
    JNIEXPORT jstring JNICALL Java_com_xc_JniTest_getStringFromC
      (JNIEnv *, jclass); 
    

    定义的头文件,需要引入<jni.h>,这个头文件是JDK提供,对应的jni.c也是在JDK中实现,我们需要jni.h中定义的方法需要把头文件考到VS工程下面,我的jni.h路径:

    C:\Program Files\Java\jdk1.8.0_144\include

    由于jni.h中引入了jni_md.h,也需要把jni_md.h进行拷贝。
    说下头文件里面的方法,方法名需要严格按照一定格式拼接,在java native方法调用的时候会去寻找对应的c的方法。
    那么我们怎么写C代码然后打包给eclipse工程用呢?我是用的VS写的,由于在windows环境下,动态库为dll后缀,在Android、linux和macOS都是.a。
    在配置好VS环境之后,新建一个空的工程(这里的环境等等步骤省略)


    image.png

    建了一个a.c用来实现Java_com_xc_JniTest_getStringFromC方法:

    #include "com_xc_JNITest.h"
    
    JNIEXPORT jstring JNICALL Java_com_xc_JniTest_getStringFromC
    (JNIEnv *env, jclass jcls) {
    
        return (*env)->NewStringUTF(env,"hello world");
    };
    

    代码中JNIEnv 在c是一个二级指正,在c++中是一个一级指针,jstring为JNI的一个类型,返回java中的字符串,在C中要转为jstring需要用到NewStringUTF方法。
    然后在工程中生成dll动态链接库:


    image.png
    image.png

    在工程中就可以找到这个动态库了


    image.png
    • 将.dll拷贝到eclipse工程中:


      image.png

      然后加载动态库:

    public class JniTest {
    
        public static void main(String[] args) {
            System.out.println(getStringFromC());
        }
        
        public native static String getStringFromC();
    
        
        static {
            System.loadLibrary("Project1");
        }
    }
    

    需要把dll动态库配置到环境变量,然后重启eclipse就可以运行了

    总结一下:
    总的流程


    image.png
    • 在Java层编写native方法,并且把c/c++动态库加载到虚拟机中
    • javah命令建立一个对应的头文件。
    • 编写c/c++代码,实现头文件中的方法,在这期间要用到jni.h里面申明的代码,我们需要拷贝到工程中,jni的具体实现我们不用管,在JVM的JNI层。

    相关文章

      网友评论

        本文标题:NDK开发系列(一)——JNI

        本文链接:https://www.haomeiwen.com/subject/rjyvbxtx.html