Android学习开发4--Android在C++中调用java
2011-05-12 15:26
399 查看
原文地址::http://hi.baidu.com/lihn1987/blog/item/42ed9407f465337503088182.html
然后建立两个软连接
$ ln -s /cygdrive/e/hello_jni/jni ./jni
$ ln -s /cygdrive/e/hello_jni/libs ./libs
这样我们在cygwin中编译的文件将直接生成在android工程之下,当然方法也不止这一个,也可以直接修改Application.mk更改其编译位置
下面步入正题
有点乱。。。不过这个主要是实验目的,所以不去理会他的功能性。
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++调用。
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生成的头文件,主要用于辅助,其实没有也行
示例化的函数如下所示
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类型….)返回类型。而相应类型的表示如下所示
比如我们要找个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为构造函数的句柄,而类的实例被该函数返回。
(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
一、优化
由于在上一章里的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中定义的别名 |
int | long | jint |
long | _int64 | jlong |
byte | signed char | jbyte |
boolean | unsigned char | jboolean |
char | unsigned short | jchar |
short | short | jshort |
float | float | jfloat |
double | double | jdouble |
Object | _jobject* | jobject |
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
相关文章推荐
- Android-NDK开发之基础--Android JNI实例代码(一)-- 在JNI中执行Java方法--C/C++调用Java
- Android JNI/NDK开发(2)JNI实现C/C++与Android/JAVA相互调用
- 【学习Android NDK开发】Java通过JNI调用native方法
- android jni开发 把一段java代码转换成c++方式调用
- Android 开发中 JAVA 调用 C++
- Android开发学习之路--Java和Js互相调用
- Android开发学习之路--Java和Js互相调用
- Android开发,Kotlin的了解与学习(八)-----kotlin与java的互相调用
- Android Java调用.so和.a、Unity C#调用jar插件和C++开发的插件
- Android-NDK开发之基础--Android JNI实例代码(一)-- 在JNI中执行Java方法--C/C++调用Java
- android-JNI学习之java和c++相互调用
- Android开发 通过JNI实现JAVA与C/C++程序间的调用和回调
- Android-NDK开发之第四个例子--用C/C++调用Java
- Android-NDK开发之第四个例子--用C/C++调用Java
- android jni开发 把一段java代码转换成c++方式调用
- Android-NDK开发之第四个例子--用C/C++调用Java
- android-JNI学习之java和c++相互调用
- Android开发中在JAVA中调用C/C++ native代码
- Android-NDK开发之基础--Android JNI实例代码(一)-- 在JNI中执行Java方法--C/C++调用Java