JAVA和C/C++之间的相互调用。

时间:2024-01-07 10:02:32

在一些Android应用的开发中,需要通过JNI和 Android NDK工具实现JAVA和C/C++之间的相互调用。

Java Native Interface (JNI)标准是java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI是本地编程接口,它使得在 Java 虚拟机 (VM)内部运行的 Java代码能够与用其它编程语言(如C、C++和汇编语言)编写的应用程序和库进行交互操作。

由于Android的应用层的类都是以Java写的,这些Java类编译为Dex型式的Bytecode之后,必须靠Dalvik虚拟机(VM: Virtual Machine)来执行。在执行Java类的过程中,如果Java类需要与C组件沟通时,VM就会去载入C组件,然后让Java的函数顺利地调用到C组件的函数。此时,VM扮演着桥梁的角色,让Java与C组件能通过标准的JNI介面而相互沟通。

在实际应用中这两者之间的调用关系可以归纳为以下四种方式:

1.       在应用的JAVA代码中调用NDK中C/C++实现的函数。

2.       在NDK开发中的C/C++代码调用应用中JAVA类的静态函数。

3.       在NDK开发中的C/C++代码调用应用中JAVA类当前传入NDK中的实例的函数。

4.       在NDK开发中的C/C++代码调用应用中JAVA类新建实例的函数。

下面我们就怎样在Eclipse中实现JNI编码和四种调用方式加以阐述。

一、在Eclipse中建立一个包含JNI开发的工程。

在这里我们不直接导入NDK中的hello-jni来说明JNI的使用方法。而是新建立一个工程,来说明怎样建立一个包含JNI的工程。

第一步:建立一个Andriod工程JniDemo,在该工程的根目录下建立一个叫jni的目录,在jni目录下建立一个叫Android.mk的文件,(当然你也可以从其他地方,比如ndk样例代码hello-jni中将里面的Android.mk复制过来修改)。 Android.mk里面的内容如下所示

LOCAL_PATH :=$(call my-dir)

include$(CLEAR_VARS)

LOCAL_MODULE   := demo-jni

LOCAL_SRC_FILES := demo-jni.c

include$(BUILD_SHARED_LIBRARY)

关于这几句话的含义,在这里不再赘述。网上搜下,就可以很明白。

然后在jni目录下生成demo-jni.c文件。实现的接口的内容。

现在选中工程中的jni目录,点击鼠标右键,选Refresh,jni目录中的文件就显示在工程的jni目录下了。

第二步:设置jni的编译环境。选中工程中的根目录JniDemo,点击鼠标右键,选Properties。弹出对话框,选中列表中的Builders。如图一所示:

JAVA和C/C++之间的相互调用。

图一:JniDemo特性设置对话框

点击对话框右端的new按钮,弹出“Choose configuration type”对话框,如图二,选择Program,点击对话框下面的OK按钮。

JAVA和C/C++之间的相互调用。

图二:选择配置类型

现在我们打开了”Edit Configuration”对话框,在Name对应的文本框中输入名字JniBuilder(当然也可是你喜欢的其他名字).在Main选项下,在Location中输入cygwin系统中bash.exe的绝对路径。我这里是c:\cygwin\bin\bash.exe(c:\cygwin\为我的系统中cygwin的安装目录,这里要根据你的电脑中cygwin的安装目录来确定),在Working Directory中输入c:\cygwin\bin\.在Arguments中输入--login -c "cd /cygdrive/d/study/JniDemo && /cygdrive/d/android-ndk-r6b/ndk-build"。这里/cygdrive/d/study/JniDemo为工程根目录, /cygdrive/d/android-ndk-r6b为NDK的安装目录。这两个目录参数根据你的工程目录和ndk的安装目录而定。注意的是驱动器要采用cygwin的方式。(比如:Windows系统下的D:对应/cygdrive/d,其余类推)。设置结果如图三所示,然后点击 OK按钮即可。

JAVA和C/C++之间的相互调用。

图三:编辑JNI配置参数

二、演示四种调用方式

演示界面如图四所示,四个按钮分别测试四种调用方式。

JAVA和C/C++之间的相互调用。

图四:演示界面图

分别点击按钮Test1, Test2, Tes3, Test四的测试结果如图五、六、七、八所示。

JAVA和C/C++之间的相互调用。

图五:点击Test1的测试结果

JAVA和C/C++之间的相互调用。

图六:点击Test2的测试结果

JAVA和C/C++之间的相互调用。

图七:点击Test3的测试结果

JAVA和C/C++之间的相互调用。

图八:点击Test4的测试结果

Test1演示在应用中调用NDK中C/C++实现的函数。JAVA代码和C代码分别为:

JAVA 代码:

  1. Button btn01 = (Button)findViewById(R.id.Button01);
  2. btn01.setOnClickListener(new Button.OnClickListener()
  3. {
  4. publicvoid onClick(View v)
  5. {
  6. TextView tv = (TextView)findViewById(R.id.tv01);
  7. tv.setText(stringFromJNI());
  8. showMessage(JniDemoActivity.this,"JNI test1", stringFromJNI());
  9. }
  10. });

C代码:

  1. Jstring Java_study_jnidemo_JniDemoActivity_stringFromJNI( JNIEnv* env,jobject thiz )
  2. {
  3. return (*env)->NewStringUTF(env,"JniDemo, Hello from JNI!");
  4. }

l         Test2 静态调用。JAVA代码和C代码分别为:

JAVA 代码:

  1. // 测试C/C++中对JAVA函数的静态回调
  2. Button btn02 = (Button)findViewById(R.id.Button02);
  3. btn02.setOnClickListener(new Button.OnClickListener()
  4. {
  5. publicvoid onClick(View v)
  6. {
  7. int ret =jniStaticShowMessage(JniDemoActivity.this,"JNI test2","test static callback Message");
  8. TextView tv = (TextView)findViewById(R.id.tv01);
  9. if(ret == 0)
  10. {
  11. tv.setText("test JNI static callback successed");
  12. }
  13. else
  14. {
  15. tv.setText("test JNI static callback fialed");
  16. }
  17. }
  18. });

C代码:

  1. Jint Java_study_jnidemo_JniDemoActivity_jniStaticShowMessage(JNIEnv* env,
  1. jobject thiz,jobject ctx, jstring strTitle, jstring strMessage)
  2. {
  3. jclass cls = (*env)->FindClass(env, "study/jnidemo/JniDemoActivity");
  4. //  jclass cls = (*env)->GetObjectClass(env, thiz);
  5. if(cls != NULL)
  6. {
  7. jmethodID id = (*env)->GetStaticMethodID(env, cls,"staticShowMessage",
  8. "(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)I");
  9. if(id != NULL)
  10. {
  11. return (*env)->CallStaticIntMethod(env, cls, id, ctx, strTitle, strMessage);
  12. }
  13. }
  14. return 1;
  15. }

l         Test3 当前实例调用:JAVA代码和C代码分别为:

JAVA 代码:

  1. Button btn03 = (Button)findViewById(R.id.Button03);
  2. btn03.setOnClickListener(new Button.OnClickListener()
  3. {
  4. publicvoid onClick(View v)
  5. {
  6. strTest = " [message has changed now]";
  7. int ret = jniShowMessage(JniDemoActivity.this,"JNI test3","test callback in current instance");
  8. TextView tv = (TextView)findViewById(R.id.tv01);
  9. if(ret == 0)
  10. {
  11. tv.setText("test JNI callback successed");
  12. }
  13. else
  14. {
  15. tv.setText("test JNI callback fialed");
  16. }
  17. }
  18. });

C代码:

  1. Jint Java_study_jnidemo_JniDemoActivity_jniShowMessage(JNIEnv* env, jobject thiz,
  2. jobject ctx, jstring strTitle, jstring strMessage)
  3. {
  4. jclass cls = (*env)->GetObjectClass(env, thiz);
  5. if(cls != NULL)
  6. {
  7. jstring str;
  8. jmethodID strTest_id = (*env)->GetMethodID(env, cls, "getTestString","()Ljava/lang/String;");
  9. if(strTest_id != NULL)
  10. {
  11. str = (*env)->CallObjectMethod(env, thiz, strTest_id);
  12. }
  13. jmethodID showMessage_id = (*env)->GetMethodID(env, cls, "showMessage",
  14. "(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)I");
  15. if(showMessage_id != NULL)
  16. {
  17. return (*env)->CallIntMethod(env, thiz, showMessage_id, ctx,
  18. strTitle, combine_jstring(env, strMessage, str));
  19. }
  20. }
  21. return 1;
  22. }

