JNI的学习与实践所得(Android)
Motivation
又快到毕业季了,实验室研三的的学长学姐们开始赶自己的毕业论文,写论文自然少不了做实验。因此就被派了这么个活儿,“制作”包含JNI调用的APP供他们做实验用。
需要做的准备
1. Android Studio 的安装与配置(包括SDK)
这个不用多说,选择Android Studio 3.0.1作为IDE,Gradle和SDK没问题就好。
2. JDK的安装与配置(包含系统环境变量)
这个就更不用多说,首先我们要做的是JNI,其次Java没配好,AS就配不好。
实践部分
1. javah外部工具的配置
Java类中我们定义的JNI native函数形如:
public static native int jniFunction(int parameter);
与之链接的native层的函数头形如:
extern "C" //注①
/*
* Class: com_implementist_jnitest_MainActivity //注②
* Method: jniFunction //注③
* Signature: (I)I //注④
*/
JNIEXPORT jboolean JNICALL Java_com_implementist_jnitest_MainActivity_jniFunction //注⑤
(JNIEnv *env, jclass _this, jint parameter) { //注⑥
**注释①:**每一个JNI函数都需要写这一句,不然运行的时候会报错说找不到相应的函数。
注释②:
注释③:
**注释④:**这三个注释里的东西可有可无,毕竟是一个注释,后面我会讲如何自动生成它们。
**注释⑤:**函数头,包含了返回值类型,包名,类名,函数名
**注释⑥:**参数部分,你可能会想,我定义的是一个参数,怎么这里有三个参数?解释如下:
前两个参数是每个JNI native函数都有的,且是必要的。
JNIEnv:是JNI的环境指针变量,用env->的方式可以调用很多必要的JNI函数,后面我们会用到一些。
jclass:定义的是定义这个JNI native函数的Java类的实例,一般用于在native里回调Java函数时(用class A的实例调用class B的成员函数是要报错的!调用前考虑清楚)
jint:这个参数才是我们自己声明的参数,需要注意的是在这里它变成了jint,因为这里(cpp文件中)是C语言的环境,所以需要把Java int 和 C int区分开来(自然还有jstring、jboolean等对应的类型存在)
说了这么多,想手动把这些东西写对,对于新手来说还是很难的,况且JNI的报错信息让人调试起来很抓狂。因此,我们可以用javah命令来帮我们编译出这些东西。
但是无论cmd还是Android Studio里的Terminal,不但不好用每次都需要敲一大串路径进去,繁琐耗时。
我们可以预先配置Android Studio里的外部工具,然后每次一键编译,重点终于来了!
①依次点击IDE左上方的File->Settrings菜单项
②在弹出的Settings窗口,依次选择Tools -> External Tools -> +
③在Name, Program, Parameters和Working directory这四个输入框填入以下指令:
Name:javah
Program:$JDKPath$\bin\javah.exe
Parameters: -classpath . -jni -o $ModuleFileDir$/src/main/jni/$Prompt$ $FileClass$
Working directory:$ModuleFileDir$\src\main\java
填好的情形如下图,点击OK按钮就完成了配置。
2. 创建一个支持JNI的Android工程
对已经创建好的Android工程添加JNI支持是很麻烦的,因此我们在创建工程的时候让AS帮我们直接配置好。
和创建普通的Android工程基本类似,填好工程的名称,包名等,关键的不同在于,选中C++ Support复选框。
后面一路Next就可以了。
3. 定义JNI函数
在MainActivity里,我们定义一个具有一个整型参数和整型返回值的JNI native函数:
4. 用javah外部工具编译MainActivity
①在IDE左边的工程树上选中要编译的Java类(MainActivity);
②右键唤出菜单;
③在菜单的最下方,依次选择External Tool -> javah
④此时会弹出一个输入框,要求填入头文件的名称,随便起,比如就叫MainActivity
⑤此时,下方会弹出Run控制台,如果最后一行最后边是exit code 0,就说明编译过程中没有错误,正常结束。 如果exit code不是0,就是异常退出,就要视情况而定了。
5. 取得函数头
前面做了那么多工作,全都是为这里做准备。
①切换工程树模式
首先把工程树切换到project模式,依次点击左上方的下三角 -> project
②找到头文件
依次打开目录app -> src - > main -> jni -> MainActivity
③函数头
函数头就是红色框里面的部分
④复制到native-lib.cpp中
native-lib.cpp是我们实际编写JNI程序的地方,我们需要把上述函数头复制到这个文件中来使用,但是在使用之前,需要做一些简单的修改。
extern "C" { //注①
#endif //注②
/*
* Class: com_implementist_jnitest_MainActivity
* Method: jniFunction
* Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_com_implementist_jnitest_MainActivity_jniFunction
(JNIEnv *, jclass, jint); //注③
#ifdef __cplusplus //注④
} //注⑤
注释①:extern "C"必须写在每一个JNI函数前面,否则会报找不到JNI函数的异常,左大括号删掉。
注释②:这行删掉
注释③:编译的时候只给了参数类型,没给参数名,填上,然后删掉分号,加上左右大括号。
注释④:
注释⑤:这两行删掉。
修改后的函数头在native-lib.cpp里如下所示:
现在,就可以在这个函数里编写JNI程序了。
⑤调用
调用的时候直接调用在Java类里调用我们之前声明的那个函数就可以了:
后记
到目前为止,这只是学习JNI编程之前的各项准备,相当于我们准备好了工作台和工具。今天就先写到这。对于数组操作、字符串操作和Java函数回调我会在之后分几篇帖子写出来,敬请期待。
——2018.01.30
上一篇: PHP 多工具安全相关库:SecurityMultiTool
下一篇: Swift4.1 第一章
推荐阅读
-
Vue.js教程之axios与网络传输的学习实践
-
详解有关Android截图与录屏功能的学习
-
微信小程序中多个页面传参通信的学习与实践
-
Android-JNI开发系列实践-利用Android C源码实现GIF图片的播放
-
黑马Android76期学习笔记01基础--day07--广播,有、无序广播、特殊广播接受者、样式和主题,this与context的区别、普通对话框,进度条对话框、帧动画
-
android kotlin学习之路 函数的认识与定义
-
android kotlin学习之路 kotlin中的分支判断与循环判断(2)
-
一份关于 Java、Kotlin 与 Android 的学习笔记
-
Android JNI中jfloatArray与jfloat*的相互转换
-
Linux内核学习笔记 -29 动手实践 - 中断下半部的代码分析与应用