Java数据类型 | JNI数据类型 |
---|---|
byte | jbyte |
char | jchar |
short | jshort |
int | jint |
long | jlong |
float | jfloat |
double | jdouble |
Java类型 | 类型签名 |
---|---|
boolean | Z |
byte | B |
int | I |
char | C |
short | S |
long | L |
float | F |
double | D |
void | V |
数组 | [类型签名,比如String[] 是[Ljava/lang/String; |
类 | L全限定名;,比如String, 其签名为Ljava/lang/String;(注意后面有个分号) |
根据函数名来建立 java 方法与 JNI 函数的一一对应关系;
命名规则如下:Java+方法的全路径(路径中一定要用“_”代替Java全路径里面的"“.”)
固定模式1:每个函数头部必须包含extern “C” JNIEXPORT <函数的返回类型> JNICALL
固定模式2:每个函数的入参必须包含JNIEnv env, jclass clazz
jobject与jclass通常作为JNI函数的第二个参数,当所声明Native方法是静态方法时,对应参数jclass,因为静态方法不依赖对象实例,而依赖于类,所以参数中传递的是一个jclass类型。相反,如果声明的Native方法时非静态方法时,那么对应参数是jobject
在MainActivity里面定义了一个native方法:
public native String stringFromJNI();
按照命名对应规则,JNI层的函数注册为:
extern "C" JNIEXPORT jstring JNICALL
Java_com_anniljing_jnidemo_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
LOGD("Get native message success");
return env->NewStringUTF(hello.c_str());
}
问题1:正常情况下,光标放在native方法上,然后使用快捷键alt+enter会有Create JNI function for setMode提示,选择Create JNI function for setMode,会快速帮我们创建对应的cpp文件,
如果没有的话,需要我们手动创建,手动输入是不存在的,还是把光标放在对应的native方法之上,复制提示的内容,然后加上头部固定模式就可以了。
原理:利用 RegisterNatives 方法来注册 java 方法与 JNI 函数的一一对应关系;
实现流程:
1、实现 JNI_OnLoad 方法,该方法在jni.h的头文件中定义,其中还定义了JNI_OnUnload方法,在加载动态库后,执行动态注册;
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved);
JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved);
JNIEXPORT int JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
}
2、调用 FindClass 方法,获取 java 对象;
jclass jcls;
jcls = env->FindClass(name);
if (jcls == nullptr) {
return JNI_FALSE;
}
3、利用结构体 JNINativeMethod 数组记录 java 方法与 JNI 函数的对应关系;
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
name为java层的方法名,signature为java层的参数和返回值的类型签名,fnPtr为jni层方法名
static JNINativeMethod gMethods[] = {
{"sum", "(II)I", (void *) sum},
{"getNativeString", "()Ljava/lang/String;", (void *) getMessage}
};
4、调用 RegisterNatives 方法,传入 java 对象,以及 JNINativeMethod 数组,以及注册数目完成注册;
if (env->RegisterNatives(jcls, methods, nMethods) < 0) {
return JNI_FALSE;
}
JNI获取属性方法 | 含义 |
---|---|
GetFieldID(jclass clazz, const char* name, const char* sig) | 获取属性Id,形参:clazz-java类,name-java类属性名,sig-java类属性类型签名 |
GetObjectField(jobject obj, jfieldID fieldID) | 获取属性为对象的 |
GetBooleanField(jobject obj, jfieldID fieldID) | 获取类型为布尔值的属性 |
GetByteField(jobject obj, jfieldID fieldID) | 获取类型为byte的属性 |
GetCharField(jobject obj, jfieldID fieldID) | 获取类型为Char的属性 |
GetShortField(jobject obj, jfieldID fieldID) | 获取类型为Short的属性 |
GetIntField(jobject obj, jfieldID fieldID) | 获取类型为Int的属性 |
GetLongField(jobject obj, jfieldID fieldID) | 获取类型为long的属性 |
GetFloatField(jobject obj, jfieldID fieldID) | 获取类型为float的属性 |
GetDoubleField(jobject obj, jfieldID fieldID) | 获取类型为double的属性 |
JNI设置属性方法 | 含义 |
---|---|
SetObjectField(jobject obj, jfieldID fieldID, jobject value) | 设置类型为Object的属性值 |
SetBooleanField(jobject obj, jfieldID fieldID, jobject value) | 设置类型为Boolean的属性值 |
SetByteField(jobject obj, jfieldID fieldID, jobject value) | 设置类型为Byte的属性值 |
SetCharField(jobject obj, jfieldID fieldID, jobject value) | 设置类型为Char的属性值 |
SetShortField(jobject obj, jfieldID fieldID, jobject value) | 设置类型为Short的属性值 |
SetIntField(jobject obj, jfieldID fieldID, jobject value) | 设置类型为Int的属性值 |
SetLongField(jobject obj, jfieldID fieldID, jobject value) | 设置类型为Long的属性值 |
SetFloatField(jobject obj, jfieldID fieldID, jobject value) | 设置类型为Float的属性值 |
SetDoubleField(jobject obj, jfieldID fieldID, jobject value) | 设置类型为Double的属性值 |
第一步、获取到Java类
jclass jc = env->GetObjectClass(people);
第二步、GetFieldID方法获取到Java层类属性
jfieldID fidAge = env->GetFieldID(jc, "age", "I");
第三步、调用SetXXXField()方法,为java层的类属性设置值
env->SetIntField(people, fidAge, 18);
第一步、获取Java类
jclass jc = env->GetObjectClass(people);
第二步、调用GetMethodID()方法,获取jMethodID
jmethodID jMethod = env->GetMethodID(jc, "doIntroduce", "()V");
第三步、调用CallVoidMethod()方法,调用Java类成员方法
env->CallVoidMethod(people, jMethod);
先创建空的AndroidLog.h文件,然后在其他的cpp源文件加入其预编译的头文件,重新编译一下,AndroidLog头文件再重新加入相关的头文件,编译即可通过
新增了如下代码:
add_library(
dynamicLoad-lib
SHARED
jni_dynamic_load.cpp
)
cmake_minimum_required(VERSION 3.10.2)
project("jnidemo")
add_library(
native-lib
SHARED
native-lib.cpp
jni_dynamic_load.cpp
)
find_library(
log-lib
log
)
include_directories(${CMAKE_HOME_DIRECTORY}/base)
target_link_libraries(
native-lib
${log-lib})
这样的话,就放在了一个库里面。
cmake_minimum_required(VERSION 3.10.2)
project("jnidemo")
add_library(
native-lib
SHARED
native-lib.cpp
)
add_library(
dynamicLoad-lib
SHARED
jni_dynamic_load.cpp
)
find_library(
log-lib
log
)
include_directories(${CMAKE_HOME_DIRECTORY}/base)
target_link_libraries(
native-lib
${log-lib})
target_link_libraries(
dynamicLoad-lib
${log-lib}
)
target_link_libraries(
dynamicLoad-lib
${log-lib}
#define JAVA_CLASS "com/anniljing/jnidemo/JniDynamicLoad"