今天继续学习JNI,前几篇学习了一些基本的内容,今天我们全部实践一下,这篇文章仅作为笔记,以防以后忘记
先来看下需要用到的Api
jclass GetObjectClass(JNIEnv *env, jobject obj);
jfieldID GetFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
GetObjectClass
或者FindClass
获取根据变量的类型,会有不同的函数来获取变量的值,函数的基本形式如下:
NativeType Get<type>Field(JNIEnv *env, jobject obj, jfieldID fieldID);
函数名 | 返回值类型 |
---|---|
GetBooleanField() | jboolean |
GetByteField() | jbyte |
GetCharField() | jchar |
GetShortField() | jshort |
GetIntField() | jint |
GetLongField() | jlong |
GetFloatField() | jfloat |
GetDoubleField() | jdouble |
GetObjectField() | jobject |
前八项是基本数据类型,第九项是获取所有引用数据类型
例如获取int变量,如下:
jint GetIntField(JNIEnv * env, jobject obj, jfieldID fieldID);
GetFieldID
获取根据变量的类型,有不同的函数设置变量的值
void Set<type>Field(JNIEnv *env, jobject obj, jfieldID fieldID,
NativeType value);
函数名 | 参数类型 |
---|---|
SetBooleanField() | jboolean |
SetByteField() | jbyte |
SetCharField() | jchar |
SetShortField() | jshort |
SetIntField() | jint |
SetLongField() | jlong |
SetFloatField() | jfloat |
SetDoubleField() | jdouble |
SetObjectField() | jobject |
前八项对应基本数据类型,第九项java所有引用类型
例如,int的函数原型
void setIntField(JNIEnv * env, jobject obj, jfieldID fieldID, jint value);
jmethodID GetMethodID(JNIEnv *env, jclass clazz,
const char *name, const char *sig);
根据java方法返回值得不同,jni有不同的函数来调用java对象的方法,基本的形式有三种
NativeType Call<type>Method(JNIEnv *env, jobject obj,
jmethodID methodID, ...);
NativeType Call<type>MethodA(JNIEnv *env, jobject obj,
jmethodID methodID, const jvalue *args);
NativeType Call<type>MethodV(JNIEnv *env, jobject obj,
jmethodID methodID, va_list args);
这三个的方法的区别在与传递参数的不同,最常用的是第一个,我们只讲第一个
如果方法返回的是int,那么函数原型为
jint CallIntMethod(JNIEnv * env, jobject obj, jmethodID methodID, ...);
如果方法的返回值是引用数据类型
jobject CallObjectMethod(JNIEnv * env, jobject obj, jmethodID methodID, ...);
如果java的返回值类型为void
void CallVoidMethod(JNIEnv * env, jobject obj, jmethodID methodID, ...);
GetMethodID
获取首先定义一个java类,里面有个native方法,当创建对象是会调用
public class Student {
public String name = "小红";
static {
System.loadLibrary("studentlib");
}
native void native_init();
public Student() {
native_init();
}
public void print() {
Log.d("mmm", name);
}
}
在jni注册这个java的native方法,然后用C实现它
#include
#include
#include
static void native_init(JNIEnv *env, jobject jobject1) {
//1 获取java对象的变量的值,并重新为他设置新的值
//获取class对象
jclass jclass_student = env->GetObjectClass(jobject1);
//从class中获取变量
jfieldID jfieldId = env->GetFieldID(jclass_student, "name", "Ljava/lang/String;");
//从java对象obj中获取name变量的值
jstring name = static_cast<jstring>(env->GetObjectField(jobject1, jfieldId));
const char *char_name = env->GetStringUTFChars(name, JNI_FALSE);
//打印出原来的name变量的值
__android_log_print(ANDROID_LOG_INFO, "mmm", "method = %s, msg = %s", __FUNCTION__,
char_name);
const char native_name[] = "小明";
jstring jstring_name = env->NewStringUTF(native_name);
//通过jni重新设置变量name的值
env->SetObjectField(jobject1, jfieldId, jstring_name);
//2 调用java对象中的方法
//获取class对象中的print方法
jmethodID print = env->GetMethodID(jclass_student, "print", "()V");
//调用java对象中的print方法
env->CallVoidMethod(jobject1, print);
}
static const JNINativeMethod nativeMethod[] = {
{
"native_init", "()V", (void *) native_init},
};
static int registNativeMethod(JNIEnv *env) {
int result = -1;
jclass class_text = env->FindClass("com.taobao.alinnkit.ndk1.Student");
if (env->RegisterNatives(class_text, nativeMethod,
sizeof(nativeMethod) / sizeof(nativeMethod[0])) == JNI_OK) {
result = 0;
}
return result;
}
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
int result = -1;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_1) == JNI_OK) {
if (registNativeMethod(env) == JNI_OK) {
result = JNI_VERSION_1_6;
}
return result;
}
}
动态注册上篇文章讲了,这里就不多说了,主要的实现逻辑就在native_init
的实现里,大概逻辑就是,获取Student
对象中的变量name
的值,并打印出来,然后重新设置name
变量的值,最后调用Student
对象的print
方法,打印出name
变量的值
最后就是调用,直接new就可以触发
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Student student = new Student();
}
看下打印结果
/mmm: method = native_init, msg = 小红
/mmm: 小明
先看下用带的Api
获取class对象
jclass FindClass(JNIEnv *env, const char *name);
获取class对象的静态字段
jfieldID GetStaticFieldID (JNIEnv *env,
jclass clazz,
const char *name,
const char *sig);
获取静态字段的值,根据静态字段的类型不同,jni有不同的方法获取静态字段的值
方法名 | 返回值 |
---|---|
GetStaticBooleanField | jboolean |
GetStaticByteField | jbyte |
GetStaticCharField | jchar |
GetStaticShortField | jshort |
GetStaticIntField | jint |
GetStaticLongField | jlong |
GetStaticFloatField | jfloat |
GetStaticDoubleField | jdouble |
GetStaticObjectField | jobject |
void SetStatic<type>Field(JNIEnv *env,
jclass clazz,
jfieldID fieldID,
NativeType value);
方法名 | NativeType |
---|---|
SetStaticBooleanField | jboolean |
SetStaticByteField | jbyte |
SetStaticCharField | jchar |
SetStaticShortField | jshort |
SetStaticIntField | jint |
SetStaticLongField | jlong |
SetStaticFloatField | jfloat |
SetStaticDoubleField | jdouble |
SetStaticObjectField | jobject |
jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* sig)
void CallStaticVoidMethod(jclass clazz, jmethodID methodID, ...)
这俩个跟之前调用普通方法用法一样就是换了个函数名
java代码
public class Student {
public static String text = "没改过";
static {
System.loadLibrary("studentlib");
}
native void native_Text();
public static void printText() {
Log.d("mmm", text);
}
}
c代码
static void native_text(JNIEnv *env, jobject jobject1) {
//获取class
jclass jclass_student = env->FindClass("com.taobao.alinnkit.ndk1.Student");
//获取静态变量text
jfieldID jfieldId_text = env->GetStaticFieldID(jclass_student, "text", "Ljava/lang/String;");
//获取静态变量text的值
jstring text = (jstring) env->GetStaticObjectField(jclass_student, jfieldId_text);
const char *char_name = env->GetStringUTFChars(text, JNI_FALSE);
__android_log_print(ANDROID_LOG_INFO, "mmm", "method = %s, msg = %s", __FUNCTION__,
char_name);
jstring jstring1 = env->NewStringUTF("改过了");
//修改静态变量text的值
env->SetStaticObjectField(jclass_student, jfieldId_text, jstring1);
//获取静态方法printText
jmethodID jmethodId = env->GetStaticMethodID(jclass_student, "printText", "()V");
//调用静态方法printText
env->CallStaticVoidMethod(jclass_student, jmethodId);
}
static const JNINativeMethod nativeMethod[] = {
{
"native_Text", "()V", (void *) native_text}
};
static int registNativeMethod(JNIEnv *env) {
int result = -1;
jclass class_text = env->FindClass("com.taobao.alinnkit.ndk1.Student");
if (env->RegisterNatives(class_text, nativeMethod,
sizeof(nativeMethod) / sizeof(nativeMethod[0])) == JNI_OK) {
result = 0;
}
return result;
}
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
int result = -1;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_1) == JNI_OK) {
if (registNativeMethod(env) == JNI_OK) {
result = JNI_VERSION_1_6;
}
return result;
}
}
主要的实现就是native_text
方法,主要做了获取静态变量text
的值,修改静态变量text
的值,调用静态方法printText
调用
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Student student = new Student();
student.native_Text();
}
打印数据
/mmm: method = native_text, msg = 没改过
/mmm: 改过了
https://juejin.im/post/5d23fb066fb9a07eb15d7b29
https://blog.csdn.net/afei__/article/details/81016413
https://www.jianshu.com/p/67081d9b0a9c