您的位置:首页 > 移动开发 > Android开发

Android学习开发4--Android在C++中调用java

2011-05-12 15:26 399 查看
原文地址::http://hi.baidu.com/lihn1987/blog/item/42ed9407f465337503088182.html







一、优化

由于在上一章里的NDK使用的在ndk的路径下生成lib,然后拷贝到我们的android工程目录下,这样由于在测试ndk的时候过于麻烦,所以在这里将不再这么做,而是直接将NDK中 app/hm/jni中的jni目录和libs拷贝到我们的android目录下,然后删除ndk中的这两个目录(假如没有libs目录就到android目录下新建一个)

然后建立两个软连接

$ ln -s /cygdrive/e/hello_jni/jni ./jni

$ ln -s /cygdrive/e/hello_jni/libs ./libs

这样我们在cygwin中编译的文件将直接生成在android工程之下,当然方法也不止这一个,也可以直接修改Application.mk更改其编译位置

下面步入正题

二、目标

我们上次java调用C的方法,不过为了保证通用性,这次使用的是java调用c++代码,然后调用的c++代码在调用我们的java代码。

有点乱。。。不过这个主要是实验目的,所以不去理会他的功能性。

三、开始编码

首先是前面章节都说过的做一个java使用jni调用c++的代码。

四、java部分代码

我们用eclipse建立一个和上次NDK相同的工程。代码也是相同我们的源文件一样如下所示

package com.hm.hello;

import android.app.Activity;

import android.os.Bundle;

import android.widget.TextView;



public class CActivityMain extends Activity {


/** Called when the activity is first created. */


static {


System.loadLibrary("my_jni");


}


@Override


public void onCreate(Bundle savedInstanceState)


{


super.onCreate(savedInstanceState);


TextView tv = new TextView(this);


tv.setText( stringFromJNI() );


setContentView(tv);


}


public native String stringFromJNI();

}

这里依然是声明了String stringFromJNI()这个本地代码,然后我们利用javah生成相应头文件(上一章已经讲过如何使用了)。

生成的.h如下所示

/* DO NOT EDIT THIS FILE - it is machine generated */

#include <jni.h>

/* Header for class com_hm_hello_CActivityMain */



#ifndef _Included_com_hm_hello_CActivityMain

#define _Included_com_hm_hello_CActivityMain

#ifdef __cplusplus

extern "C" {

#endif

/*

* Class:
com_hm_hello_CActivityMain

* Method:
stringFromJNI

* Signature: ()Ljava/lang/String;

*/

JNIEXPORT jstring JNICALL Java_com_hm_hello_CActivityMain_stringFromJNI

(JNIEnv *, jobject);



#ifdef __cplusplus

}

#endif

#endif



因为我们这次主要的目的是要在c++中调用java所以我们需要在java中定义一个供C++调用的库。我是新建了一个类

// CforCall.java

package com.hm.hello;



public class CForCall

{

public
CForCall(){};

//public ~CForCall(){};

public String GetJavaString()

{

String str;

str = "123456";

return str;

}

}

该类和一般的java类没有啥区别,其中只定义一个GetJavaString供外部C++调用。


五、C++代码编写

完了后面就看我们的C++代码如何写了

5.1 C++总代码

#include "my_jni.h"



jobject getInstance(JNIEnv* env, jclass obj_class)

{


jmethodID construction_id = env->GetMethodID(obj_class, "<init>", "()V");


jobject obj = env->NewObject(obj_class, construction_id);


return obj;

}

JNIEXPORT jstring JNICALL Java_com_hm_hello_CActivityMain_stringFromJNI

(JNIEnv* env, jobject)

{


jstring str;


jclass java_class = env->FindClass("com/hm/hello/CForCall");


if (java_class == 0)


{


return env->NewStringUTF("not find class!");


}


jobject java_obj = getInstance(env, java_class);


if (java_obj == 0)


{


return env->NewStringUTF("not find java OBJ!");


}


jmethodID java_method = env->GetMethodID(java_class, "GetJavaString", "()Ljava/lang/String;");


if(java_method == 0)


{


return env->NewStringUTF("not find java method!");


}


str = (jstring)env->CallObjectMethod(java_obj, java_method);


return str;

}

