文章创建时间:2021-07-02,最后修改时间:2021-07-06
1、 静态注册与动态注册
前面一篇文章讲了Android JNI开发的简单Demo,但是可能还不能明白java层声明的方法,是怎么对应到jni函数的。这就要涉及到下面要讲的两种jni函数注册的方法了。
jni函数有两种注册方法:
- 静态注册:
先在Java层声明本地方法,然后在JNI层实现该声明方法;- 动态注册:
先在JNI层重载JNI_OnLoad()实现本地方法,然后直接在Java层中调用本地方法。
上篇文章中Demo注册jni函数的途径是静态注册。
(1)静态注册
静态注册实现的大致流程:
当java虚拟机需要调用JAVA层声明一个本地方法,从加载的动态库中去寻找名字匹配特定格式的实现,找到了就建立关联,保存在函数指针中;找不到就报错。后面再访问java声明方法就直接调用到这个函数指针。
回顾一下上篇文章通过javah生成头文件的操作。
// java中关于jni函数的声明
public static native String getTextFromNDK();
/*
* Class: com_howie_ndkdemo_NDKTools
* Method: getTextFromNDK
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_howie_ndkdemo_NDKTools_getTextFromNDK
(JNIEnv *, jclass);
根据上面的头文件,我们可以看出来静态注册的jni函数的特定格式为:
JNIEXPORT + 返回值 + JNICALL + Java_包名_类名_方法名 + 参数JNIEnv + 参数jobject + 参数other
这里注意的是,java包名的的"."都会替换为"_";如果在Java中声明的方法是"static",则jni函数也是static。
如果我们尝试将头文件和cpp文件中Java_com_howie_ndkdemo_NDKTools_getTextFromNDK改成Java_com_howie_ndkdemo_NDKTools_getTextFromNDKs,即不满足上述的特定格式,这时候再编译运行app,点击按钮后,会报如下的error:
2021-07-03 12:45:06.762 22626-22626/com.howie.ndkdemo E/m.howie.ndkdem: No implementation found for java.lang.String com.howie.ndkdemo.NDKTools.getTextFromNDK() (tried Java_com_howie_ndkdemo_NDKTools_getTextFromNDK and Java_com_howie_ndkdemo_NDKTools_getTextFromNDK__)
2021-07-03 12:45:06.762 22626-22626/com.howie.ndkdemo D/AndroidRuntime: Shutting down VM
2021-07-03 12:45:06.762 22626-22626/com.howie.ndkdemo E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.howie.ndkdemo, PID: 22626
java.lang.UnsatisfiedLinkError: No implementation found for java.lang.String com.howie.ndkdemo.NDKTools.getTextFromNDK() (tried Java_com_howie_ndkdemo_NDKTools_getTextFromNDK and Java_com_howie_ndkdemo_NDKTools_getTextFromNDK__)
at com.howie.ndkdemo.NDKTools.getTextFromNDK(Native Method)
at com.howie.ndkdemo.MainActivity$1.onClick(MainActivity.java:30)
at android.view.View.performClick(View.java:7515)
at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1119)
at android.view.View.performClickInternal(View.java:7492)
at android.view.View.access$3600(View.java:847)
at android.view.View$PerformClick.run(View.java:28742)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:236)
at android.app.ActivityThread.main(ActivityThread.java:8145)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:656)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:967)
2021-07-03 12:45:06.765 22626-22626/com.howie.ndkdemo D/OOMEventManagerFK: checkEventAndDumpForJE: 0
2021-07-03 12:45:06.784 22626-22626/com.howie.ndkdemo I/Process: Sending signal. PID: 22626 SIG: 9
(2)动态注册
动态注册不需要像静态注册一样,jni函数的名字必须是特定格式,否则jvm会找不到对应的jni函数。动态注册其实是通过一个方法jniRegisterNativeMethods,将java层的声明与jni层中的函数实现映射起来。
先看这个函数的实现
int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
const JNINativeMethod* gMethods, int numMethods)
{
JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
ALOGV("Registering %s's %d native methods...", className, numMethods);
scoped_local_ref<jclass> c(env, findClass(env, className));
ALOG_ALWAYS_FATAL_IF(c.get() == NULL,
"Native registration unable to find class '%s'; aborting...",
className);
int result = e->RegisterNatives(c.get(), gMethods, numMethods);
ALOG_ALWAYS_FATAL_IF(result < 0, "RegisterNatives failed for '%s'; aborting...",
className);
return 0;
}
先看传进来的第一个参数C_JNIEnv,是函数指针表。
typedef const struct JNINativeInterface* C_JNIEnv;
/*
* Table of interface function pointers.
*/
struct JNINativeInterface {
void* reserved0;
void* reserved1;
void* reserved2;
void* reserved3;
jint (*GetVersion)(JNIEnv *);
jclass (*DefineClass)(JNIEnv*, const char*, jobject, const jbyte*,
jsize);
jclass (*FindClass)(JNIEnv*, const char*);
jmethodID (*FromReflectedMethod)(JNIEnv*, jobject);
jfieldID (*FromReflectedField)(JNIEnv*, jobject);
/* spec doesn't show jboolean parameter */
jobject (*ToReflectedMethod)(JNIEnv*, jclass, jmethodID, jboolean);
jclass (*GetSuperclass)(JNIEnv*, jclass);
jboolean (*IsAssignableFrom)(JNIEnv*, jclass, jclass);
/* spec doesn't show jboolean parameter */
jobject (*ToReflectedField)(JNIEnv*, jclass, jfieldID, jboolean);
jint (*Throw)(JNIEnv*, jthrowable);
jint (*ThrowNew)(JNIEnv *, jclass, const char *);
jthrowable (*ExceptionOccurred)(JNIEnv*);
void (*ExceptionDescribe)(JNIEnv*);
void (*ExceptionClear)(JNIEnv*);
void (*FatalError)(JNIEnv*, const char*);
jint (*PushLocalFrame)(JNIEnv*, jint);
jobject (*PopLocalFrame)(JNIEnv*, jobject);
jobject (*NewGlobalRef)(JNIEnv*, jobject);
void (*DeleteGlobalRef)(JNIEnv*, jobject);
void (*DeleteLocalRef)(JNIEnv*, jobject);
jboolean (*IsSameObject)(JNIEnv*, jobject, jobject);
jobject (*NewLocalRef)(JNIEnv*, jobject);
jint (*EnsureLocalCapacity)(JNIEnv*, jint);
jobject (*AllocObject)(JNIEnv*, jclass);
jobject (*NewObject)(JNIEnv*, jclass, jmethodID, ...);
jobject (*NewObjectV)(JNIEnv*, jclass, jmethodID, va_list);
jobject (*NewObjectA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jclass (*GetObjectClass)(JNIEnv*, jobject);
jboolean (*IsInstanceOf)(JNIEnv*, jobject, jclass);
jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
jobject (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...);
jobject (*CallObjectMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jobject (*CallObjectMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
jboolean (*CallBooleanMethod)(JNIEnv*, jobject, jmethodID, ...);
jboolean (*CallBooleanMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jboolean (*CallBooleanMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
jbyte (*CallByteMethod)(JNIEnv*, jobject, jmethodID, ...);
jbyte (*CallByteMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jbyte (*CallByteMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
jchar (*CallCharMethod)(JNIEnv*, jobject, jmethodID, ...);
jchar (*CallCharMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jchar (*CallCharMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
jshort (*CallShortMethod)(JNIEnv*, jobject, jmethodID, ...);
jshort (*CallShortMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jshort (*CallShortMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
jint (*CallIntMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jint (*CallIntMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
jlong (*CallLongMethod)(JNIEnv*, jobject, jmethodID, ...);
jlong (*CallLongMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jlong (*CallLongMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
jfloat (*CallFloatMethod)(JNIEnv*, jobject, jmethodID, ...);
jfloat (*CallFloatMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jfloat (*CallFloatMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
jdouble (*CallDoubleMethod)(JNIEnv*, jobject, jmethodID, ...);
jdouble (*CallDoubleMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jdouble (*CallDoubleMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
void (*CallVoidMethodV)(JNIEnv*, jobject, jmethodID, va_list);
void (*CallVoidMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
jobject (*CallNonvirtualObjectMethod)(JNIEnv*, jobject, jclass,
jmethodID, ...);
jobject (*CallNonvirtualObjectMethodV)(JNIEnv*, jobject, jclass,
jmethodID, va_list);
jobject (*CallNonvirtualObjectMethodA)(JNIEnv*, jobject, jclass,
jmethodID, const jvalue*);
jboolean (*CallNonvirtualBooleanMethod)(JNIEnv*, jobject, jclass,
jmethodID, ...);
jboolean (*CallNonvirtualBooleanMethodV)(JNIEnv*, jobject, jclass,
jmethodID, va_list);
jboolean (*CallNonvirtualBooleanMethodA)(JNIEnv*, jobject, jclass,
jmethodID, const jvalue*);
jbyte (*CallNonvirtualByteMethod)(JNIEnv*, jobject, jclass,
jmethodID, ...);
jbyte (*CallNonvirtualByteMethodV)(JNIEnv*, jobject, jclass,
jmethodID, va_list);
jbyte (*CallNonvirtualByteMethodA)(JNIEnv*, jobject, jclass,
jmethodID, const jvalue*);
jchar (*CallNonvirtualCharMethod)(JNIEnv*, jobject, jclass,
jmethodID, ...);
jchar (*CallNonvirtualCharMethodV)(JNIEnv*, jobject, jclass,
jmethodID, va_list);
jchar (*CallNonvirtualCharMethodA)(JNIEnv*, jobject, jclass,
jmethodID, const jvalue*);
jshort (*CallNonvirtualShortMethod)(JNIEnv*, jobject, jclass,
jmethodID, ...);
jshort (*CallNonvirtualShortMethodV)(JNIEnv*, jobject, jclass,
jmethodID, va_list);
jshort (*CallNonvirtualShortMethodA)(JNIEnv*, jobject, jclass,
jmethodID, const jvalue*);
jint (*CallNonvirtualIntMethod)(JNIEnv*, jobject, jclass,
jmethodID, ...);
jint (*CallNonvirtualIntMethodV)(JNIEnv*, jobject, jclass,
jmethodID, va_list);
jint (*CallNonvirtualIntMethodA)(JNIEnv*, jobject, jclass,
jmethodID, const jvalue*);
jlong (*CallNonvirtualLongMethod)(JNIEnv*, jobject, jclass,
jmethodID, ...);
jlong (*CallNonvirtualLongMethodV)(JNIEnv*, jobject, jclass,
jmethodID, va_list);
jlong (*CallNonvirtualLongMethodA)(JNIEnv*, jobject, jclass,
jmethodID, const jvalue*);
jfloat (*CallNonvirtualFloatMethod)(JNIEnv*, jobject, jclass,
jmethodID, ...);
jfloat (*CallNonvirtualFloatMethodV)(JNIEnv*, jobject, jclass,
jmethodID, va_list);
jfloat (*CallNonvirtualFloatMethodA)(JNIEnv*, jobject, jclass,
jmethodID, const jvalue*);
jdouble (*CallNonvirtualDoubleMethod)(JNIEnv*, jobject, jclass,
jmethodID, ...);
jdouble (*CallNonvirtualDoubleMethodV)(JNIEnv*, jobject, jclass,
jmethodID, va_list);
jdouble (*CallNonvirtualDoubleMethodA)(JNIEnv*, jobject, jclass,
jmethodID, const jvalue*);
void (*CallNonvirtualVoidMethod)(JNIEnv*, jobject, jclass,
jmethodID, ...);
void (*CallNonvirtualVoidMethodV)(JNIEnv*, jobject, jclass,
jmethodID, va_list);
void (*CallNonvirtualVoidMethodA)(JNIEnv*, jobject, jclass,
jmethodID, const jvalue*);
jfieldID (*GetFieldID)(JNIEnv*, jclass, const char*, const char*);
jobject (*GetObjectField)(JNIEnv*, jobject, jfieldID);
jboolean (*GetBooleanField)(JNIEnv*, jobject, jfieldID);
jbyte (*GetByteField)(JNIEnv*, jobject, jfieldID);
jchar (*GetCharField)(JNIEnv*, jobject, jfieldID);
jshort (*GetShortField)(JNIEnv*, jobject, jfieldID);
jint (*GetIntField)(JNIEnv*, jobject, jfieldID);
jlong (*GetLongField)(JNIEnv*, jobject, jfieldID);
jfloat (*GetFloatField)(JNIEnv*, jobject, jfieldID);
jdouble (*GetDoubleField)(JNIEnv*, jobject, jfieldID);
void (*SetObjectField)(JNIEnv*, jobject, jfieldID, jobject);
void (*SetBooleanField)(JNIEnv*, jobject, jfieldID, jboolean);
void (*SetByteField)(JNIEnv*, jobject, jfieldID, jbyte);
void (*SetCharField)(JNIEnv*, jobject, jfieldID, jchar);
void (*SetShortField)(JNIEnv*, jobject, jfieldID, jshort);
void (*SetIntField)(JNIEnv*, jobject, jfieldID, jint);
void (*SetLongField)(JNIEnv*, jobject, jfieldID, jlong);
void (*SetFloatField)(JNIEnv*, jobject, jfieldID, jfloat);
void (*SetDoubleField)(JNIEnv*, jobject, jfieldID, jdouble);
jmethodID (*GetStaticMethodID)(JNIEnv*, jclass, const char*, const char*);
jobject (*CallStaticObjectMethod)(JNIEnv*, jclass, jmethodID, ...);
jobject (*CallStaticObjectMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jobject (*CallStaticObjectMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jboolean (*CallStaticBooleanMethod)(JNIEnv*, jclass, jmethodID, ...);
jboolean (*CallStaticBooleanMethodV)(JNIEnv*, jclass, jmethodID,
va_list);
jboolean (*CallStaticBooleanMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jbyte (*CallStaticByteMethod)(JNIEnv*, jclass, jmethodID, ...);
jbyte (*CallStaticByteMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jbyte (*CallStaticByteMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jchar (*CallStaticCharMethod)(JNIEnv*, jclass, jmethodID, ...);
jchar (*CallStaticCharMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jchar (*CallStaticCharMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jshort (*CallStaticShortMethod)(JNIEnv*, jclass, jmethodID, ...);
jshort (*CallStaticShortMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jshort (*CallStaticShortMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jint (*CallStaticIntMethod)(JNIEnv*, jclass, jmethodID, ...);
jint (*CallStaticIntMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jint (*CallStaticIntMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jlong (*CallStaticLongMethod)(JNIEnv*, jclass, jmethodID, ...);
jlong (*CallStaticLongMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jlong (*CallStaticLongMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jfloat (*CallStaticFloatMethod)(JNIEnv*, jclass, jmethodID, ...);
jfloat (*CallStaticFloatMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jfloat (*CallStaticFloatMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jdouble (*CallStaticDoubleMethod)(JNIEnv*, jclass, jmethodID, ...);
jdouble (*CallStaticDoubleMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jdouble (*CallStaticDoubleMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
void (*CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...);
void (*CallStaticVoidMethodV)(JNIEnv*, jclass, jmethodID, va_list);
void (*CallStaticVoidMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jfieldID (*GetStaticFieldID)(JNIEnv*, jclass, const char*,
const char*);
jobject (*GetStaticObjectField)(JNIEnv*, jclass, jfieldID);
jboolean (*GetStaticBooleanField)(JNIEnv*, jclass, jfieldID);
jbyte (*GetStaticByteField)(JNIEnv*, jclass, jfieldID);
jchar (*GetStaticCharField)(JNIEnv*, jclass, jfieldID);
jshort (*GetStaticShortField)(JNIEnv*, jclass, jfieldID);
jint (*GetStaticIntField)(JNIEnv*, jclass, jfieldID);
jlong (*GetStaticLongField)(JNIEnv*, jclass, jfieldID);
jfloat (*GetStaticFloatField)(JNIEnv*, jclass, jfieldID);
jdouble (*GetStaticDoubleField)(JNIEnv*, jclass, jfieldID);
void (*SetStaticObjectField)(JNIEnv*, jclass, jfieldID, jobject);
void (*SetStaticBooleanField)(JNIEnv*, jclass, jfieldID, jboolean);
void (*SetStaticByteField)(JNIEnv*, jclass, jfieldID, jbyte);
void (*SetStaticCharField)(JNIEnv*, jclass, jfieldID, jchar);
void (*SetStaticShortField)(JNIEnv*, jclass, jfieldID, jshort);
void (*SetStaticIntField)(JNIEnv*, jclass, jfieldID, jint);
void (*SetStaticLongField)(JNIEnv*, jclass, jfieldID, jlong);
void (*SetStaticFloatField)(JNIEnv*, jclass, jfieldID, jfloat);
void (*SetStaticDoubleField)(JNIEnv*, jclass, jfieldID, jdouble);
jstring (*NewString)(JNIEnv*, const jchar*, jsize);
jsize (*GetStringLength)(JNIEnv*, jstring);
const jchar* (*GetStringChars)(JNIEnv*, jstring, jboolean*);
void (*ReleaseStringChars)(JNIEnv*, jstring, const jchar*);
jstring (*NewStringUTF)(JNIEnv*, const char*);
jsize (*GetStringUTFLength)(JNIEnv*, jstring);
/* JNI spec says this returns const jbyte*, but that's inconsistent */
const char* (*GetStringUTFChars)(JNIEnv*, jstring, jboolean*);
void (*ReleaseStringUTFChars)(JNIEnv*, jstring, const char*);
jsize (*GetArrayLength)(JNIEnv*, jarray);
jobjectArray (*NewObjectArray)(JNIEnv*, jsize, jclass, jobject);
jobject (*GetObjectArrayElement)(JNIEnv*, jobjectArray, jsize);
void (*SetObjectArrayElement)(JNIEnv*, jobjectArray, jsize, jobject);
jbooleanArray (*NewBooleanArray)(JNIEnv*, jsize);
jbyteArray (*NewByteArray)(JNIEnv*, jsize);
jcharArray (*NewCharArray)(JNIEnv*, jsize);
jshortArray (*NewShortArray)(JNIEnv*, jsize);
jintArray (*NewIntArray)(JNIEnv*, jsize);
jlongArray (*NewLongArray)(JNIEnv*, jsize);
jfloatArray (*NewFloatArray)(JNIEnv*, jsize);
jdoubleArray (*NewDoubleArray)(JNIEnv*, jsize);
jboolean* (*GetBooleanArrayElements)(JNIEnv*, jbooleanArray, jboolean*);
jbyte* (*GetByteArrayElements)(JNIEnv*, jbyteArray, jboolean*);
jchar* (*GetCharArrayElements)(JNIEnv*, jcharArray, jboolean*);
jshort* (*GetShortArrayElements)(JNIEnv*, jshortArray, jboolean*);
jint* (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*);
jlong* (*GetLongArrayElements)(JNIEnv*, jlongArray, jboolean*);
jfloat* (*GetFloatArrayElements)(JNIEnv*, jfloatArray, jboolean*);
jdouble* (*GetDoubleArrayElements)(JNIEnv*, jdoubleArray, jboolean*);
void (*ReleaseBooleanArrayElements)(JNIEnv*, jbooleanArray,
jboolean*, jint);
void (*ReleaseByteArrayElements)(JNIEnv*, jbyteArray,
jbyte*, jint);
void (*ReleaseCharArrayElements)(JNIEnv*, jcharArray,
jchar*, jint);
void (*ReleaseShortArrayElements)(JNIEnv*, jshortArray,
jshort*, jint);
void (*ReleaseIntArrayElements)(JNIEnv*, jintArray,
jint*, jint);
void (*ReleaseLongArrayElements)(JNIEnv*, jlongArray,
jlong*, jint);
void (*ReleaseFloatArrayElements)(JNIEnv*, jfloatArray,
jfloat*, jint);
void (*ReleaseDoubleArrayElements)(JNIEnv*, jdoubleArray,
jdouble*, jint);
void (*GetBooleanArrayRegion)(JNIEnv*, jbooleanArray,
jsize, jsize, jboolean*);
void (*GetByteArrayRegion)(JNIEnv*, jbyteArray,
jsize, jsize, jbyte*);
void (*GetCharArrayRegion)(JNIEnv*, jcharArray,
jsize, jsize, jchar*);
void (*GetShortArrayRegion)(JNIEnv*, jshortArray,
jsize, jsize, jshort*);
void (*GetIntArrayRegion)(JNIEnv*, jintArray,
jsize, jsize, jint*);
void (*GetLongArrayRegion)(JNIEnv*, jlongArray,
jsize, jsize, jlong*);
void (*GetFloatArrayRegion)(JNIEnv*, jfloatArray,
jsize, jsize, jfloat*);
void (*GetDoubleArrayRegion)(JNIEnv*, jdoubleArray,
jsize, jsize, jdouble*);
/* spec shows these without const; some jni.h do, some don't */
void (*SetBooleanArrayRegion)(JNIEnv*, jbooleanArray,
jsize, jsize, const jboolean*);
void (*SetByteArrayRegion)(JNIEnv*, jbyteArray,
jsize, jsize, const jbyte*);
void (*SetCharArrayRegion)(JNIEnv*, jcharArray,
jsize, jsize, const jchar*);
void (*SetShortArrayRegion)(JNIEnv*, jshortArray,
jsize, jsize, const jshort*);
void (*SetIntArrayRegion)(JNIEnv*, jintArray,
jsize, jsize, const jint*);
void (*SetLongArrayRegion)(JNIEnv*, jlongArray,
jsize, jsize, const jlong*);
void (*SetFloatArrayRegion)(JNIEnv*, jfloatArray,
jsize, jsize, const jfloat*);
void (*SetDoubleArrayRegion)(JNIEnv*, jdoubleArray,
jsize, jsize, const jdouble*);
jint (*RegisterNatives)(JNIEnv*, jclass, const JNINativeMethod*,
jint);
jint (*UnregisterNatives)(JNIEnv*, jclass);
jint (*MonitorEnter)(JNIEnv*, jobject);
jint (*MonitorExit)(JNIEnv*, jobject);
jint (*GetJavaVM)(JNIEnv*, JavaVM**);
void (*GetStringRegion)(JNIEnv*, jstring, jsize, jsize, jchar*);
void (*GetStringUTFRegion)(JNIEnv*, jstring, jsize, jsize, char*);
void* (*GetPrimitiveArrayCritical)(JNIEnv*, jarray, jboolean*);
void (*ReleasePrimitiveArrayCritical)(JNIEnv*, jarray, void*, jint);
const jchar* (*GetStringCritical)(JNIEnv*, jstring, jboolean*);
void (*ReleaseStringCritical)(JNIEnv*, jstring, const jchar*);
jweak (*NewWeakGlobalRef)(JNIEnv*, jobject);
void (*DeleteWeakGlobalRef)(JNIEnv*, jweak);
jboolean (*ExceptionCheck)(JNIEnv*);
jobject (*NewDirectByteBuffer)(JNIEnv*, void*, jlong);
void* (*GetDirectBufferAddress)(JNIEnv*, jobject);
jlong (*GetDirectBufferCapacity)(JNIEnv*, jobject);
/* added in JNI 1.6 */
jobjectRefType (*GetObjectRefType)(JNIEnv*, jobject);
};
第三个参数,JNINativeMethod
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
这三个参数分别代表java层方法名;参数和返回值;函数指针。
jniRegisterNativeMethods通过findClass(env, className)找到声明的native方法,通过e->RegisterNatives(c.get(), gMethods, numMethods)将注册函数的Java类,以及注册函数的数组,以及个数注册在一起,这样就实现了绑定。
那怎样会调用到jniRegisterNativeMethods呢?
可以在JNI_OnLoad函数中实现。
/*
* Prototypes for functions exported by loadable shared libs. These are
* called by JNI, not provided by JNI.
*/
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved);
JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved);
到这,需要实现的函数已经介绍了,我们接下来看一下Demo怎么改写成动态注册。
先把之前javah生成的头文件com_howie_ndkdemo_NDKTools.h删除掉,然后修改com_howie_ndkdemo_NDKTools.cpp。
#include <jni.h>
static const char *howieclassName = "com/howie/ndkdemo/NDKTools";
static jstring jni_sayHello(JNIEnv* jniEnv, jclass clazz) {
return jniEnv -> NewStringUTF("Hello, Howie");
}
static JNINativeMethod howieMethods[] = {
{"getTextFromNDK", "()Ljava/lang/String;", (jstring*) jni_sayHello},
};
static int register_howie_jni_native_methods(JNIEnv* env, const char* className,
const JNINativeMethod* gMethods, int numMethods) {
jclass clazz;
clazz = env -> FindClass(className);
if (clazz == nullptr) {
return JNI_FALSE;
}
jint result = env -> RegisterNatives(clazz, gMethods, numMethods);
if (result < 0) {
return JNI_FALSE;
}
return JNI_TRUE;
}
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env = nullptr;
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
return result;
}
register_howie_jni_native_methods(env, howieclassName, howieMethods, ((int) (sizeof(howieMethods) / sizeof((howieMethods)[0]))));
return JNI_VERSION_1_4;
}
2、 Native ==> Java
先前讲的都是从Java层调用Native层的方法,那怎样从native调用Java层的代码呢?其实实现原理差不多。
- 获取类实例
- 获得构造函数
- 创建对象
- 获得方法ID
- 调用方法
直接看代码。我们先在java层实现一个获取当前时间的方法
public String getCurrentTime() {
long mNow = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
Calendar c = Calendar.getInstance();
c.setTimeInMillis(mNow);
sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
return sb.toString();
}
然后在JNI层添加一个函数调用它。
jstring jni_getCurrentTime(JNIEnv* jniEnv) {
// 获取类实例
jclass clazz = jniEnv -> FindClass(howieclassName);
if (clazz == nullptr) {
return jniEnv -> NewStringUTF("error!");
}
// 获得构造函数
jmethodID jmethodId = jniEnv -> GetMethodID(clazz, "<init>", "()V");
// 创建对象
jobject obj = jniEnv -> NewObject(clazz, jmethodId);
// 获取方法id
jmethodID jmethodId1 = jniEnv -> GetMethodID(clazz, "getCurrentTime", "()Ljava/lang/String;");
// 调用方法
auto str = (jstring)jniEnv ->CallObjectMethod(obj, jmethodId1);
return str;
}
现在我们想看一下效果,得有个函数调用jni_getCurrentTime,没想到什么好方法,就在java再注册一个方法来调用吧。
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String text = NDKTools.getInstance().main();
Log.i("NDK Demo ","text=" + text);
textView.setText(text);
}
});
package com.howie.ndkdemo;
import java.util.Calendar;
public class NDKTools {
private static NDKTools ndkTools;
static {
System.loadLibrary("hello-howie-jni");
}
public static NDKTools getInstance() {
if (ndkTools == null) {
ndkTools = new NDKTools();
}
return ndkTools;
}
private NDKTools() {}
public native String getTextFromNDK();
public native String callgetCurrentTime();
public String getCurrentTime() {
long mNow = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
Calendar c = Calendar.getInstance();
c.setTimeInMillis(mNow);
sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
return sb.toString();
}
public static String main() {
return getInstance().callgetCurrentTime();
}
}
jstring jni_call_getCurrentTime(JNIEnv* jniEnv, jclass clazz) {
return jni_getCurrentTime(jniEnv);
}
static JNINativeMethod howieMethods[] = {
{"getTextFromNDK", "()Ljava/lang/String;", (jstring*) jni_sayHello},
{"callgetCurrentTime", "()Ljava/lang/String;", (jstring*) jni_call_getCurrentTime},
};
这样就完成了一个native调用java的demo,看一下效果。
jni中函数的更多用处在C_JNIEnv这个函数指针表中,这里不去过多介绍,需要用到的时候去使用对应的函数即可。
3、Demo下载
https://download.csdn.net/download/hihan_5/20041272