突然写博客的初衷
以前从来也没有写博客的习惯,不过最近面试中,面试官问有没有写过什么博客啊,有没有博客的地址啊,我蒙蔽了,半开玩笑的说:“正准备写…”匆忙岔开话题。
正好也临近年关,忙了一年了,回去也好好过个年,顺便整理一下自己的知识点,用博客的方式记录下来,方便自己查阅,顺便吐槽一下百度云,坑爹的公司出的坑爹的产品,我的项目都在上面,现在拿下来都很艰难。
今天写一下android JNI的调用,写一个最简单的计算器,只有两个数字的加减乘除,逻辑运算用C来实现。
JNI的调用逻辑共以下几步
1、创建一个java类,加载一个动态库,编写一个需要的native方法。
2、编译上面的java类,生成一个class字节码文件。
3、根据上面的字节码文件生成一个后缀为 .h 开头的h文件。
4、创建一个后缀为 .c 的文件,将.h文件的代码copy进入,重新编写实现代码.
5、配置jni运行环境
6、rebuild project 一下,会在app > build文件夹下生成需要的.so文件,这就是你需要的文件,上面的.class .h .c 都可以删除了,只要这些so文件copy到jni文件夹中就可以使用了,下面详细讲解,同时加深自己的印象并且和大家共勉。
1、创建一个java类,加载一个动态库,编写一个需要的native方法。
public class MyJNI {
static {
System.loadLibrary("Ccalculate");
}
public static native int add(int i ,int j);
public static native int subtract(int i ,int j);
public static native int multiply(int i ,int j);
public static native int divide(int i ,int j);
}
里面的静态代码块中的“Ccalculate” 是自定义的命名,下面是运算的几个方法,这几个方法在java文件中声明,用C去实现,这时定义的几个native方法都处在报错状态,没关系,不用处理。
2、编译上面的java类,生成一个class字节码文件。
使用cmd或者使用alt+f12快捷键调用控制台,生成对应的.class文件,我这里是MyJNI.class,第一步完成,如果没完成,请去补课。
3、根据上面的字节码文件生成一个后缀为 .h 开头的h文件。
找到自己的class文件,用java提供的工具 javah来生成一个.h文件,代码为 javah -jni MyJNI ,这句话的意思就是用javah找到刚刚编写的字节码文件MyJNI.class 文件,生成一个.h的文件
我的编译代码是javah -jni com.org.zl.testjni.JNI.MyJNI生成一个.h的文件
打来.h文件,查看里面的代码
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_org_zl_testjni_JNI_MyJNI */
#ifndef _Included_com_org_zl_testjni_JNI_MyJNI
#define _Included_com_org_zl_testjni_JNI_MyJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_org_zl_testjni_JNI_MyJNI
* Method: add
* Signature: (II)I
*/
JNIEXPORT jint JNICALL
/*
其实我也看不懂,不过没关系,这个方法后面有个add,是我在java中定义的一个方法,这个我看懂了,后来问做C开发的朋友,他们说不知道这个什么鬼,只好我就自己想办法,详情请看 JNIEnv解析
*/
Java_com_org_zl_testjni_JNI_MyJNI_add
(JNIEnv *, jclass, jint, jint);
/*
* Class: com_org_zl_testjni_JNI_MyJNI
* Method: subtract
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_org_zl_testjni_JNI_MyJNI_subtract
(JNIEnv *, jclass, jint, jint);
/*
* Class: com_org_zl_testjni_JNI_MyJNI
* Method: multiply
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_org_zl_testjni_JNI_MyJNI_multiply
(JNIEnv *, jclass, jint, jint);
/*
* Class: com_org_zl_testjni_JNI_MyJNI
* Method: divide
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_org_zl_testjni_JNI_MyJNI_divide
(JNIEnv *, jclass, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
这时.h文件就OK了,下一步COPY,实现它,.h也在报错,没干系,不要管他,因为我们用它过度的,最后需要的是SO文件,所以到最后删掉都没事
4、创建一个后缀为 .c 的文件,将.h文件的代码copy进入,重新编写实现代码(包括修改比较).
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_org_zl_testjni_JNI_MyJNI */
#ifndef _Included_com_org_zl_testjni_JNI_MyJNI
#define _Included_com_org_zl_testjni_JNI_MyJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_org_zl_testjni_JNI_MyJNI
* Method: add
* Signature: (II)I
*/
JNIEXPORT jint JNICALL
//这里是.h文件的代码的add方法,
Java_com_org_zl_testjni_JNI_MyJNI_add
(JNIEnv *, jclass, jint, jint);
//这里是修改过后的实现方法
Java_com_org_zl_testjni_JNI_MyJNI_add
**(JNIEnv *en, jclass obj, jint i, jint j){//这里修改过(添加了变量,和大括号)
return i+j;//这里也是,其他很像**
}
/
*
* Class: com_org_zl_testjni_JNI_MyJNI
* Method: subtract
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_org_zl_testjni_JNI_MyJNI_subtract
(JNIEnv *en, jclass obj, jint i, jint j) {
return i-j;
}
/*
* Class: com_org_zl_testjni_JNI_MyJNI
* Method: multiply
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_org_zl_testjni_JNI_MyJNI_multiply
(JNIEnv *en, jclass obj, jint i, jint j){
return i*j;
}
/*
* Class: com_org_zl_testjni_JNI_MyJNI
* Method: divide
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_org_zl_testjni_JNI_MyJNI_divide
(JNIEnv *en, jclass obj, jint i, jint j){
return i/j;
}
#ifdef __cplusplus
}
#endif
#endif
5、配置jni运行环境
1、app下的build.gradle需要添加一句代码
(这个名字和上面的静态代码块中的一样,用到名字的就这两个地方,保证一样就可以了)
System.loadLibrary("Ccalculate"); 和下面的名字一样,记得ndk{}应该写在defaultConfig {}里面,不然会报错
报错信息是
Error:(15, 1) A problem occurred evaluating project ':app'.
> Could not find method ndk() for arguments [build_2y5lwsos45iu20kkou5i1vyei$_run_closure1$_closure4@5363efbc] on project ':app' of type org.gradle.api.Project.
2、gradle.properties
android.useDeprecatedNdk=true
3、local.properties
因为我的NDK是用android studio自己下载的,所以我直接去sdk文件夹中找ndk了,如果你是自己下载的NDK,那么这里填写你自己的ndk路径
ndk.dir=D\:\\Android\\SDK\\ndk-bundle
6、重建项目,获取新生成的so文件
刚开始是没有ndk文件夹的,重建完后即可,将so文件夹放入jni中,需要的文件已经创建出来了,将.class .h .c 删除,即可。