Java调用C代码
-
在Java中声明native方法
创建一个类,类名可以根据自己情况起,代码如下:package xiaojiukeji.ndk002javaforc; /** * Created by mixiaojiu on 2019/10/25. */ public class JNI { { System.loadLibrary("Hello"); } public native int add(int x,int y); public native String sayHello(String s); public native int[] increaseArrayEles(int[] intArray); public native int checkPwd(String pwd); }
我们可以看到上面的一行代码块:
{ System.loadLibrary("Hello"); }
这个Hello是指相应调用的c/c++文件,整体目录如下
注意:红色箭头指向的即是Java所要调用的C文件名称,蓝色箭头指的是头文件名称,接下类马上就会提到。
为了项目在运行过程中能够顺利进行,在项目的build.gradle中也要进行配置
这个moduleName中的“Hello”是本次demo测试里的.c文件的名称,这里不是一成不变的,换成所使用的即可,abiFilters中所提到的这三个,是调用硬件所需的,如果不配置,会造成某些设备上无法使用。 -
生成头文件
编译Java的源文件,之后通过 javah 命令导出JNI的文件
生成头文件:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class xiaojiukeji_ndk002javaforc_JNI */
#ifndef _Included_xiaojiukeji_ndk002javaforc_JNI
#define _Included_xiaojiukeji_ndk002javaforc_JNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: xiaojiukeji_ndk002javaforc_JNI
* Method: add
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_xiaojiukeji_ndk002javaforc_JNI_add
(JNIEnv *, jobject, jint, jint);
/*
* Class: xiaojiukeji_ndk002javaforc_JNI
* Method: sayHello
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_xiaojiukeji_ndk002javaforc_JNI_sayHello
(JNIEnv *, jobject, jstring);
/*
* Class: xiaojiukeji_ndk002javaforc_JNI
* Method: increaseArrayEles
* Signature: ([I)[I
*/
JNIEXPORT jintArray JNICALL Java_xiaojiukeji_ndk002javaforc_JNI_increaseArrayEles
(JNIEnv *, jobject, jintArray);
/*
* Class: xiaojiukeji_ndk002javaforc_JNI
* Method: checkPwd
* Signature: (Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_xiaojiukeji_ndk002javaforc_JNI_checkPwd
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
这里需要说明一下:首先函数名的格式遵循以下规则:
Java_包名_类名_方法名。
比如:Java_xiaojiukeji_ndk002javaforc_JNI_add
很明显就是Java中的add方法
- JNIEnv * :表示一个指向JNI环境的指针,可以通过它来访问JNI提供的接口方法
- jobject: 表示Java中的对象this
- jint,jstring:表示Java中的数据类型
- 实现JNI方法
在所调用的Hello.c中所执行的方法如下:
/
// Created by mixiaojiu on 2019/10/25.
//
#include "xiaojiukeji_ndk002javaforc_JNI.h"
#include <string.h>
JNIEXPORT jint JNICALL Java_xiaojiukeji_ndk002javaforc_JNI_add(JNIEnv *env, jobject job, jint intA, jint intB){
return intA+intB;
}
JNIEXPORT jintArray JNICALL Java_xiaojiukeji_ndk002javaforc_JNI_increaseArrayEles(JNIEnv *env, jobject job, jintArray intArray){
//1.得到数组的长度
int size = (*env)->GetArrayLength(env,intArray);
//2.得到数组元素
jint* array=(*env)->GetIntArrayElements(env,intArray,JNI_FALSE);
//3.遍历数组,给每个元素加10
int i;
for (int i = 0; i < size; ++i) {
*(array+i)+=10;
}
return intArray;
}
JNIEXPORT jint JNICALL Java_xiaojiukeji_ndk002javaforc_JNI_checkPwd(JNIEnv *env, jobject jobj, jstring str){
//比较字符串是否相同
char* origin = "123456";
return 200;
}
注意引用头文件:#include "xiaojiukeji_ndk002javaforc_JNI.h"
,这个即是引用的生成的调用的头文件,作为桥接
- 生成.so文件并在Java中调用
还记得最开始的JNI文件的代码块吗?
{
System.loadLibrary("Hello");
}
在这个地方引用所生成的.so文件,调用就是常规的调用即可了。创建一个该实例对象,饮用该对象中的JNI方法即可。
结果正常打印输出,很明显,我们的Java调用C代码的基本流程已经执行完毕。
C调用Java代码
JNI调用Java方法的流程是先通过类名找到类,然后再根据方法名找到方法的ID,最后就可以调用这个方法了。
- 在 java中声明native方法,不同的是,同时也要声明一些供c调用的java代码。
package xiaojiukeji.ndk003ccalljava;
import android.util.Log;
/**
* Created by mixiaojiu on 2019/10/25.
*/
public class JNI {
{
System.loadLibrary("CCallJava");
}
/*
//
* */
public native void callbackAdd();
public native void callbackHelloFromJava();
public native void callbackPrintString();
public native void callbackSayHello();
public int add(int x,int y){
Log.d("JNI","x=y================");
return x+y;
}
public void helloFromJava(){
Log.d("JNI","helloFromJava================");
}
public void printString(String str){
Log.d("JNI","printString================"+str);
}
public static void sayHello(String str){
Log.d("JNI","sayHello================"+str);
}
}
- 通过javah生成头文件
- 重点来了啊
1处是通过反射找到代码的.class文件实例,2是实例中的被调用的方法,3处是该方法所在的签名,缺一不可,然后通过callInMethod方法调用Java中的文件即可 - 这样就建立起了桥接,实例调用即可
JNI jni = new JNI();
jni.callbackAdd();
文笔较差,顺着思路走下来的,哪天有空了再调整调整。