内容简介: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生态编程语言实战
- 函数式编程之数组的函数式编程
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Looking For a Challenge
the University of Warsaw / the University of Warsaw / 2012-11 / 0
LOOKING FOR PROGRAMMING CHALLENGES? Then this book is for you! Whether it's the feeling of great satisfaction from solving a complex problem set, or the frustration of being unable to solve a task,......一起来看看 《Looking For a Challenge》 这本书的介绍吧!