以上就是C++的代码,由于我们使用的是C++而不是以前的C所以这里代码,还有makefile都需要做一定的修改,前面已尽介绍过那些什么.mk相应改下就可以了。

下面我就来解释下这段代码



#include "my_jni.h"

这是刚才javah生成的头文件,主要用于辅助,其实没有也行



5.2 java类的实例化

都知道java需要 obj var = new obj();这样一个过程,我们在C++中调用java类的成员函数,当然也要先示例话一个类。

示例化的函数如下所示

jobject getInstance(JNIEnv* env, jclass obj_class)

{


jmethodID construction_id = env->GetMethodID(obj_class, "<init>", "()V");


jobject obj = env->NewObject(obj_class, construction_id);


return obj;

}

这个函数中的env表示环境参数,jclass表示一个java类的句柄。

jmethodID construction_id = env->GetMethodID(obj_class, "<init>", "()V");

GetMethodID的参数分别为(类句柄,方法名称,参数名称)

这个是为了获取java类中某个方法的句柄,有一点需要特别注意的,在获取构造方法的句柄和别的方法的句柄是不一样的。

获取一般方法的句柄所填写“方法名称”参数直接就是这个方法的名称,而构造函数的话就必须填写"<init>"。除了这点区别外,就没有区别了。

而我们的“参数名称”似乎写的就有些奇怪了。但是细说下他的规律也就不怪了。

参数名称的书写规则为 (参数1类型,参数2类型….)返回类型。而相应类型的表示如下所示

Java类型本地类型 JNI中定义的别名
intlongjint
long_int64jlong
bytesigned charjbyte
booleanunsigned charjboolean
charunsigned shortjchar
shortshortjshort
floatfloatjfloat
doubledoublejdouble
Object_jobject*jobject
比如我们要找个int func(double)类型的函数,就该这么写:

jmethodID construction_id = env->GetMethodID(obj_class, " func ", "(D)I");



找到了构造函数的方法之后我们直接调用构造函数,然后把生成的类返回即可

jobject obj = env->NewObject(obj_class, construction_id);

其中obj_class为类的句柄construction_id为构造函数的句柄,而类的实例被该函数返回。

5.3 调用java类的其他函数

JNIEXPORT jstring JNICALL Java_com_hm_hello_CActivityMain_stringFromJNI

(JNIEnv* env, jobject)

{


jstring str;


jclass java_class = env->FindClass("com/hm/hello/CForCall");


if (java_class == 0)


{


return env->NewStringUTF("not find class!");


}


jobject java_obj = getInstance(env, java_class);


if (java_obj == 0)


{


return env->NewStringUTF("not find java OBJ!");


}


jmethodID java_method = env->GetMethodID(java_class, "GetJavaString", "()Ljava/lang/String;");


if(java_method == 0)


{


return env->NewStringUTF("not find java method!");


}


str = (jstring)env->CallObjectMethod(java_obj, java_method);


return str;

}



看完了构造函数的介绍,这段代码其实很容易就看懂了,我们发现调用java中的函数的步骤无非几步:

1找到类句柄

2找到类的方法的句柄

3实例化类

4调用实例化类的方法。

而以上代码中只有一段没说过,就是

jclass java_class = env->FindClass("com/hm/hello/CForCall");

看名字就知道,是在找相应的类的句柄,其中com.hm.hello准备调用类的完整包名(把.转换为/),而CforCall就是我们要调用的类的名称。

至此,编译,然后在Android调试,可爱的机器人又出现了,哈哈,屏幕上显示出现了12345

成功!!!!

参考文章http://blog.chinaunix.net/u3/90973/showart.php?id=2061985



如有疑问可以联系lihn1987@gmail.com
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: