JNI之------C调用java接口

来源:互联网 发布:vue.js window.open 编辑:程序博客网 时间:2024/04/28 18:50

主要是分调用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();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);Button test1 = (Button)findViewById(R.id.test1);test1.setOnClickListener(new OnClickListener() {@Overridepublic 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();}}

本地java code:

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);    }}

native code:

#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======005-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======005-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 RUNNABLE05-20 07:01:54.578: I/dalvikvm(13050):   | group="main" sCount=0 dsCount=0 obj=0xb3b03ca8 self=0xb8e0c39805-20 07:01:54.578: I/dalvikvm(13050):   | sysTid=13050 nice=0 sched=0/0 cgrp=apps handle=-122481015605-20 07:01:54.578: I/dalvikvm(13050):   | state=R schedstat=( 960000000 1800000000 263 ) utm=65 stm=31 core=005-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 aborting05-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-





0 0
原创粉丝点击