JNI 和 NDK 编程

栏目: Java · 发布时间: 5年前

内容简介:JNI 全称 Java Native Interface,它是为了方便 Java 调用 C、C++ 等本地代码所封装的一层接口。NDK 是 Android 所提供的一个工具集合,通过 NDK 可以在 Android 中更加方便的用过 JNI 来访问本地代码。此外 NDK 还提供了交叉编译器,只需简单的配置即可生成特定 CPU 平台的动态库。使用 NDK 的好处:

JNI 全称 Java Native Interface,它是为了方便 Java 调用 C、C++ 等本地代码所封装的一层接口。

NDK 是 Android 所提供的一个 工具 集合,通过 NDK 可以在 Android 中更加方便的用过 JNI 来访问本地代码。此外 NDK 还提供了交叉编译器,只需简单的配置即可生成特定 CPU 平台的动态库。

使用 NDK 的好处:

  1. 提高代码的安全性
  2. 可以很方便地使用目前已有的 C/C++ 开源库
  3. 便于平台间移植
  4. 提高程序在某些特定情形下的执行效率

JNI 的开发流程

  1. 在 Java 中声明 native 方法。

    public class JniTest {
    
        static {
            System.loadLibrary("jni-test"); // 加载动态库,库文件完整名为 libjni-test.so
        }
        ...
        public native String get();
    
        public native void set(String str);
    }
    
  2. 编译 Java 源文件得到 class 文件,然后通过 javah 命令导出 JNI 头文件。

    javac .\com\liyu\JniTest.java
    javah com.liyu.JniTest
    

    在当前目录自动产生一个 com_liyu_JniTest.h 的头文件。

  3. 实现 JNI 方法
  4. 编译 so 库并在 Java 中调用

NDK 的开发流程

本流程结合书籍和网络资料,当然也可以直接通过 Android Studio 新建 Project 时勾选 Include C++ support ,这时 Android Studio 会自动生成一个 JNI 示例项目。

  1. 下载并配置 NDK
  2. 创建一个 Android 项目,并声明所需的 native 方法

    package com.example.liyu.studytest;
    
    public class JniTest {
    
        static {
            System.loadLibrary("jni-test");
        }
    
        public native String get();
    
        public native void set(String str);
    
    }
    
  3. 右键 app -> New -> Folder -> JNI Folder 创建 jni 目录

  4. 通过 javah 命令生成头文件至 jni 目录

    javah -d src/main/jni/ -classpath build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/ com.example.liyu.studytest.JniTest
    
  5. 实现所声明的 native 方法

    • 创建 test.cpp 实现头文件中的方法

      #include <jni.h>
      #include <stdio.h>
      
      #ifdef __cplusplus
      extern "C" {
      #endif
      
      jstring Java_com_example_liyu_studytest_JniTest_get(JNIEnv *env, jobject thiz) {
          printf("invoke get in c++\n");
          callJavaMethod(env, thiz);
          return env->NewStringUTF("Hello from JNI in libjni-test.so !");
      }
      
      void Java_com_example_liyu_studytest_JniTest_set(JNIEnv *env, jobject thiz, jstring string) {
          printf("invoke set from C++\n");
          char* str = (char*)env->GetStringUTFChars(string,NULL);
          printf("%s\n", str);
          env->ReleaseStringUTFChars(string, str);
      }
      
      #ifdef __cplusplus
      }
      #endif
      
    • 创建 Application.mk 文件

      APP_MODULES := jni-test
      APP_ABI :=all
      
    • 创建 Android.mk 文件

      LOCAL_PATH := $(call my-dir)
      
      include $(CLEAR_VARS)
      
      LOCAL_MODULE    := jni-test
      LOCAL_SRC_FILES := test.cpp
      
      include $(BUILD_SHARED_LIBRARY)
      
  6. 切换到 jni 目录下,执行 ndk-build 命令,即可生成对应平台的 so 库。

  7. 最后在 app/src/main 中新建一个名为 jniLibs 的目录,将生成的 so 库复制进去即可。( jniLibs 的目录是 Android Studio 所能识别的默认目录,也可修改)

