JNI 和 NDK 编程

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

内容简介: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);
}

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

查看所有标签

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

IT不再重要

IT不再重要

(美)尼古拉斯·卡尔 / 闫鲜宁 / 中信出版社 / 2008-10 / 29.00元

在这部跨越历史、经济和技术领域的著作中,作者从廉价的电力运营方式对社会变革的深刻影响延伸到互联网对我们生活的这个世界的重构性影响。他批判式的认为,企业想应用网络或应用程序,不再需要自建资料中心、自组IT团队维护和管理系统,因为互联网就像自来水或电力一样,可由专门公司提供服务,你可以付费使用。而如果他的设想真的会实现,我们的世界将会变成什么样子?IT产业的命运又将如何?这又对企业的IT领域投资产生什......一起来看看 《IT不再重要》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

URL 编码/解码
URL 编码/解码

URL 编码/解码