Android 中Java 和C/C++的相互调用方法


关键词: Android JNI C/C++ 移动开发

 

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

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

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

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

             1.       在应用的JAVA代码中调用NDKC/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目录,点击鼠标右键,选Refreshjni目录中的文件就显示在工程的jni目录下了。

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

图一:JniDemo特性设置对话框

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

图二:选择配置类型

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

图三:编辑JNI配置参数

 

二、演示四种调用方式

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

图四:演示界面图

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

图五:点击Test1的测试结果

图六:点击Test2的测试结果

图七:点击Test3的测试结果

 

图八:点击Test4的测试结果

 

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

JAVA 代码:

    Button btn01 = (Button)findViewById(R.id.Button01);
    btn01.setOnClickListener(new Button.OnClickListener()
    {
        publicvoid onClick(View v)
        {      
            TextView tv = (TextView)findViewById(R.id.tv01);
            tv.setText(stringFromJNI());            

            showMessage(JniDemoActivity.this,"JNI test1", stringFromJNI());
        }
    });

C代码:

Jstring Java_study_jnidemo_JniDemoActivity_stringFromJNI( JNIEnv* env,jobject thiz )

{
    return (*env)->NewStringUTF(env,"JniDemo, Hello from JNI!");
}


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

JAVA 代码:

    // 测试C/C++中对JAVA函数的静态回调
    Button btn02 = (Button)findViewById(R.id.Button02);
    btn02.setOnClickListener(new Button.OnClickListener()
    {
        publicvoid onClick(View v)
        {
            int ret =jniStaticShowMessage(JniDemoActivity.this,"JNI test2","test static callback Message");
            TextView tv = (TextView)findViewById(R.id.tv01);
            if(ret == 0)
            {
               tv.setText("test JNI static callback successed");
            }
            else
            {
               tv.setText("test JNI static callback fialed");
            }
        }
    });


C代码:

Jint Java_study_jnidemo_JniDemoActivity_jniStaticShowMessage(JNIEnv* env, 
						jobject thiz,jobject ctx, jstring strTitle, jstring strMessage)
{
    jclass cls = (*env)->FindClass(env, "study/jnidemo/JniDemoActivity");
//  jclass cls = (*env)->GetObjectClass(env, thiz);
    if(cls != NULL)
    {
       jmethodID id = (*env)->GetStaticMethodID(env, cls,"staticShowMessage",
			"(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)I");
       if(id != NULL)
       {
	  return (*env)->CallStaticIntMethod(env, cls, id, ctx, strTitle, strMessage);
       }
    }
    return 1;
}


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

JAVA 代码:

    Button btn03 = (Button)findViewById(R.id.Button03);
    btn03.setOnClickListener(new Button.OnClickListener()
    {
        publicvoid onClick(View v)
        {
            strTest = " [message has changed now]";
            int ret = jniShowMessage(JniDemoActivity.this,"JNI test3","test callback in current instance");
            TextView tv = (TextView)findViewById(R.id.tv01);            

            if(ret == 0)
            {
               tv.setText("test JNI callback successed");                      
            }
            else
            {
               tv.setText("test JNI callback fialed");
            }
        }
    });

C代码:

Jint Java_study_jnidemo_JniDemoActivity_jniShowMessage(JNIEnv* env, jobject thiz,
       jobject ctx, jstring strTitle, jstring strMessage)

{
    jclass cls = (*env)->GetObjectClass(env, thiz);
    if(cls != NULL)
    {
       jstring str;
       jmethodID strTest_id = (*env)->GetMethodID(env, cls, "getTestString","()Ljava/lang/String;");

       if(strTest_id != NULL)

       {
          str = (*env)->CallObjectMethod(env, thiz, strTest_id);
       } 

       jmethodID showMessage_id = (*env)->GetMethodID(env, cls, "showMessage",
	"(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)I"); 

       if(showMessage_id != NULL)
       {
          return (*env)->CallIntMethod(env, thiz, showMessage_id, ctx,
                 strTitle, combine_jstring(env, strMessage, str));
       }
    }
    return 1;
}


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

JAVA 代码:

    Button btn04 = (Button)findViewById(R.id.Button04);
    btn04.setOnClickListener(newButton.OnClickListener()
    {
        publicvoid onClick(View v)
        {
            strTest = " [message has changed now]";
            int ret = jniInstanceShowMessage(JniDemoActivity.this,
		 "JNI test4","test callback in new instance");
            TextView tv = (TextView)findViewById(R.id.tv01);            

            if(ret == 0)
            {
               tv.setText("test JNI new instance successed");      
            }
           else
            {
               tv.setText("test JNI new instance fialed");
            }
        }
    });


C代码:

