主要是分调用java中的static函数和非static函数。
本地activity code:
public class TestJNIActivity extends Activity { private static final String TAG = "TestJNIActivity"; static{ System.loadLibrary("shift_jni"); } TestJNI jni = new TestJNI(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Button test1 = (Button)findViewById(R.id.test1); test1.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { test1(); } }); Button test2 = (Button)findViewById(R.id.test2); test2.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { test2(); } }); Button test3 = (Button)findViewById(R.id.test3); test3.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { test3(); } }); } private void test1(){ Log.d(TAG, "=====call c method======"+jni.test1()); } private void test2(){ Log.d(TAG, "=====c call java static method======"+jni.test2()); } private void test3(){ Log.d(TAG, "=====c call java not static method======"); jni.test3(); } }
public class TestJNI { private static final String TAG = "TestJNI"; public native int printJNI(); public native int callJavaMethod1(); public native void callJavaMethod2(); /** * java call c method by jni */ public int test1(){ return printJNI(); } public int test2(){ return callJavaMethod1(); } public void test3(){ callJavaMethod2(); } /** * c call java static method by jni */ public static int method1(){ Log.d(TAG, "===method1====="); return 0; } public void method2(String msg){ Log.d(TAG, "===method2===="+msg); } }
#include <jni.h> #include <assert.h> #include "test_jni.h" #include "c2java.h" /* #include "com_shift_testjni_TestJNI.h" JNIEXPORT jint JNICALL Java_com_shift_testjni_TestJNI_printJNI (JNIEnv *env, jobject obj) { ALOGE("====jni test successfully==="); return 0; }*/ JNIEXPORT jint JNICALL printForTest (JNIEnv *env, jobject obj) { LOGD("====printForTest==="); return 0; } JNIEXPORT jint JNICALL callStaticMethod (JNIEnv *env, jobject obj) { int ret = c_2_static_java(env); LOGD("====callStaticMethod==%d===", ret); return ret; } JNIEXPORT void JNICALL callNonstaticMethod (JNIEnv *env, jobject obj) { LOGD("====callNonstaticMethod==%d===", c_2_nonstatic_java(env)); } static JNINativeMethod methods[] = { { "printJNI", "()I", (void*)printForTest}, { "callJavaMethod1", "()I", (void*)callStaticMethod}, { "callJavaMethod2", "()V", (void*)callNonstaticMethod}, }; /* * Register methods for one class. */ static int registerNativeMethods(JNIEnv* env, const char* className, const JNINativeMethod* methods, int numMethods) { int rc; jclass clazz; clazz = (*env)->FindClass(env, className); if (clazz == NULL) { LOGE("Native registration unable to find class '%s'\n", className); return JNI_FALSE; } if (rc = ((*env)->RegisterNatives(env, clazz, methods, numMethods)) < 0) { LOGE("RegisterNatives failed for '%s' %d\n", className, rc); return JNI_FALSE; } return JNI_TRUE; } /* * Register methods for all classes. * * returns JNI_TRUE on success. */ static int registerNatives(JNIEnv* env) { if (!registerNativeMethods(env, "com/shift/testjni/TestJNI", methods, NELEM(methods))){ return JNI_FALSE; } return JNI_TRUE; } /* * Called by the VM when the shared library is loaded. */ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env = NULL; jint result = -1; LOGD("=====JNI_OnLoad=====\n"); if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) goto bail; assert(env != NULL); if (!registerNatives(env)) goto bail; /* success -- return valid version number */ result = JNI_VERSION_1_6; bail: LOGE("Leaving JNI_OnLoad (result=0x%x)\n", result); return result; }之前的code都是很ok的,之前 Android基础总结之九:JNI详解已经有过介绍。
但是实现部分的code在书写调试过程中出现了问题:
#include <jni.h> #include "c2java.h" #include "test_jni.h" jclass TestJNI; jobject mTestJNI; jmethodID c2static; jmethodID c2nonstatic; int init(JNIEnv *env) { LOGD("====init====="); if(TestJNI == NULL) { TestJNI = (*env)->FindClass(env, "com/shift/testjni/TestJNI"); if(TestJNI == NULL) return -1; } if(mTestJNI == NULL) { if(getInstance(env) != 1) { (*env)->DeleteLocalRef(env, TestJNI); return -2; } } if(c2static == NULL) { c2static = (*env)->GetStaticMethodID(env, TestJNI, "method1", "()I"); if(c2static == NULL) { (*env)->DeleteLocalRef(env, TestJNI); (*env)->DeleteLocalRef(env, mTestJNI); return -3; } } if(c2nonstatic ==NULL) { c2nonstatic = (*env)->GetMethodID(env, TestJNI, "method2", "(Ljava/lang/String;)V"); if(c2nonstatic == NULL) { (*env)->DeleteLocalRef(env, TestJNI); (*env)->DeleteLocalRef(env, mTestJNI); (*env)->DeleteLocalRef(env, c2static); return -4; } } return JNI_OK; } int getInstance(JNIEnv *env){ LOGD("====getInstance===="); jmethodID id = (*env)->GetMethodID(env, TestJNI, "<init>", "()V");//默认构造函数,不传参数 if(id == 0) return -1; mTestJNI = (*env)->NewObject(env, TestJNI, id);//通过NewObject来创建对象 if(mTestJNI == NULL) return -2; return 1; } int c_2_static_java(JNIEnv *env) { int result = -1; if(env == NULL) { return result; } if(TestJNI==NULL || c2static==NULL) { result = init(env); if(result != JNI_OK) return result; } jint java_ret = (*env)->CallStaticIntMethod(env, TestJNI, c2static);//CallStatic***Method,关键字static,***代表返回类型 LOGD("==c_2_static_java==%d=", java_ret); return JNI_OK; } int c_2_nonstatic_java(JNIEnv *env) { int result = -1; if(env == NULL) { return result; } if(TestJNI==NULL || mTestJNI==NULL || c2nonstatic==NULL) { result = init(env); if(result != JNI_OK) return result; } jstring jstrMSG = NULL; jstrMSG =(*env)->NewStringUTF(env, "I'm From C"); (*env)->CallVoidMethod(env, mTestJNI, c2nonstatic, jstrMSG);//Call***Method,***代表返回类型 (*env)->DeleteLocalRef(env, jstrMSG); return JNI_OK; }
c2java中如果这样写的话,会出现一个问题,第一次调用java函数时是ok的,但是第二次调用就会出现:
05-20 07:01:50.048: D/Shift_Test_JNI(13050): ====printForTest=== 05-20 07:01:50.048: D/TestJNIActivity(13050): =====call c method======0 05-20 07:01:50.958: D/Shift_Test_JNI(13050): ====init===== 05-20 07:01:50.978: D/Shift_Test_JNI(13050): ====getInstance==== 05-20 07:01:50.978: D/TestJNI(13050): ===method1===== 05-20 07:01:50.978: D/Shift_Test_JNI(13050): ==c_2_static_java==0= 05-20 07:01:50.978: D/Shift_Test_JNI(13050): ====callStaticMethod==0=== 05-20 07:01:50.978: D/TestJNIActivity(13050): =====c call java static method======0 05-20 07:01:54.568: E/dalvikvm(13050): JNI ERROR (app bug): accessed stale local reference 0xd7d0001d (index 7 in a table of size 7) 05-20 07:01:54.568: W/dalvikvm(13050): JNI WARNING: jclass is an invalid local reference (0xd7d0001d) (CallStaticIntMethod) 05-20 07:01:54.568: W/dalvikvm(13050): in Lcom/shift/testjni/TestJNI;.callJavaMethod1:()I (CallStaticIntMethod) 05-20 07:01:54.578: I/dalvikvm(13050): "main" prio=5 tid=1 RUNNABLE 05-20 07:01:54.578: I/dalvikvm(13050): | group="main" sCount=0 dsCount=0 obj=0xb3b03ca8 self=0xb8e0c398 05-20 07:01:54.578: I/dalvikvm(13050): | sysTid=13050 nice=0 sched=0/0 cgrp=apps handle=-1224810156 05-20 07:01:54.578: I/dalvikvm(13050): | state=R schedstat=( 960000000 1800000000 263 ) utm=65 stm=31 core=0 05-20 07:01:54.578: I/dalvikvm(13050): at com.shift.testjni.TestJNI.callJavaMethod1(Native Method) 05-20 07:01:54.618: I/dalvikvm(13050): at com.shift.testjni.TestJNI.test2(TestJNI.java:19) 05-20 07:01:54.618: I/dalvikvm(13050): at com.shift.testjni.TestJNIActivity.test2(TestJNIActivity.java:53) 05-20 07:01:54.618: I/dalvikvm(13050): at com.shift.testjni.TestJNIActivity.access$1(TestJNIActivity.java:52) 05-20 07:01:54.618: I/dalvikvm(13050): at com.shift.testjni.TestJNIActivity$2.onClick(TestJNIActivity.java:34) 05-20 07:01:54.618: I/dalvikvm(13050): at android.view.View.performClick(View.java:4438) 05-20 07:01:54.618: I/dalvikvm(13050): at android.view.View$PerformClick.run(View.java:18422) 05-20 07:01:54.618: I/dalvikvm(13050): at android.os.Handler.handleCallback(Handler.java:733) 05-20 07:01:54.628: I/dalvikvm(13050): at android.os.Handler.dispatchMessage(Handler.java:95) 05-20 07:01:54.628: I/dalvikvm(13050): at android.os.Looper.loop(Looper.java:136) 05-20 07:01:54.628: I/dalvikvm(13050): at android.app.ActivityThread.main(ActivityThread.java:5017) 05-20 07:01:54.628: I/dalvikvm(13050): at java.lang.reflect.Method.invokeNative(Native Method) 05-20 07:01:54.628: I/dalvikvm(13050): at java.lang.reflect.Method.invoke(Method.java:515) 05-20 07:01:54.628: I/dalvikvm(13050): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779) 05-20 07:01:54.628: I/dalvikvm(13050): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595) 05-20 07:01:54.628: I/dalvikvm(13050): at dalvik.system.NativeStart.main(Native Method) 05-20 07:01:54.628: E/dalvikvm(13050): VM aborting 05-20 07:01:54.648: A/libc(13050): Fatal signal 6 (SIGABRT) at 0x000032fa (code=-6), thread 13050 (m.shift.testjni)后来查了很多资料,才弄清楚,在c返回java的时候LocalRef都会释放。这样导致了指针的游离,下一次使用就不行了。
后来只能采用GlobalRef,试验证明这样做是ok的。
code如下:
#include <jni.h> #include <stdio.h> #include <string.h> #ifndef _C_TO_JAVA_H_ #define _C_TO_JAVA_H_ struct fields_t { jclass TestJNI; jobject mTestJNI; jmethodID c2static; jmethodID c2nonstatic; }; int c_2_static_java(JNIEnv *env); int c_2_nonstatic_java(JNIEnv *env); #endif
#include <jni.h> #include "c2java.h" #include "test_jni.h" static struct fields_t fields; int getInstance(JNIEnv *env) { LOGD("====getInstance===="); jmethodID id = (*env)->GetMethodID(env, fields.TestJNI, "<init>", "()V"); if(id == 0) return -1; jobject temp = (*env)->NewObject(env, fields.TestJNI, id); if(temp == NULL) return -2; fields.mTestJNI = (*env)->NewGlobalRef(env, temp); (*env)->DeleteLocalRef(env, temp); return 1; } int native_init(JNIEnv *env) { LOGD("====init====="); if(fields.TestJNI == NULL) { jclass temp = (*env)->FindClass(env, "com/shift/testjni/TestJNI"); if(temp == NULL) return -1; fields.TestJNI = (jclass)(*env)->NewGlobalRef(env, temp); (*env)->DeleteLocalRef(env, temp); } if(fields.mTestJNI == NULL) { if(getInstance(env) != 1) { (*env)->DeleteGlobalRef(env, fields.TestJNI); return -2; } } if(fields.c2static == NULL) { fields.c2static = (*env)->GetStaticMethodID(env, fields.TestJNI, "method1", "()I"); if(fields.c2static == NULL) { (*env)->DeleteGlobalRef(env, fields.TestJNI); (*env)->DeleteGlobalRef(env, fields.mTestJNI); return -3; } } if(fields.c2nonstatic ==NULL) { fields.c2nonstatic = (*env)->GetMethodID(env, fields.TestJNI, "method2", "(Ljava/lang/String;)V"); if(fields.c2nonstatic == NULL) { (*env)->DeleteGlobalRef(env, fields.TestJNI); (*env)->DeleteGlobalRef(env, fields.mTestJNI); (*env)->DeleteLocalRef(env, fields.c2static); return -4; } } return JNI_OK; } int c_2_static_java(JNIEnv *env) { int result = -1; if(env == NULL) { return result; } if(fields.TestJNI==NULL || fields.c2static==NULL) { result = native_init(env); if(result != JNI_OK) return result; } jint java_ret = (*env)->CallStaticIntMethod(env, fields.TestJNI, fields.c2static); LOGD("==c_2_static_java==%d=", java_ret); return JNI_OK; } int c_2_nonstatic_java(JNIEnv *env) { int result = -1; if(env == NULL) { return result; } if(fields.TestJNI==NULL || fields.mTestJNI==NULL || fields.c2nonstatic==NULL) { result = native_init(env); if(result != JNI_OK) return result; } jstring jstrMSG = NULL; jstrMSG =(*env)->NewStringUTF(env, "I'm From C"); (*env)->CallVoidMethod(env, fields.mTestJNI, fields.c2nonstatic, jstrMSG); (*env)->DeleteLocalRef(env, jstrMSG); return JNI_OK; }
1. jmethodID id = (*env)->GetMethodID(env, TestJNI, "<init>", "()V");//默认构造函数,不传参数
2. mTestJNI = (*env)->NewObject(env, TestJNI, id);//通过NewObject来创建对象
3. jint java_ret = (*env)->CallStaticIntMethod(env, TestJNI, c2static);//CallStatic***Method,关键字static,***代表返回类型
4. (*env)->CallVoidMethod(env, mTestJNI, c2nonstatic, jstrMSG);//Call***Method,***代表返回类型
5. 前面的几个函数中都会涉及到变量env,而env是java调用c函数时的第一个参数,这意味着c调用java函数只能在java调用c函数中进行,否则无法获取env变量。也就是说c是不能主动调用java函数的。
参考文献:
http://www.ibm.com/developerworks/cn/java/j-lo-jnileak/index.html?ca=drs-