内容简介: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 的好处:
- 提高代码的安全性
- 可以很方便地使用目前已有的 C/C++ 开源库
- 便于平台间移植
- 提高程序在某些特定情形下的执行效率
JNI 的开发流程
-
在 Java 中声明 native 方法。
public class JniTest { static { System.loadLibrary("jni-test"); // 加载动态库,库文件完整名为 libjni-test.so } ... public native String get(); public native void set(String str); }
-
编译 Java 源文件得到 class 文件,然后通过
javah
命令导出 JNI 头文件。javac .\com\liyu\JniTest.java javah com.liyu.JniTest
在当前目录自动产生一个
com_liyu_JniTest.h
的头文件。 - 实现 JNI 方法
- 编译 so 库并在 Java 中调用
NDK 的开发流程
本流程结合书籍和网络资料,当然也可以直接通过 Android Studio 新建 Project 时勾选 Include C++ support
,这时 Android Studio 会自动生成一个 JNI 示例项目。
- 下载并配置 NDK
-
创建一个 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); }
-
右键 app -> New -> Folder -> JNI Folder 创建 jni 目录
-
通过
javah
命令生成头文件至 jni 目录javah -d src/main/jni/ -classpath build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/ com.example.liyu.studytest.JniTest
-
实现所声明的 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)
-
-
切换到
jni
目录下,执行ndk-build
命令,即可生成对应平台的 so 库。 -
最后在
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 |
数组的签名
-
一维数组的签名就是
[+类型签名
如:
String[]
的签名为[Ljava/lang/String;
,byte[]
的签名为[B
-
多维数组的签名就是
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); }
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 编程范式 —— 函数式编程入门
- 【go网络编程】-HTTP编程
- 【go网络编程】-Socket编程
- c++并发编程—分布式编程
- Scala面向对象编程之Trait高级编程技术实践-JVM生态编程语言实战
- 函数式编程之数组的函数式编程
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。