android JNI NDK入门

1、JNI(Java Native Interface) Java本地接口,又叫Java原生接口。它允许Java调用C/C++的代码,同时也允许在C/C++中调用Java的代码。可以把JNI理解为一个桥梁,连接Java和底层

JNI的书写步骤如下:
a.编写带有native声明的方法的Java类
b.使用javac命令编译编写的Java类
c.使用java -jni ****来生成后缀名为.h的头文件
d.引入.h的头文件,使用其他语言(C、C++)实现本地方法
e.将本地方法编写的文件生成动态链接库

class HelloWorld
{
    //native声明,用于生成c/c++代码
    public native void sayHelloWorld();

    //加载c/c++编译好的库
    static
    {
        System.loadLibrary("Dll1");
    }

    public static void main(String[] args)
    {
        new HelloWorld().sayHelloWorld();
    }
}

在Androidstudio Terminal直接运行
运行指令: javah -jni 包名.类名(直接放入java文件夹内不用包名)

javah -jni HelloWorld
得到HelloWorld.h文件

/* DO NOT EDIT THIS FILE - it is machine generated */
#include 
/* Header for class HelloWorld */

#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     HelloWorld
 * Method:    sayHelloWorld
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_HelloWorld_sayHelloWorld
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

使用visual studio创建一个dll项目
引入HelloWorld.h实现jni接口

JNIEXPORT void JNICALL Java_HelloWorld_sayHelloWorld
  (JNIEnv *, jobject){

    printf("Hello World !");
    return;
}

生成dll包,我这边叫Dll1.dll。放到java目录,就可以实现打印hello world了

2,为了使您能够在 Android 应用中使用 C 和 C++ 代码,并提供众多平台库(arm64-v8a x86等),需要、Android NDK 就是一套工具集合配置才能打出Android 平台适用的so。

1、android端这边的jni接口

public class JNITools {
    static {
       System.loadLibrary("jniyjn");
    }

    //加法
    public static native int  add(int a,int b);

    //减法
    public static native int sub(int a,int b);

    //乘法
    public static native int mul(int a,int b);

    //除法
    public static native int div(int a,int b);
}

2、在main下创建一个jni文件夹

2.1创建Android.mk文件

Android.mk文件是用来指定诸如编译生成so库名、引用的头文件目录、需要编译的.c/.cpp文件和.a静态库文件等。

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := jniyjn

LOCAL_SRC_FILES := jnitools.c

LOCAL_LDLIBS :=  -L$(SYSROOT)/usr/lib -llog

include $(BUILD_SHARED_LIBRARY)

3、创建一个c++文件,并实现注册jni接口,并实现接口的功能

jni开发中C/C++ 之所以需要头文件(.h),有两个用处,一个是在开发编译的时候,在各个编译单元(Compile Unit)之间共享同样的定义;一个是在发布程序库的时候,让使用者知道调用接口。本次使用中我们没有使用java -jni ****来生成后缀名为.h的头文件,所以需要注册.

3.1 静态注册:

通过 JNIEXPORT 和 JNICALL 两个宏定义声明,在虚拟机加载 so 时发现上面两个宏定义的函数时就会链接到对应的 native 方法。

3.2 动态注册:

先通过JNI重载JNI_OnLoad()实现本地方法,然后直接在Java中调用本地方法。(当前例子通过动态注册,更灵活)

jintools.c文件

#include 
#include 
#include 
#include 


jint addNumber(JNIEnv *env,jclass clazz,jint a,jint b);

jint subNumber(JNIEnv *env,jclass clazz,jint a,jint b);

jint mulNumber(JNIEnv *env,jclass clazz,jint a,jint b);

jint divNumber(JNIEnv *env,jclass clazz,jint a,jint b);




JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved){

    //打印日志,说明已经进来了
    __android_log_print(ANDROID_LOG_DEBUG,"JNITag","enter jni_onload");

    JNIEnv* env = NULL;
    jint result = -1;

    // 判断是否正确
    if((*vm)->GetEnv(vm,(void**)&env,JNI_VERSION_1_6)!= JNI_OK){
        return result;
    }

    //注册四个方法,注意签名
    const JNINativeMethod method[]={
            {"add","(II)I",(void*)addNumber},
            {"sub","(II)I",(void*)subNumber},
            {"mul","(II)I",(void*)mulNumber},
            {"div","(II)I",(void*)divNumber}
    };

    //找到对应的JNITools类  com.example.myapplication
    jclass jClassName=(*env)->FindClass(env,"com/example/jniyjn/JNITools");

    //开始注册
    jint ret = (*env)->RegisterNatives(env,jClassName,method, 4);

     //如果注册失败,打印日志
    if (ret != JNI_OK) {
        __android_log_print(ANDROID_LOG_DEBUG, "JNITag", "jni_register Error");
        return -1;
    }

    return JNI_VERSION_1_6;
}


jint addNumber(JNIEnv *env,jclass clazz,jint a,jint b){
     return a+b;
}

jint subNumber(JNIEnv *env,jclass clazz,jint a,jint b){
     return a-b;
}

jint mulNumber(JNIEnv *env,jclass clazz,jint a,jint b){
     return a*b;
}

jint divNumber(JNIEnv *env,jclass clazz,jint a,jint b){
     return a/b;
}

4、Android 端使用

4.1 app下的buildgradle引入

defaultConfig下加入

        ndk{
            moduleName "jniyjn"
            abiFilters 'x86','armeabi-v7a','arm64-v8a'
            ldLibs "log"
        }

buildTypes下加入

        externalNativeBuild {
            ndkBuild {
                path 'src/main/jni/Android.mk'
            }
        }

4.2 调用

int result = JNITools.add(a, b);

5、第三方so的使用

常见的开源第三方c++代码,可以使用linux配置打包,打出相应平台的so文件和一个jar包。这里基本上第三方平台会把jni接口写在jar中,native的实现在so中。Android使用时只要直接引入so和jar既可以直接调用。

一般的开源c++项目已经写好jni接口并实现,直接打出so和jar,android引入既可用。

对于开源c++项目没有实现jni接口的,又想通过JAVA程序调用C/C++动态库的话,则需要使用SWIG,Simplified Wrapper and Interface Generator,这是一个封装C/C++动态库供其他编程语言调用的神器。

你可能感兴趣的:(android JNI NDK入门)