l         Test4新建实例调用:JAVA代码和C代码分别为:

JAVA 代码:

  1. Button btn04 = (Button)findViewById(R.id.Button04);
  2. btn04.setOnClickListener(newButton.OnClickListener()
  3. {
  4. publicvoid onClick(View v)
  5. {
  6. strTest = " [message has changed now]";
  7. int ret = jniInstanceShowMessage(JniDemoActivity.this,
  8. JNI test4","test callback in new instance");
  9. TextView tv = (TextView)findViewById(R.id.tv01);
  10. if(ret == 0)
  11. {
  12. tv.setText("test JNI new instance successed");
  13. }
  14. else
  15. {
  16. tv.setText("test JNI new instance fialed");
  17. }
  18. }
  19. });

C代码:

  1. jint Java_study_jnidemo_JniDemoActivity_jniInstanceShowMessage(JNIEnv* env, jobject thiz,
  2. jobject ctx, jstring strTitle, jstring strMessage)
  3. {
  4. jclass cls = (*env)->FindClass(env, "study/jnidemo/JniDemoActivity");
  5. if(cls != NULL)
  6. {
  7. // get instance
  8. jmethodID constuctor_id = (*env)->GetMethodID(env, cls, "<init>", "()V");
  9. if(constuctor_id != NULL)
  10. {
  11. jobject obj = (*env)->NewObject(env, cls, constuctor_id);
  12. if(obj != NULL)
  13. {
  14. jstring str;
  15. jmethodID strTest_id = (*env)->GetMethodID(env, cls, "getTestString","()Ljava/lang/String;");
  16. if(strTest_id != NULL)
  17. {
  18. str = (*env)->CallObjectMethod(env, obj, strTest_id);
  19. }
  20. jmethodID showMessage_id = (*env)->GetMethodID(env, cls,"showMessage",
  21. "(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)I");
  22. if(showMessage_id != NULL)
  23. {
  24. return (*env)->CallIntMethod(env, obj, showMessage_id, ctx,strTitle, combine_jstring(env, strMessage, str));
  25. }
  26. }
  27. }
  28. }
  29. return 1;
  30. }

Test1 和Test2都是常规的调用,在这里不做解释了。现在我们看看Test3和Test4的区别,在Test3中,strTest = " [message has changed now]" 在相应的代码中都做了赋值。但是在Test4中并没有改变,还是初始值。这是因为Test创建了一个新实例,和应用的JAVA代码中所赋值的实例并不是同一个。因此才出现了不同的结果。

附完整的JAVA和C代码

JAVAD代码: JniDemoActivity.java

  1. package study.jnidemo;
  2. import android.app.Activity;
  3. import android.app.AlertDialog;
  4. import android.os.Bundle;
  5. import android.widget.Button;
  6. import android.view.View;
  7. import android.widget.TextView;
  8. import android.content.Context;
  9. import android.content.DialogInterface;
  10. publicclass JniDemoActivityextends Activity {
  11. public StringstrTest =" [initial message]";
  12. /** Called when the activity is first created. */
  13. @Override
  14. publicvoid onCreate(Bundle savedInstanceState) {
  15. super.onCreate(savedInstanceState);
  16. setContentView(R.layout.main);
  17. findAndModifyButton();
  18. }
  19. public String getTestString()
  20. {
  21. returnstrTest;
  22. }
  23. // 测试JAVA的NDK调用
  24. publicnative String stringFromJNI();
  25. // 测试C/C++中对JAVA函数的静态回调
  26. publicnativestaticint jniStaticShowMessage(Context ctx, String strTitle, String strMessage);
  27. // 测试实例中C/C++中对JAVA类的函数的调用
  28. publicnativeint jniShowMessage(Context ctx, String strTitle, String strMessage);
  29. // 测试创建新实例C/C++对JAVA类的函数的调用
  30. publicnativeint jniInstanceShowMessage(Context ctx, String strTitle, String strMessage);
  31. static {
  32. System.loadLibrary("demo-jni");
  33. }
  34. staticint staticShowMessage(Context ctx, String strTitle, String strMessage)
  35. {
  36. AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
  37. builder.setTitle(strTitle);
  38. builder.setMessage(strMessage);
  39. builder.setPositiveButton("确定",
  40. new DialogInterface.OnClickListener(){
  41. public void onClick(DialogInterface dialog,int whichButton){
  42. }
  43. });
  44. builder.show();
  45. return 0;
  46. }
  47. publicint showMessage(Context ctx, String strTitle, String strMessage)
  48. {
  49. returnstaticShowMessage(ctx, strTitle, strMessage);
  50. }
  51. privatevoid findAndModifyButton()
  52. {
  53. // 测试JAVA的NDK调用
  54. Button btn01 = (Button)findViewById(R.id.Button01);
  55. btn01.setOnClickListener(new Button.OnClickListener()
  56. {
  57. publicvoid onClick(View v)
  58. {
  59. TextView tv = (TextView)findViewById(R.id.tv01);
  60. tv.setText(stringFromJNI());
  61. showMessage(JniDemoActivity.this,"JNI test1", stringFromJNI());
  62. }
  63. });
  64. // 测试C/C++中对JAVA函数的静态回调
  65. Button btn02 = (Button)findViewById(R.id.Button02);
  66. btn02.setOnClickListener(new Button.OnClickListener()
  67. {
  68. publicvoid onClick(View v)
  69. {
  70. int ret =jniStaticShowMessage(JniDemoActivity.this,"JNI test2", "test static callback Message");
  71. TextView tv = (TextView)findViewById(R.id.tv01);
  72. if(ret == 0)
  73. {
  74. tv.setText("test JNI static callback successed");
  75. }
  76. else
  77. {
  78. tv.setText("test JNI static callback fialed");
  79. }
  80. }
  81. });
  82. // 测试实例中C/C++中对JAVA类的函数的调用
  83. Button btn03 = (Button)findViewById(R.id.Button03);
  84. btn03.setOnClickListener(new Button.OnClickListener()
  85. {
  86. publicvoid onClick(View v)
  87. {
  88. strTest = " [message has changed now]";
  89. int ret = jniShowMessage(JniDemoActivity.this,"JNI test3", "test callback in current instance");
  90. TextView tv = (TextView)findViewById(R.id.tv01);
  91. if(ret == 0)
  92. {
  93. tv.setText("test JNI callback successed");
  94. }
  95. else
  96. {
  97. tv.setText("test JNI callback fialed");
  98. }
  99. }
  100. });
  101. // 测试创建新实例C/C++对JAVA类的函数的调用
  102. Button btn04 = (Button)findViewById(R.id.Button04);
  103. btn04.setOnClickListener(new Button.OnClickListener()
  104. {
  105. publicvoid onClick(View v)
  106. {
  107. strTest = " [message has changed now]";
  108. int ret = jniInstanceShowMessage(JniDemoActivity.this,"JNI test4", "test callback in new instance");
  109. TextView tv = (TextView)findViewById(R.id.tv01);
  110. if(ret == 0)
  111. {
  112. tv.setText("test JNI new instance successed");
  113. }
  114. else
  115. {
  116. tv.setText("test JNI new instance fialed");
  117. }
  118. }
  119. });
  120. }
  121. }

C代码 demo-jni.cpp

  1. #include<string.h>
  2. #include<jni.h>
  3. // 加载此动态库时系统自动首先加载
  4. jint JNI_OnLoad(JavaVM* vm, void *reserved)
  5. {
  6. JNIEnv *env;
  7. if((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_4) != JNI_OK)
  8. {
  9. return -1;
  10. }
  11. return JNI_VERSION_1_4;
  12. }
  13. jstring
  14. Java_study_jnidemo_JniDemoActivity_stringFromJNI( JNIEnv* env,jobject thiz )
  15. {
  16. return (*env)->NewStringUTF(env,"JniDemo, Hello from JNI!");
  17. }
  18. jstring
  19. combine_jstring(JNIEnv* env, jstring str1, jstring str2)
  20. {
  21. jboolean b_ret;
  22. constchar *s1 = (*env)->GetStringUTFChars(env, str1, &b_ret);
  23. constchar *s2 = (*env)->GetStringUTFChars(env, str2, &b_ret);
  24. int n1 = strlen(s1);
  25. int n2 = strlen(s2);
  26. char *new_str = (char *)malloc(n1+n2+1);
  27. memset(new_str, 0, n1+n2+1);
  28. strcat(new_str, s1);
  29. strcat(new_str, s2);
  30. jstring ret_str = (*env)->NewStringUTF(env,(constchar *)new_str);
  31. free(new_str);
  32. return ret_str;
  33. }
  34. jint
  35. Java_study_jnidemo_JniDemoActivity_jniStaticShowMessage(JNIEnv* env, jobject thiz,
  36. jobject ctx, jstring strTitle, jstring strMessage)
  37. {
  38. jclass cls = (*env)->FindClass(env, "study/jnidemo/JniDemoActivity");
  39. //  jclass cls = (*env)->GetObjectClass(env, thiz);
  40. if(cls != NULL)
  41. {
  42. jmethodID id = (*env)->GetStaticMethodID(env, cls,
  43. "staticShowMessage",
  44. "(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)I");
  45. if(id != NULL)
  46. {
  47. return (*env)->CallStaticIntMethod(env, cls, id, ctx, strTitle, strMessage);
  48. }
  49. }
  50. return 1;
  51. }
  52. // 在当前已有的JAVA实例中调用
  53. jint
  54. Java_study_jnidemo_JniDemoActivity_jniShowMessage(JNIEnv* env, jobject thiz,
  55. jobject ctx, jstring strTitle, jstring strMessage)
  56. {
  57. jclass cls = (*env)->GetObjectClass(env, thiz);
  58. if(cls != NULL)
  59. {
  60. jstring str;
  61. jmethodID strTest_id = (*env)->GetMethodID(env, cls, "getTestString",
  62. "()Ljava/lang/String;");
  63. if(strTest_id != NULL)
  64. {
  65. str = (*env)->CallObjectMethod(env, thiz, strTest_id);
  66. }
  67. jmethodID showMessage_id = (*env)->GetMethodID(env, cls, "showMessage",
  68. "(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)I");
  69. if(showMessage_id != NULL)
  70. {
  71. return (*env)->CallIntMethod(env, thiz, showMessage_id, ctx,
  72. strTitle, combine_jstring(env, strMessage, str));
  73. }
  74. }
  75. return 1;
  76. }
  77. // 在新建JAVA实例中调用
  78. jint
  79. Java_study_jnidemo_JniDemoActivity_jniInstanceShowMessage(JNIEnv* env, jobject thiz,
  80. jobject ctx, jstring strTitle, jstring strMessage)
  81. {
  82. jclass cls = (*env)->FindClass(env, "study/jnidemo/JniDemoActivity");
  83. if(cls != NULL)
  84. {
  85. // get instance
  86. jmethodID constuctor_id = (*env)->GetMethodID(env, cls, "<init>", "()V");
  87. if(constuctor_id != NULL)
  88. {
  89. jobject obj = (*env)->NewObject(env, cls, constuctor_id);
  90. if(obj != NULL)
  91. {
  92. jstring str;
  93. jmethodID strTest_id = (*env)->GetMethodID(env, cls,"getTestString",
  94. "()Ljava/lang/String;");
  95. if(strTest_id != NULL)
  96. {
  97. str = (*env)->CallObjectMethod(env, obj, strTest_id);
  98. }
  99. jmethodID showMessage_id = (*env)->GetMethodID(env, cls,"showMessage",
  100. "(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)I");
  101. if(showMessage_id != NULL)
  102. {
  103. return (*env)->CallIntMethod(env, obj, showMessage_id, ctx,
  104. strTitle, combine_jstring(env, strMessage, str));
  105. }
  106. }
  107. }
  108. }
  109. return 1;
  110. }

源代码下载地址:http://download.csdn.net/detail/seniorwizard/4394466