jint Java_study_jnidemo_JniDemoActivity_jniInstanceShowMessage(JNIEnv* env, jobject thiz,
       jobject ctx, jstring strTitle, jstring strMessage)
{
    jclass cls = (*env)->FindClass(env, "study/jnidemo/JniDemoActivity");
    if(cls != NULL)
    {
       // get instance
       jmethodID constuctor_id = (*env)->GetMethodID(env, cls, "", "()V");
       if(constuctor_id != NULL)
       {
          jobject obj = (*env)->NewObject(env, cls, constuctor_id);
          if(obj != NULL)
          {
             jstring str;
             jmethodID strTest_id = (*env)->GetMethodID(env, cls, "getTestString","()Ljava/lang/String;");
             if(strTest_id != NULL)
             {
                 str = (*env)->CallObjectMethod(env, obj, strTest_id);
             }
             jmethodID showMessage_id = (*env)->GetMethodID(env, cls,"showMessage",
                    "(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)I");
             if(showMessage_id != NULL)
             {
                 return (*env)->CallIntMethod(env, obj, showMessage_id, ctx,strTitle, combine_jstring(env, strMessage, str));
             }
          }
       }
    }
    return 1;
}


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

 

附完整的JAVAC代码

JAVAD代码: JniDemoActivity.java

package study.jnidemo; 

import android.app.Activity;
import android.app.AlertDialog;
import android.os.Bundle;
import android.widget.Button;
import android.view.View;
import android.widget.TextView;
import android.content.Context;
import android.content.DialogInterface; 

publicclass JniDemoActivityextends Activity {    

    public StringstrTest =" [initial message]";           

    /** Called when the activity is first created. */
    @Override
    publicvoid onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);       

        findAndModifyButton();
    }

    public String getTestString()
    {
    	returnstrTest;
    }   

    // 测试JAVA的NDK调用
    publicnative String stringFromJNI();    

    // 测试C/C++中对JAVA函数的静态回调
    publicnativestaticint jniStaticShowMessage(Context ctx, String strTitle, String strMessage);    

    // 测试实例中C/C++中对JAVA类的函数的调用
    publicnativeint jniShowMessage(Context ctx, String strTitle, String strMessage);    

    // 测试创建新实例C/C++对JAVA类的函数的调用
    publicnativeint jniInstanceShowMessage(Context ctx, String strTitle, String strMessage);    

    static {
        System.loadLibrary("demo-jni");
    }    

    staticint staticShowMessage(Context ctx, String strTitle, String strMessage)
    {   
    	AlertDialog.Builder builder = new AlertDialog.Builder(ctx);   
    	builder.setTitle(strTitle);
    	builder.setMessage(strMessage);
    	builder.setPositiveButton("确定",
         new DialogInterface.OnClickListener(){

        	   public void onClick(DialogInterface dialog,int whichButton){	            

        	   }
    	});
    	builder.show();    
    	return 0;

    }   

    publicint showMessage(Context ctx, String strTitle, String strMessage)
    {
    	returnstaticShowMessage(ctx, strTitle, strMessage);
    }       

    privatevoid findAndModifyButton()
    {
    	// 测试JAVA的NDK调用
    	Button btn01 = (Button)findViewById(R.id.Button01);
    	btn01.setOnClickListener(new Button.OnClickListener()
    	{
        	    publicvoid onClick(View v)
             {      
            	TextView tv = (TextView)findViewById(R.id.tv01);
            	tv.setText(stringFromJNI());            

            	showMessage(JniDemoActivity.this,"JNI test1", stringFromJNI());
            }
    	});

    

    	// 测试C/C++中对JAVA函数的静态回调
    	Button btn02 = (Button)findViewById(R.id.Button02);
    	btn02.setOnClickListener(new Button.OnClickListener()
    	{

        	    publicvoid onClick(View v)
             {
            	int ret =jniStaticShowMessage(JniDemoActivity.this,"JNI test2", "test static callback Message");
            	TextView tv = (TextView)findViewById(R.id.tv01);

                  if(ret == 0)
            	{
               	    tv.setText("test JNI static callback successed");                      
            	}
            	else
            	{

               	    tv.setText("test JNI static callback fialed");
            	}
        	    }

    });
 

    // 测试实例中C/C++中对JAVA类的函数的调用
    Button btn03 = (Button)findViewById(R.id.Button03);
    btn03.setOnClickListener(new Button.OnClickListener()
    {
        publicvoid onClick(View v)
        {
            strTest = " [message has changed now]";
            int ret = jniShowMessage(JniDemoActivity.this,"JNI test3", "test callback in current instance");
            TextView tv = (TextView)findViewById(R.id.tv01);            

           if(ret == 0)
            {
               tv.setText("test JNI callback successed");                      
            }
            else
            {
               tv.setText("test JNI callback fialed");
            }
        }
    });

    
    // 测试创建新实例C/C++对JAVA类的函数的调用
    Button btn04 = (Button)findViewById(R.id.Button04);
    btn04.setOnClickListener(new Button.OnClickListener()
    {
        publicvoid onClick(View v)
        {
            strTest = " [message has changed now]";
            int ret = jniInstanceShowMessage(JniDemoActivity.this,"JNI test4", "test callback in new instance");
            TextView tv = (TextView)findViewById(R.id.tv01);            

            if(ret == 0)
            {
               tv.setText("test JNI new instance successed");                         
            }
            else
            {
               tv.setText("test JNI new instance fialed");
            }
        }
    });
    }  

}


 

