在Android 开发过程中,经常会用到JNI,要么就是Java调用JNI,要么就是JNI回调Java。一种习惯的做法是把在Java和JNI 都生成相同名字的Class,并将它们互相绑定,这样双方互相调用也非常方便。
例如定义Java class:
public final class NativeTest {
private static final String TAG = NativeFfmpeg.class.getSimpleName();
static {
System.loadLibrary("test");
NativeTest.nativeClassInit();
}
public static native void nativeClassInit();
private native void native_setup();
private native void native_release();
private native void native_test(int value);
// bind to Jni class
private long mNativeHandle = 0;
private void onResult(int result) {
Log.e(TAG, "result = " + result);
}
}
JNI 文件如下:
#define SAFE_FREE(p) if(p){free(p);p=NULL;}
#define DELETE_LOCAL_REF(env, obj) if(obj!=NULL){env->DeleteLocalRef(obj);obj=NULL;}
#define DELETE_GLOBAL_REF(env, obj) if(obj!=NULL){env->DeleteGlobalRef(obj);obj=NULL;}
#define DELETE_WEAK_GLOBAL_REF(env, obj) if(obj!=NULL){env->DeleteWeakGlobalRef(obj);obj=NULL;}
#define JNI_FIELDID(name, field) fieldID_##name_##field
#define JNI_METHODID(name, method) methodID_##name_##method
#define JNI_DEFINE_FIELDID(name, field) static jfieldID JNI_FIELDID(name, field) = NULL;
#define JNI_DEFINE_METHODID(name, method) static jmethodID JNI_METHODID(name, method) = NULL;
#define JNI_LOAD_FIELDID(env, clazz, name, field, sig) {JNI_FIELDID(name, field) = (env)->GetFieldID(clazz, #field, sig);__android_log_print(ANDROID_LOG_DEBUG, SV_LOGTAG, "fieldID_"#name"_"#field" = %p", JNI_FIELDID(name, field));}
#define JNI_LOAD_METHODID(env, clazz, name, method, sig) {JNI_METHODID(name, method) = (env)->GetMethodID(clazz, #method, sig);__android_log_print(ANDROID_LOG_DEBUG, SV_LOGTAG, "methodID_"#name"_"#method" = %p", JNI_METHODID(name, method));}
JNI_DEFINE_FIELDID(NativeTest, mNativeHandle);
JNI_DEFINE_METHODID(NativeTest, onResult);
class NativeTest
{
private:
jweak mObject;
public:
NativeTest():mObject(NULL){};
~NativeTest(){unbind(NULL)};
public:
void bind(JNIEnv* env, jobject obj)
{
jclass clazz = env->GetObjectClass(obj);
// refer to Java object
mObject = env->NewWeakGlobalRef(obj);
DELETE_LOCAL_REF(env, clazz);
// bind this Jni object to its Java object(reference)
env->SetLongField(mObject, JNI_FIELDID(NativeTest, mNativeHandle), (jlong)this);
}
void unbind(JNIEnv* env)
{
if(env == NULL)
{
jvm->AttachCurrentThread(&env, NULL);
}
env->SetLongField(mObject, JNI_FIELDID(NativeTest, mNativeHandle), (jlong)0);
DELETE_WEAK_GLOBAL_REF(env, mObject);
}
void test(JNIEnv* env, int value)
{
if(env == NULL)
{
jvm->AttachCurrentThread(&env, NULL);
}
if(mObject != NULL && JNI_METHODID(NativeTest, onResult) != NULL)
{
env->CallVoidMethod(mObject, JNI_METHODID(NativeTest, onResult), ++value);
}
}
}
static void nativeClassInit(JNIEnv* env, jclass clazz)
{
jclass clazz = env->FindClass(kNativeTestPath);
JNI_LOAD_FIELDID(env, clazz, NativeTest, mNativeHandle, "J");
JNI_LOAD_METHODID(env, clazz, NativeTest, onResult, "(I)V");
DELETE_LOCAL_REF(env, clazz);
}
static void native_setup(JNIEnv* env, jobject thiz)
{
NativeTest* nt = new NativeTest();
nt->bind(env, thiz);
}
static void native_release(JNIEnv* env, jobject thiz)
{
jlong handle = env->GetLongField(thiz, JNI_FIELDID(NativeTest, mNativeHandle));
NativeTest* nt = (NativeTest*)handle;
if(nt != NULL)
{
nt->unbind(env);
delete nt;
}
}
static void native_test(JNIEnv* env, jobject thiz, jint value)
{
jlong handle = env->GetLongField(thiz, JNI_FIELDID(NativeTest, mNativeHandle));
NativeTest* nt = (NativeTest*)handle;
if(nt != NULL)
{
nt->test(env, value);
}
}
static JNINativeMethod gNativeFfmpegMethod[] =
{
{"nativeClassInit", "()V", (void*)nativeClassInit },
{"native_setup", "()V", (void*)native_setup },
{"native_release", "()V", (void*)native_release },
{"native_test", "(I)V", (void*)native_test },
}