容桂品牌网站建设优惠,ppt免费下载完整版免费下载,wordpress浏览量插件,学seo如何入门引言
在Android开发中#xff0c;Java是一种主要的编程语言#xff0c;然而#xff0c;对于一些性能要求较高的场景#xff08;如音视频处理、图像处理、计算密集型任务等#xff09;#xff0c;我们可能需要使用到C或C等语言来编写底层的高效代码。为了实现Java代码与C…
引言
在Android开发中Java是一种主要的编程语言然而对于一些性能要求较高的场景如音视频处理、图像处理、计算密集型任务等我们可能需要使用到C或C等语言来编写底层的高效代码。为了实现Java代码与C/C代码之间的交互Android提供了一个强大的工具——JNIJava Native Interface。通过JNIJava可以调用C/C代码C/C也可以调用Java代码从而实现高效的原生交互。 开始之前先了解一些基础概念 开始之前
如果你对C/C语言比较陌生可以先看一下我的这两篇文章 大致过一下就行挑重点去记毕竟不是做C开发没必要完全理解更多的是我们在开发中去学习
C语言基础C基础
1. 什么是 JNIJava Native Interface JNI 是 Java 与其他编程语言通常是 C 或 C之间的接口允许 Java 代码与底层的本地代码进行交互。通过 JNI我们可以在 Java 代码中调用本地native方法或者让本地代码调用 Java 方法。
1.1 为什么要使用 JNI
JNI 的主要作用是实现 Java 程序与本地程序之间的交互特别是在以下几种情况下非常有用
性能优化有些运算或操作Java 实现的效率可能较低使用 C/C 可以提高性能特别是在图像处理、音视频编解码等领域。访问底层硬件或特性Java 不能直接访问底层硬件或操作系统的某些特性而 JNI 使得 Java 程序可以调用 C/C 中的底层代码进而访问这些特性。重用现有的本地代码库有时为了节省开发时间我们希望直接重用一些已有的 C/C 代码或第三方库这时 JNI 就是连接 Java 和本地代码的桥梁。
1.2 JNI 如何工作
JNI 的工作机制可以分为几个步骤
Java 调用 C/C 方法通过在 Java 中声明本地方法native并使用 System.loadLibrary() 加载本地库。Java 代码通过 JNI 机制调用底层的 C/C 函数。C/C 调用 Java 方法JNI 允许在 C/C 中调用 Java 中的方法甚至可以操作 Java 对象。数据传递通过 JNIJava 和 C/C 之间可以传递基本数据类型如整数、浮点数和复杂的数据结构如数组、对象等。
1.3 JNI 的基本结构
Java 层Java 中声明 native 方法并通过 System.loadLibrary() 加载本地库。本地层通过 C/C 实现 JNI 接口并将它编译成共享库.so 文件。JNI 头文件使用 javah 工具或者在 Android 中通过 ndk-build生成的头文件定义了 Java 类与本地方法之间的映射关系。
2. NDK 与 JNI 的关系 在 Android 开发中NDKNative Development Kit是一个工具集它允许开发者在 Android 应用中编写和使用 C/C 代码。JNI 是 NDK 的一部分它提供了 Android 中 Java 代码和 C/C 本地代码之间的交互接口。
2.1 NDK 的功能
NDK 是一组工具和库允许开发者用 C 和 C 编写 Android 应用中的一些性能关键的代码。NDK 提供的功能包括
访问硬件资源通过 NDK你可以直接访问一些低级的硬件特性比如摄像头、传感器、GPS 等。性能优化一些计算密集型的任务例如图像处理、音视频编解码等可以通过 C/C 实现性能上更有优势。使用已有的本地库有时候开发者会利用一些已有的 C/C 库或第三方库而这些库通常需要通过 NDK 来编译和链接。
2.2 NDK 与 JNI 的结合
JNI 是 NDK 与 Java 层之间的桥梁利用 JNIJava 层可以调用本地层的 C/C 函数反之C/C 代码也可以调用 Java 层的代码。使用 NDK 时JNI 使得 Java 和 C/C 之间的数据和方法调用变得可能。通过 JNI我们可以在 Java 代码中调用 NDK 中编写的本地方法或者直接操作 Java 对象。
3. 数据类型 Java、JNI、C/C 三者之间的数据类型转换是跨语言编程中的一个核心问题尤其在涉及到 Java 调用 C/C 编写的本地方法时。JNIJava Native Interface作为 Java 与 C/C 交互的桥梁提供了一套标准机制来实现 Java 与本地代码之间的数据交换。
3.1 基础类型
Java 通过 JNI 与 C/C 交互时JNI 提供了一些专门的类型和方法来桥接 Java 类型与 C/C 类型的差异。
Java 类型JNI 类型C/C 类型备注bytejbytechar (8-bit)JNI 使用 jbyte 来表示 Java 的 byte 类型。shortjshortshort (16-bit)JNI 使用 jshort 来表示 Java 的 short 类型。intjintint (32-bit)JNI 使用 jint 来表示 Java 的 int 类型。longjlonglong long (64-bit)JNI 使用 jlong 来表示 Java 的 long 类型。floatjfloatfloat (32-bit)JNI 使用 jfloat 来表示 Java 的 float 类型。doublejdoubledouble (64-bit)JNI 使用 jdouble 来表示 Java 的 double 类型。charjcharwchar_t (16-bit)JNI 使用 jchar 来表示 Java 的 char 类型它是 16 位 Unicode 字符C/C 中通常用 wchar_t 来表示宽字符。booleanjbooleanbool (1-bit)JNI 使用 jboolean 来表示 Java 的 boolean 类型jboolean 是 8 位的布尔值通常与 C/C 中的 bool 类型兼容。
3.2 引用类型
Java 对象类型通常通过 JNI 提供的 API 转换为 C/C 中的指针类型这些指针类型并不代表实际的数据内容而是用于访问 Java 对象或方法的接口。
Java 类型JNI 类型C/C 类型转换方式JNI API 示例StringjstringjstringJava String 到 C/C 的转换通过 GetStringUTFChars 或 GetStringCharsenv-GetStringUTFChars(jstring, nullptr)ObjectjobjectjobjectJava 对象到 C/C 的转换可以用来操作任意 Java 对象env-GetObjectClass(jobject)ClassjclassjclassJava Class 对象到 C/C 的转换通过 FindClass 或 GetObjectClass 获取类引用env-FindClass(java/lang/String)Array (Object)jobjectArrayjobjectArray对象数组到 C/C 的转换通过 JNI API 访问数组元素env-GetObjectArrayElement(jobjectArray, index)Array (Primitive)jintArrayjintArray基本类型数组转换如 int[] 到 jintArrayenv-GetIntArrayElements(jintArray, nullptr)FieldjfieldIDjfieldID通过 JNI 获取字段 ID通常用于访问 Java 类中的字段env-GetFieldID(jclass, fieldName, I)MethodjmethodIDjmethodID通过 JNI 获取方法 ID通常用于调用 Java 方法env-GetMethodID(jclass, methodName, ()V)
4. JNI 中的 Java 签名信息 在学习签名之前先来看一段Java反射代码
import java.lang.reflect.Method;public class ReflectionExample {public void sayHello(String name) {System.out.println(Hello, name);}public static void main(String[] args) throws Exception {// 获取 ReflectionExample 类的 Class 对象Class? clazz Class.forName(ReflectionExample);// 获取方法 sayHello(String)Method method clazz.getMethod(sayHello, String.class);// 创建实例并调用方法Object instance clazz.getDeclaredConstructor().newInstance();method.invoke(instance, World);}
}在clazz.getMethod中我们通过方法名称 和 参数类型拿到了sayHello方法在JNI中C/C 调用Java的方法也类似不同点是参数类型 和 返回值 要用签名方式代替因为C/C不能直接拿到Java方法嘛那么JNI中签名长什么样呢
4.1 基本数据类型的签名
Java 中的基本数据类型对应 JNI 中的签名符号。JNI 使用单一字符来表示 Java 中的基本数据类型。
Java 类型JNI 签名booleanZbyteBcharCshortSintIlongJfloatFdoubleDvoidV
4.2 对象类型的签名
Java 对象类型类类型、接口类型等的签名格式如下
以 L 开始后接类的全名包括包名最后以 ; 结尾。例如String 类型的签名为 Ljava/lang/String;。注意数组类型的签名也以 [ 开头并且每增加一个维度就多一个 [。
Java 类型JNI 签名StringLjava/lang/String;ObjectLjava/lang/Object;int[][IString[][Ljava/lang/String;Object[][Ljava/lang/Object;
4.3 方法签名
Java 方法的签名由两部分组成方法的参数类型和返回类型方法签名的格式为(参数类型1, 参数类型2, ...)返回类型。例如一个有两个 int 参数并返回 String 类型的方法签名为 (II)Ljava/lang/String;。
Java 方法JNI 签名int add(int a, int b)(II)IString getName(String name)(Ljava/lang/String;)Ljava/lang/String;void setValues(int x, int y)(II)V
4.4 构造函数签名
Java 构造函数的签名与普通方法类似不同之处在于构造函数没有返回类型V且通常没有方法名。在 JNI 中构造函数的签名格式是 (参数类型1, 参数类型2, ...)V。
Java 构造函数JNI 签名MyClass(int, String)(ILjava/lang/String;)V
4.5 静态方法签名(重点)
静态方法的签名与实例方法类似唯一的区别是静态方法是类级别的因此它通过类的对象引用来调用。静态方法的签名与实例方法的签名相同但 JNI 调用时不需要实例对象。 没必要死记硬背有规律的写两遍就记住了 4.6 示例
(1) 获取 Java 方法签名
GetMethodID 或 GetStaticMethodID,拿到相应的方法。
jmethodID methodId env-GetMethodID(clazz, methodName, (I)Ljava/lang/String;);这个方法的签名为 (I)Ljava/lang/String;表示该方法有一个 int 类型的参数返回一个 String 类型。
(2) 获取字段签名 GetFieldID 或 GetStaticFieldID,拿到类的属性字段。
jfieldID fieldId env-GetFieldID(clazz, fieldName, Ljava/lang/String;);这个字段的签名为 Ljava/lang/String;表示它是一个 String 类型的字段。
(3) 构造函数签名 通过签名和构造函数名称查找类的构造函数 ID。构造函数的签名与普通方法相同但没有返回类型。
jmethodID constructorId env-GetMethodID(clazz, init, (I)V);构造函数的签名为 (I)V表示它接受一个 int 类型的参数并没有返回值。
5. 在Android中使用JNI 5.1 配置项目
在build.gradle包含对NDK的支持:
android {...defaultConfig {...externalNativeBuild {cmake {cppFlags }}}externalNativeBuild {cmake {path CMakeLists.txt}}
}5.2 编写Java代码
在Java代码中声明本地方法:
public class NativeLib {static {System.loadLibrary(native-lib);}public native String stringFromJNI();
}5.3 编写C/C代码
在cpp目录下创建对应的C/C文件实现上述声明的本地方法
#include jni.h
#include stringextern C JNIEXPORT jstring JNICALL
Java_com_example_myapplication_NativeLib_stringFromJNI(JNIEnv* env, jobject /* this */) {std::string hello Hello from C;return env-NewStringUTF(hello.c_str());
}5.4 配置CMakeLists.txt
在项目的根目录下配置CMakeLists.txt 如
cmake_minimum_required(VERSION 3.4.1)add_library(native-libSHAREDsrc/main/cpp/native-lib.cpp)find_library(log-liblog)target_link_libraries(native-lib${log-lib})如果你项目中想写多个.cpp文件CMakeLists.txt xiugai配置如下
cmake_minimum_required(VERSION 3.4.1)add_library(native-libSHAREDsrc/main/cpp/native-lib.cpp)add_library(native-lib2SHAREDsrc/main/cpp/native-lib2.cpp)//更多...find_library(log-liblog)target_link_libraries(native-lib${log-lib})target_link_libraries(native-lib2${log-lib})//更多...即在 find_library 和 target_link_libraries 增加相对应的.cpp文件即可。
6. 实战 因为在写这篇文章之前我已经完善了一些实战的功能在此就不一一讲解了包括
传递int数据传递String数据传递Array数据在C中调用Java的返回值Void方法在C中调用Java的返回值int方法在C中调用Java的返回值String方法在C中显示Toast文本加解密演示锅炉压力进度条C 创建子线程C 线程锁之生产者消费者串口通信(SerialPort) - 可拿来直接使用已验证功能。
代码已经上传GithubJNIStudy感兴趣的可以下载看看里面我加了世上最全注释由基础到复杂看不懂来打我
打包为.so文件可以看我的这篇文章在Android中将 .cpp 文件编译成共享库.so 文件
7. 最后 之前一直对JNI望而却步真正学过后回头看看也不是那么的难难的是你不主动去学。所有伟大都源于一个勇敢的开始共勉
另外给喜欢记笔记的同学安利一款好用的云笔记软件对比大部分国内的这个算还不错的免费好用wolai