C代码 demo-jni.cpp

#include
#include

// 加载此动态库时系统自动首先加载
jint JNI_OnLoad(JavaVM* vm, void *reserved)
{
    JNIEnv *env;
    if((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_4) != JNI_OK)
    {
       return -1;
    }
    return JNI_VERSION_1_4;
} 

jstring
Java_study_jnidemo_JniDemoActivity_stringFromJNI( JNIEnv* env,jobject thiz )
{
    return (*env)->NewStringUTF(env,"JniDemo, Hello from JNI!");
}

jstring
combine_jstring(JNIEnv* env, jstring str1, jstring str2)
{
    jboolean b_ret;
    constchar *s1 = (*env)->GetStringUTFChars(env, str1, &b_ret);
    constchar *s2 = (*env)->GetStringUTFChars(env, str2, &b_ret);
    int n1 = strlen(s1);
    int n2 = strlen(s2);
    char *new_str = (char *)malloc(n1+n2+1);
    memset(new_str, 0, n1+n2+1);
    strcat(new_str, s1);
    strcat(new_str, s2);
    jstring ret_str = (*env)->NewStringUTF(env,(constchar *)new_str);
    free(new_str);

    return ret_str;
}

jint
Java_study_jnidemo_JniDemoActivity_jniStaticShowMessage(JNIEnv* env, jobject thiz,
       jobject ctx, jstring strTitle, jstring strMessage)
{
    jclass cls = (*env)->FindClass(env, "study/jnidemo/JniDemoActivity");
//  jclass cls = (*env)->GetObjectClass(env, thiz);
    if(cls != NULL)
    {
       jmethodID id = (*env)->GetStaticMethodID(env, cls,
             "staticShowMessage",
             "(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)I");
       if(id != NULL)
       {
          return (*env)->CallStaticIntMethod(env, cls, id, ctx, strTitle, strMessage);
       }
    }
    return 1;
}

// 在当前已有的JAVA实例中调用
jint
Java_study_jnidemo_JniDemoActivity_jniShowMessage(JNIEnv* env, jobject thiz,
       jobject ctx, jstring strTitle, jstring strMessage)
{
    jclass cls = (*env)->GetObjectClass(env, thiz);
    if(cls != NULL)
    {
       jstring str;
       jmethodID strTest_id = (*env)->GetMethodID(env, cls, "getTestString",
             "()Ljava/lang/String;");
       if(strTest_id != NULL)
       {
          str = (*env)->CallObjectMethod(env, thiz, strTest_id);
       }

        jmethodID showMessage_id = (*env)->GetMethodID(env, cls, "showMessage",
             "(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)I");

       if(showMessage_id != NULL)
       {
          return (*env)->CallIntMethod(env, thiz, showMessage_id, ctx,
                 strTitle, combine_jstring(env, strMessage, str));
       }
    }
    return 1;
}

// 在新建JAVA实例中调用
jint
Java_study_jnidemo_JniDemoActivity_jniInstanceShowMessage(JNIEnv* env, jobject thiz,
       jobject ctx, jstring strTitle, jstring strMessage)
{
    jclass cls = (*env)->FindClass(env, "study/jnidemo/JniDemoActivity");
    if(cls != NULL)
    {
       // get instance
       jmethodID constuctor_id = (*env)->GetMethodID(env, cls, "", "()V");
       if(constuctor_id != NULL)
       {
          jobject obj = (*env)->NewObject(env, cls, constuctor_id);
          if(obj != NULL)
          {
             jstring str;
             jmethodID strTest_id = (*env)->GetMethodID(env, cls,"getTestString",
                    "()Ljava/lang/String;"); 

             if(strTest_id != NULL)
             {
                 str = (*env)->CallObjectMethod(env, obj, strTest_id);
             } 

             jmethodID showMessage_id = (*env)->GetMethodID(env, cls,"showMessage",
                    "(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)I");
             if(showMessage_id != NULL)
             {
                 return (*env)->CallIntMethod(env, obj, showMessage_id, ctx,
                        strTitle, combine_jstring(env, strMessage, str));
             }
          }
       }
    }
    return 1;
}


 

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

你可能感兴趣的:(移动开发)