【NDK】【004】JNI调用dll动态库
JNI调用C++规则使用IntelliJ Idea和CLion开发JNI源代码
JNI调用C++规则
- Java方法前要用native关键字修饰,并且只有方法声明,没有实现,因为实现代码在C++里面
- C++方法前要用JNIEXPORT和JNICALL宏修饰
- C++方法中的数据类型,要使用与Java数据类型相对应的JNI类型
- C++方法名必须完全符合Java_JavaPackageName_JavaClassName_JavaFunctionName的格式
- Java中的native方法可以是static的,也可以是static的,只要方法名匹配即可
使用IntelliJ Idea和CLion开发JNI
从零开始搭建并运行一个JNI项目并不难
但对于新手来说,每个小细节都可能出错,靠自己尝试出来可能要浪费大量时间
所以这里还是不辞繁琐,细讲一遍
本次使用的开发工具是IntelliJ Idea和CLion,它们分别是Java和C++的开发环境
我们以后的NDK开发,和这个流程基本一致,只不过NDK所有工作都可以在AndroidStudio中完成
Android项目比较复杂,而我们现在演示的JNI项目则十分简单,除了必需的代码文件,几乎没什么多余的干扰信息
这样更方便我们弄清Java/C++混编背后的运作原理
虽然dll开发不是安卓开发者必须掌握的,但这其中包含了很多共通性的原理,对大家长远发展很有帮助
工作中时常会看到很多人,只要一接触到复杂点的开发工具,编译环境,或是编译失败,就开始烦躁放弃
其实这就是平时只关心业务功能实现,对软件的编译、运行、协作原理了解太少的原因
由于不了解通用原理,对新事物的分析解决能力很弱,更别提自己去对开发环境进行设计或优化了
回到正题,我们开始讲解,如何从零开始,搭建并运行一个JNI项目
在IntelliJ Idea中创建一个空的Java项目,创建一个类文件,在其中添加我们想要的native方法
IntelliJ Idea中的蓝色文件夹表示源码目录,黄色文件夹表示编译配置等非代码目录
我们可以看到,这个项目就一个src文件夹放源码,一个libs文件夹放dll库,结构非常简单
由于JNI对C++文件格式的要求比较复杂,我们自己生成比较麻烦且容易出错,所以需要借助工具来完成
JDK默认就提供了一个javah指令,用于根据Java文件中的native接口,直接生成C++头文件
我们在src文件夹处右键,打开Terminal控制台,然后执行javah指令
Terminal是IntelliJ Idea内战的快捷控制台,可以在里面执行cmd指令
我们在src处打开Terminal,cmd默认就会自动切换到src目录处,十分方便
注意javah指令的执行目录,还有类名要包括包名,这两点都不能错
我们已经得到了C++头文件,接下来我们要实现头文件中的接口方法,并编译成dll文件
由于头文件中用到的数据类型都是JNI类型,这在原生C++中是没有的,所以我们要把定义JNI数据类型的文件一起拷贝到C++工程中
JNI数据类型的定义需要使用jni.h和jni_md.h这两个文件,可以在jdk/include目录下找到
在CLion中创建一个新的C++ Libarary工程,将生成的JNI头文件和JNI数据类型定义文件一起拷入
创建一个cpp文件,实现JNI头文件中定义的方法
如图所示,C++工程和上面的Java工程一样,结构非常简单
到此为止,C++代码已经编写完成了,接着我们要将C++代码编译为dll文件
由于我们添加了新的代码文件,所以需要修改编译脚本,即CMakeLists.txt中的编译文件列表
Build - Rebuild Project,编译dll文件
现在只要把dll文件拷到Java项目里面就可以运行了
dll存放的位置是和工程配置有关系的,如果没有修改过配置,默认放到工程根目录下就行了
dll全部放在外面看起来很乱,如果我们想把dll全部放到libs文件夹下面该怎么设置呢?
网上有的教程又会叫我们把dll放到JDK或C盘下面,为什么这样也可以?
为什么有时明明按照网上教程来了,但就是dll加载失败?
为什么明明项目可以运行,打包成jar包后,dll就加载失败了?
其实我们刚刚已经说了,dll存放位置是取决于工程配置的
网上很多dll使用教程是按他自己环境来的,所以到了别人机子上就不合适了
所以我们需要弄清楚dll的加载原理,下一篇博客我们会专门来讲解这个事情
其实这个事情本身比较简单,之所以我要讲这个,是看到有些40多岁的工程师,还会出现dll找不到不知道该怎么办的问题,说明不求甚解的人实在是不少,所以决定专门写个博客来讲下,如果有人正好看到,以后就可以少走弯路
源代码
//Hello.java
package com.easing.java;
public class Hello {
static {
System.loadLibrary("libhello");
}
public static native int sum(int a, int b);
public static void main(String[] args) {
Hello instance = new Hello();
int sum = instance.sum(100, 200);
System.out.println("sum result is : " + sum);
}
}
//com_easing_java_Hello.h
#ifndef _Included_com_easing_java_Hello
#define _Included_com_easing_java_Hello
#include "jni.h"
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jint JNICALL Java_com_easing_java_Hello_sum(JNIEnv *, jobject, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
//com_easing_java_Hello.cpp
#include "com_easing_java_Hello.h"
JNIEXPORT jint JNICALL Java_com_easing_java_Hello_sum(JNIEnv *env, jobject obj, jint a, jint b) {
return a + b;
}
//CMakeLists.txt
set(CMAKE_CXX_STANDARD 17)
project(hello)
add_library(
hello SHARED
jni.h
jni_md.h
com_easing_java_Hello.h
com_easing_java_Hello.cpp
)
JNI数据类型
Java Type | Native Type |
---|---|
int | jint |
long | jong |
double | jdouble |
boolean | jboolean |
byte | jbyte |
char | jchar |
String | jstring |
Object | jobject |
Class | jclass |
Object[] | jobjectArray |
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)