以上为手动创建的步骤,其实还可以使用 Android Studio 来自动编译产生 so 库,需要修改 build.gradle

android {
    ...
    defaultConfig {
        ...
        ndk {
            moduleName "jni-test"
        }
    }

    sourceSets.main {
        jni.srcDirs 'src/main/jni'
    }

}

如果只打包特定平台的 so 库,也需要修改 build.gradle

android {
    ...
    defaultConfig {
        ...
        ndk {
            abiFilters "armeabi", "armeabi-v7a"
        }
    }
}

JNI 的数据类型和类型签名

基本类型

JNI 类型 Java 类型 描述
jbyte byte 有符号,8 位,整型
jshort short 有符号,16 位,整型
jint int 有符号,32 位,整型
jlong long 有符号,64 位,整型
jfloat float 32 位,浮点型
jdouble double 64 位,浮点型
jboolean boolean 无符号,8 位,整型
jchar char 无符号,16 位,整型
void void 无类型

引用类型

JNI 类型 Java 类型 描述
jclass Class
jobject Object Java 对象
jstring String 字符串
jobjectArray Object[] 对象数组
jbyteArray byte[] byte 数组
jshortArray short[] short 数组
jintArray int[] int 数组
jlongArray long[] long 数组
jfloatArray float[] float 数组
jdoubleArray double[] double 数组
jbooleanArray boolean[] boolean 数组
jcharArray char[] char 数组
jthrowable Throwable Throwable

类型签名

JNI 的类型签名标识了一个特定的 Java 类型,这个类型可以是类、方法或数据类型。

类和对象的签名

采用 L+包名+类名+; 的形式,且将包名中的 . 替换为 /

如: java.lang.String 的签名为 Ljava/lang/String;

基本数据类型的签名

Java 类型 签名
byte B
short S
int I
long J
float F
double D
boolean Z
char C
void V

数组的签名

  1. 一维数组的签名就是 [+类型签名

    如: String[] 的签名为 [Ljava/lang/String;byte[] 的签名为 [B

  2. 多维数组的签名就是 n个[+类型签名

    如: int[][] 的签名为 [[I

方法的签名

方法的签名为: (各参数类型签名) + 返回值类型签名

int func1() 的签名为 ()I
void func2(int i) 的签名为 (I)V
boolean func3(int a, double b, String[] c) 的签名为 (ID[Ljava/lang/String;)Z

JNI 调用 Java 方法的流程

JNI 调用 Java 静态方法的流程是先找到类,然后根据方法名找到方法的 id,最后就可以调用这个方法了。调用非静态方法时需要先构造出类的对象后才能调用它。

下面是一个 JNI 调用 Java 静态方法的示例:

void callJavaMethod(JNIEnv *env, jobject thiz) {
    // 获取 jclass
    jclass clazz = env->GetObjectClass(thiz);
    // 根据类、方法名以及方法签名获取静态方法的 ID
    // 非静态方法的 ID 通过 GetMethodID 获取,参数一致
    jmethodID id = env->GetStaticMethodID(clazz, "methodCalledByJni", "(Ljava/lang/String;)V");
    if (id == NULL) {
        printf("find method methodCalledByJni error!");
    }
    // String 参数需要通过 NewStringUTF 生成 jstring 使用,否则会报错
    jstring msg = env->NewStringUTF("msg send by callJavaMethod in test.cpp.");
    // 非静态方法通过 CallVoidMethod 调用,参数一致
    env->CallStaticVoidMethod(clazz, id, msg);
}

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

STL源码剖析

STL源码剖析

侯捷 / 华中科技大学出版社 / 2002-6 / 68.00元

学习编程的人都知道,阅读、剖析名家代码乃是提高水平的捷径。源码之前,了无秘密。大师们的缜密思维、经验结晶、技术思路、独到风格,都原原本本体现在源码之中。 这本书所呈现的源码,使读者看到vector的实现、list的实现、heap的实现、deque的实现、Red Black tree的实现、hash table的实现、set/map的实现;看到各种算法(排序、查找、排列组合、数据移动与复制技术......一起来看看 《STL源码剖析》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具