Android JNI开发
2020-04-03 本文已影响0人
好大的太阳哦
Android JNI开发
什么是JNI?有什么用?
JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是C&C++)
JNI一般创建流程
方法一
- java代码中创建本地方法使用
native
关键字标识 如:
public native void stringFromJNI();
public native void stringFromJNI2();
public native String stringFromJNI3(String s);
- 在c中编写对应方法如:
extern "C"
JNIEXPORT void JNICALL
Java_com_boby_openslaudio_MainActivity_stringFromJNI1(JNIEnv *env, jobject thiz) {
using namespace std;
cout << "Hello___________World";
}
extern "C"
JNIEXPORT jstring JNICALL
Java_com_boby_openslaudio_MainActivity_stringFromJNI2(JNIEnv *env, jobject thiz) {
return env->NewStringUTF("hello word !");
}
extern "C"
JNIEXPORT jstring JNICALL
Java_com_boby_openslaudio_MainActivity_stringFromJNI3(JNIEnv *env, jobject thiz, jstring s) {
const char *str=env->GetStringUTFChars(test,0);
env->ReleaseStringUTFChars(test,str);
return env->NewStringUTF("aaa");
}
- extern "C" : 确保c++编译器在调用c代码中的函数时使用未经修改的名称
- JNIEXPORT : 标识后面接着实际jni返回的值,对应java方法中的返回值 如 void 、 jstring 、jint 等
- JNICALL :标识后面接jni调用的c方法,方法名规律:
JAVA_包名_类名_方法名
- JNIEnv * env:这个env可以看做是Jni接口本身的一个对象,jni.h头文件中存在着大量被封装好的函数,这些函数也是Jni编程中经常被使用到的,要想调用这些函数就需要使用JNIEnv这个对象。例如:env->GetObjectClass()。(详情请查看jni.h)
- jobject obj:代表着native方法的调用者本身,如: new NativeDemo();但如果native是静态的,那就是NativeDemo.class
- 如果java中需要传参给c,前两个参数固定为 JNIEnv * env ,jobject obj 第三个参数开始则为传递的参数。
JVM将JNI接口指针传递给本地方法,本地方法只能在当前线程中访问该接口指针,不能将接口指针传递给其它线程使用。在VM中 JNI接口指针指向的区域用来分配和存储线程本地数据。
当Java代码调用本地方法时,VM将JNI接口指针作为参数传递给本地方法,当同一个Java线程调用本地方法时VM保证传递给本地方法的参数是相同的。不过,不同的Java线程调用本地方法时,本地方法接收到的JNI接口指针是不同的。
基本类型
Java Language Type | JNI Type |
---|---|
boolean | jboolean |
byte | jbyte |
char | jchar |
short | jshort |
int | jint |
long | jlong |
float | jfloat |
double | jdouble |
All Reference type | jobject |
方法二
在 JNI_OnLoad中调用
registerNative 手动映射java方法
typedef struct{
const char* name; //java层定义的名字
const char* signature;//方法签名,有规格的字符串,标注输入和输出参数
void* fnPtr;//真正需要映射到c的具体的Api
}JNINativeMethod;
注册Native的最佳时期
- jint JNI_OnLoad(JavaVM *vm, void *reserved )
- Jint JNI_OnUnload(JavaVM *vm, void *reserved)
- java层
public native String _test();
- c++层
#define JNI_CLASS_PATH "com/boby/openslaudio/MainActivity"
extern "C"
JNIEXPORT void JNICALL
my_test_register(JNIEnv *env, jobject thiz) {
retrun env->NewStringUTF("this is a test of register")
}
//方法映射表
static JNINativeMethod g_methods[] ={
"_test","()Ljava/lang/String;",(void*)my_test_register
};
jint JNI_OnLoad(JavaVM *vm,void *reserved){//同一个进程只有一个JavaVM
JNIEnv *env = NULL;
vm->GetEnv((void ** )&env,JNI_VERSION_1_6);
jclass clazz = env->FindClass(JNI_CLASS_PATH);
//注册本地方法 参数:class,方法映射表,方法映射表中的个数
env->RegisterNatives(clazz,g_methods, sizeof(g_methods)/ sizeof(g_methods[0]));
return JNI_VERSION_1_6;
}
JNI中的Signature
java与c/c++相互调用时,用于描述函数参数的描述符
可以简单理解为java 虚拟机中有一个映射表。函数名和参数拼接在一起形成一个唯一的key ,通过key可以找到函数指针
- 输入参数放在
( )
内,输出参数放在( )
外的右边 - 多个参数之间顺序存放,用
;
分割
原始类型的Signature
java类型 | 符号 |
---|---|
boolean | Z |
byte | B |
char | C |
short | S |
int | I |
long | L |
float | F |
double | D |
void | V |
类的Signature
-
java对象 “L包路径/类名” 如:
Ljava/lang/String
-
java数组 “ [ ”
例:
-
([LStudent;])[LStudenty; --> Student[] Xxx(student[] s)
- ([Ljava/lang/String;)[Ljava/lang/Object; --> Object[] Xxx(String[] s)
- ([I;[Ljava/lang/String;LStudent;)[Ljava/lang/Object --> Object[] Xxx(int [] a,String[] b,Student c)
C/C++ 调Java方法
调用过程有点像java的反射
- FindClass 可以在c后c++中找到java中的类
- GetMethodID/GetFieldID 拿到方法和属性的索引值
- NewObject 获取对象
- Call<TYPE>Method/[G/S]et<Type>Field 调用方法或调用属性
java
public class Student {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
//测试java调用
public native String test_c_callJava();
extern "C"
JNIEXPORT jstring JNICALL
test_c_calljava(JNIEnv *env, jobject thiz) {
//1.找到class
jclass clazz=env->FindClass("com/boby/openslaudio/Student");
// 2.找到属性/方法的索引值
jmethodID jmethod_init_id =env->GetMethodID(clazz,"<init>","()V");
jmethodID jmethod_set_id =env->GetMethodID(clazz,"setName","(Ljava/lang/String;)V");
jmethodID jmethod_get_id =env->GetMethodID(clazz,"getName","()Ljava/lang/String;");
// 3.创建对象
jobject obj=env->NewObject(clazz,jmethod_init_id);
//4.调用对应的方法
env->CallVoidMethod(obj,jmethod_set_id,env->NewStringUTF("boby"));
jstring name= (jstring)env->CallObjectMethod(obj, jmethod_get_id);
return name ;
}