- 浏览: 514605 次
- 性别:
- 来自: 惠州
文章分类
- 全部博客 (255)
- ant (1)
- springMVC (2)
- ajax (4)
- oracle (12)
- SSH (13)
- struts1 (2)
- Hibernate (14)
- spring (5)
- jstl (1)
- 连接池 (1)
- acegi (4)
- java (17)
- jquery (11)
- div+css (4)
- drupal (1)
- php (8)
- freemaker调模板生成静态页面 (1)
- xml (1)
- json (2)
- javascript (9)
- 正则表达式 (4)
- Ext (8)
- jdbc (1)
- sql server (2)
- perl (5)
- db4o (1)
- webservice (4)
- flex (13)
- it资讯 (1)
- joomla (0)
- 设计模式 (1)
- struts2 (4)
- s2sh (8)
- linux (3)
- ejb (2)
- android旅途 (24)
- android (36)
- C/C++ (16)
- mysql (1)
最新评论
-
fengyuxing168:
IBelyService bs = IBelyService. ...
为 Android 添加 Java 层服务也就是添加自定义的aidl服务到serviceManager 通过ServiceManager.getService取 -
dengzhangtao:
"由于ActivityManagerService是 ...
binder理解 -
yzyspy:
ActivityManagerService:startHom ...
Android的Launcher成为系统中第一个启动的,也是唯一的 -
Matchstick:
使用SELECT DISTINCT alias FROM Po ...
hibernate 一对多表查询时fetchMode.join 生成left outer join 出来数据重复问题 -
dlheart:
没看懂你什么意思啊,我遇到的问题是一对多,设了fetch = ...
hibernate 一对多表查询时fetchMode.join 生成left outer join 出来数据重复问题
自己在外面偷偷的算了下,又有将近两个月没更新过blog了。趁着今天有兴致,来更新JNI编程的第二篇文章。在第一篇里,大概介绍了 JNI的特点、用途和优劣。并且做一个最简单的JNI的例子,不过说实话那个例子在实际的开发中没有太大的价值,实际开发中所需要的JNI程序要远远比那个复杂。所以这一篇就来介绍下如何通过JNI实现java和C++的相互通信,来满足实际开发的需要。
所谓”通信“,其实说白了无非也就是我们所说的方法调用,在上一篇的例子里介绍了如何在Java中调用本地的DLL,其实在Java代码中,除了对本地方法标注native关键字和加上要加载动态链接库之外,JNI基本上是对上层coder透明的,上层coder调用那些本地方法的时候并不知道这个方法的方法体究竟是在哪里,这个道理就像我们用JDK所提供的API一样。所以在Java中使用JNI还是很简单的,相比之下在C++中调用java,就比前者要复杂的多了。
现在来介绍下JNI里的数据类型。在C++里,编译器会很据所处的平台来为一些基本的数据类型来分配长度,因此也就造成了平台不一致性,而这个问题在Java中则不存在,因为有JVM的缘故,所以Java中的基本数据类型在所有平台下得到的都是相同的长度,比如int的宽度永远都是32位。基于这方面的原因,java和c++的基本数据类型就需要实现一些mapping,保持一致性。下面的表可以概括:
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
上面的表格是我在网上搜的,放上来给大家对比一下。对于每一种映射的数据类型,JNI的设计者其实已经帮我们取好了相应的别名以方便记忆。如果想了解一些更加细致的信息,可以去看一些jni.h这个头文件,各种数据类型的定义以及别名就被定义在这个文件中。
了解了JNI中的数据类型,下面就来看这次的例子。这次我们用Java来实现一个前端的market(以下就用Foreground代替)用CPP来实现一个后端factory(以下用backend代替)。我们首先还是来编写包含本地方法的java类。
Java代码 收藏代码
1. package com.chnic.service;
2.
3. import com.chnic.bean.Order;
4.
5. public class Business {
6. static{
7. System.loadLibrary("FruitFactory");
8. }
9.
10. public Business(){
11.
12. }
13.
14. public native double getPrice(String name);
15. public native Order getOrder(String name, int amount);
16. public native Order getRamdomOrder();
17. public native void analyzeOrder(Order order);
18.
19. public void notification(){
20. System.out.println("Got a notification.");
21. }
22.
23. public static void notificationByStatic(){
24. System.out.println("Got a notification in a static method.");
25. }
26. }
package com.chnic.service;
import com.chnic.bean.Order;
public class Business {
static{
System.loadLibrary("FruitFactory");
}
public Business(){
}
public native double getPrice(String name);
public native Order getOrder(String name, int amount);
public native Order getRamdomOrder();
public native void analyzeOrder(Order order);
public void notification(){
System.out.println("Got a notification.");
}
public static void notificationByStatic(){
System.out.println("Got a notification in a static method.");
}
}
这个类里面包含4个本地方法,一个静态初始化块加载将要生成的dll文件。剩下的方法都是很普通的java方法,等会在backend中回调这些方法。这个类需要一个名为Order的JavaBean。
Java代码 收藏代码
1. package com.chnic.bean;
2.
3. public class Order {
4.
5. private String name = "Fruit";
6. private double price;
7. private int amount = 30;
8.
9. public Order(){
10.
11. }
12.
13. public int getAmount() {
14. return amount;
15. }
16.
17. public void setAmount(int amount) {
18. this.amount = amount;
19. }
20.
21. public String getName() {
22. return name;
23. }
24.
25. public void setName(String name) {
26. this.name = name;
27. }
28.
29. public double getPrice() {
30. return price;
31. }
32.
33. public void setPrice(double price) {
34. this.price = price;
35. }
36. }
package com.chnic.bean;
public class Order {
private String name = "Fruit";
private double price;
private int amount = 30;
public Order(){
}
public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
JavaBean中,我们为两个私有属性赋值,方便后面的例子演示。到此为止除了测试代码之外的Java端的代码就全部高调了,接下来进行生成.h 头文件、建立C++工程的工作,在这里就一笔带过,不熟悉的朋友请回头看第一篇。在工程里我们新建一个名为Foctory的C++ source file 文件,去实现那些native方法。具体的代码如下。
Cpp代码 收藏代码
1. #include <iostream.h>
2. #include <string.h>
3. #include "com_chnic_service_Business.h"
4.
5. jobject getInstance(JNIEnv* env, jclass obj_class);
6.
7. JNIEXPORT jdouble JNICALL Java_com_chnic_service_Business_getPrice(JNIEnv* env,
8. jobject obj,
9. jstring name)
10. {
11. const char* pname = env->GetStringUTFChars(name, NULL);
12. cout << "Before release: " << pname << endl;
13.
14. if (strcmp(pname, "Apple") == 0)
15. {
16. env->ReleaseStringUTFChars(name, pname);
17. cout << "After release: " << pname << endl;
18. return 1.2;
19. }
20. else
21. {
22. env->ReleaseStringUTFChars(name, pname);
23. cout << "After release: " << pname << endl;
24. return 2.1;
25. }
26. }
27.
28.
29. JNIEXPORT jobject JNICALL Java_com_chnic_service_Business_getOrder(JNIEnv* env,
30. jobject obj,
31. jstring name,
32. jint amount)
33. {
34. jclass order_class = env->FindClass("com/chnic/bean/Order");
35. jobject order = getInstance(env, order_class);
36.
37. jmethodID setName_method = env->GetMethodID(order_class, "setName", "(Ljava/lang/String;)V");
38. env->CallVoidMethod(order, setName_method, name);
39.
40. jmethodID setAmount_method = env->GetMethodID(order_class, "setAmount", "(I)V");
41. env->CallVoidMethod(order, setAmount_method, amount);
42.
43. return order;
44. }
45.
46. JNIEXPORT jobject JNICALL Java_com_chnic_service_Business_getRamdomOrder(JNIEnv* env,
47. jobject obj)
48. {
49. jclass business_class = env->GetObjectClass(obj);
50. jobject business_obj = getInstance(env, business_class);
51.
52. jmethodID notification_method = env->GetMethodID(business_class, "notification", "()V");
53. env->CallVoidMethod(obj, notification_method);
54.
55. jclass order_class = env->FindClass("com/chnic/bean/Order");
56. jobject order = getInstance(env, order_class);
57. jfieldID amount_field = env->GetFieldID(order_class, "amount", "I");
58. jint amount = env->GetIntField(order, amount_field);
59. cout << "amount: " << amount << endl;
60. return order;
61. }
62.
63.
64. JNIEXPORT void JNICALL Java_com_chnic_service_Business_analyzeOrder (JNIEnv* env,
65. jclass cls,
66. jobject obj)
67. {
68. jclass order_class = env->GetObjectClass(obj);
69. jmethodID getName_method = env->GetMethodID(order_class, "getName", "()Ljava/lang/String;");
70. jstring name_str = static_cast<jstring>(env->CallObjectMethod(obj, getName_method));
71. const char* pname = env->GetStringUTFChars(name_str, NULL);
72.
73. cout << "Name in Java_com_chnic_service_Business_analyzeOrder: " << pname << endl;
74. jmethodID notification_method_static = env->GetStaticMethodID(cls, "notificationByStatic", "()V");
75. env->CallStaticVoidMethod(cls, notification_method_static);
76.
77. }
78.
79. jobject getInstance(JNIEnv* env, jclass obj_class)
80. {
81. jmethodID construction_id = env->GetMethodID(obj_class, "<init>", "()V");
82. jobject obj = env->NewObject(obj_class, construction_id);
83. return obj;
84. }
#include <iostream.h>
#include <string.h>
#include "com_chnic_service_Business.h"
jobject getInstance(JNIEnv* env, jclass obj_class);
JNIEXPORT jdouble JNICALL Java_com_chnic_service_Business_getPrice(JNIEnv* env,
jobject obj,
jstring name)
{
const char* pname = env->GetStringUTFChars(name, NULL);
cout << "Before release: " << pname << endl;
if (strcmp(pname, "Apple") == 0)
{
env->ReleaseStringUTFChars(name, pname);
cout << "After release: " << pname << endl;
return 1.2;
}
else
{
env->ReleaseStringUTFChars(name, pname);
cout << "After release: " << pname << endl;
return 2.1;
}
}
JNIEXPORT jobject JNICALL Java_com_chnic_service_Business_getOrder(JNIEnv* env,
jobject obj,
jstring name,
jint amount)
{
jclass order_class = env->FindClass("com/chnic/bean/Order");
jobject order = getInstance(env, order_class);
jmethodID setName_method = env->GetMethodID(order_class, "setName", "(Ljava/lang/String;)V");
env->CallVoidMethod(order, setName_method, name);
jmethodID setAmount_method = env->GetMethodID(order_class, "setAmount", "(I)V");
env->CallVoidMethod(order, setAmount_method, amount);
return order;
}
JNIEXPORT jobject JNICALL Java_com_chnic_service_Business_getRamdomOrder(JNIEnv* env,
jobject obj)
{
jclass business_class = env->GetObjectClass(obj);
jobject business_obj = getInstance(env, business_class);
jmethodID notification_method = env->GetMethodID(business_class, "notification", "()V");
env->CallVoidMethod(obj, notification_method);
jclass order_class = env->FindClass("com/chnic/bean/Order");
jobject order = getInstance(env, order_class);
jfieldID amount_field = env->GetFieldID(order_class, "amount", "I");
jint amount = env->GetIntField(order, amount_field);
cout << "amount: " << amount << endl;
return order;
}
JNIEXPORT void JNICALL Java_com_chnic_service_Business_analyzeOrder (JNIEnv* env,
jclass cls,
jobject obj)
{
jclass order_class = env->GetObjectClass(obj);
jmethodID getName_method = env->GetMethodID(order_class, "getName", "()Ljava/lang/String;");
jstring name_str = static_cast<jstring>(env->CallObjectMethod(obj, getName_method));
const char* pname = env->GetStringUTFChars(name_str, NULL);
cout << "Name in Java_com_chnic_service_Business_analyzeOrder: " << pname << endl;
jmethodID notification_method_static = env->GetStaticMethodID(cls, "notificationByStatic", "()V");
env->CallStaticVoidMethod(cls, notification_method_static);
}
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;
}
可以看到,在我Java中的四个本地方法在这里全部被实现,接下来针对这四个方法来解释下,一些JNI相关的API的使用方法。先从第一个方法讲起吧:
1.getPrice(String name)
这个方法是从foreground传递一个类型为string的参数到backend,然后backend判断返回相应的价格。在cpp的代码中,我们用GetStringUTFChars这个方法来把传来的jstring变成一个UTF-8编码的char型字符串。因为jstring的实际类型是 jobject,所以无法直接比较。
GetStringUTFChars方法包含两个参数,第一参数是你要处理的jstring对象,第二个参数是否需要在内存中生成一个副本对象。将 jstring转换成为了一个const char*了之后,我们用string.h中带strcmp函数来比较这两个字符串,如果传来的字符串是“Apple”的话我们返回1.2。反之返回 2.1。在这里还要多说一下ReleaseStringUTFChars这个函数,这个函数从字面上不难理解,就是释放内存用的。有点像cpp里的析构函数,只不过Sun帮我们已经封装好了。由于在JVM中有GC这个东东,所以多数java coder并没有写析构的习惯,不过在JNI里是必须的了,否则容易造成内存泄露。我们在这里在release之前和之后分别打出这个字符串来看一下效果。
粗略的解释完一些API之后,我们编写测试代码。
Java代码 收藏代码
1. Business b = new Business();
2. System.out.println(b.getPrice("Apple"));
Business b = new Business();
System.out.println(b.getPrice("Apple"));
运行这段测试代码,控制台上打出
Before release: Apple
After release: ��
1.2
在release之前打印出来的是我们“需要”的Apple,release之后就成了乱码了。由于传递的是Apple,所以得到1.2。测试成功。
2. getOrder(String name, int amount)
在foreground中可以通过这个方法让backend返回一个你“指定”的Order。所谓“指定”,其实也就是指方法里的两个参数:name和amout,在cpp的代码在中,会根据传递的两个参数来构造一个Order。回到cpp的代码里。
Java代码 收藏代码
1. jclass order_class = env->FindClass("com/chnic/bean/Order");
jclass order_class = env->FindClass("com/chnic/bean/Order");
是不是觉得这句代码似曾相识?没错,这句代码很像我们java里写的Class.forName(className)反射的代码。其实在这里 FindClass的作用和上面的forName是类似的。只不过在forName中要用完整的类名,但是在这里必须用"/"来代替“.”。这个方法会返回一个jclass的对象,其实也就是我们在Java中说的类对象。
Java代码 收藏代码
1. jmethodID construction_id = env->GetMethodID(obj_class, "<init>", "()V");
2. jobject obj = env->NewObject(obj_class, construction_id);
jmethodID construction_id = env->GetMethodID(obj_class, "<init>", "()V");
jobject obj = env->NewObject(obj_class, construction_id);
拿到"类对象"了之后,按照Java RTTI的逻辑我们接下来就要唤醒那个类对象的构造函数了。在JNI中,包括构造函数在内的所有方法都被看成Method。每个method都有一个特定的ID,我们通过GetMethodID这个方法就可以拿到我们想要的某一个java 方法的ID。GetMethodID需要传三个参数,第一个是很显然jclass,第二个参数是java方法名,也就是你想取的method ID的那个方法的方法名(有些绕口 ),第三个参数是方法签名。
在这里有必要单独来讲一讲这个方法签名,为什么要用这个东东呢?我们知道,在Java里方法是可以被重载的,比如我一个类里有public void a(int arg)和public void a(String arg)这两个方法,在这里用方法名来区分方法显然就是行不通的了。方法签名包括两部分:参数类型和返回值类型;具体的格式:(参数1类型签名 参数2类型签名)返回值类型签名。下面是java类型和年名类型的对照的一个表
Java类型 对应的签名
boolean Z
byte B
char C
shrot S
int I
long L
float F
double D
void V
Object L用/分割包的完整类名; Ljava/lang/String;
Array [签名 [I [Ljava/lang/String;
其实除了自己对照手写之外,JDK也提供了一个很好用的生成签名的工具javap,cmd进入控制台到你要生成签名的那个类的目录下。在这里用 Order类打比方,敲入: javap -s -private Order。 所有方法签名都会被输出,关于javap的一些参数可以在控制台下面输入 javap -help查看。(做coder的 毕竟还是要认几个单词的)
啰嗦了一大堆,还是回到我们刚刚的getMethodID这个方法上。因为是调用构造函数,JNI规定调用构造函数的时候传递的方法名应该为<init> ,通过javap查看 我们要的那个无参的构造函数的方法签是()V。得到方法签名,最后我们调用NewObject方法来生成一个新的对象。
拿到了对象,之后我们开始为对象jobject填充数值,还是首先拿到setXXX方法的Method ID,之后调用Call<Type>Method来调用java方法。这里的<Type>所指的是方法的返回类型,我们刚刚调用的是set方法的返回值是void,因此这里的方法也就是CallVoidMethod,这个方法的参数除了前两个要传入jobject和 jmethodID之外还要传入要调用的那个方法的参数,而且要顺序必须一致,这点和Java的反射一模一样,在这里就不多解释。(看到这一步是不是对 java 反射又有了自己新的理解?)
终于介绍完了第二个方法,下来就是测试代码测试。
Java代码 收藏代码
1. Order o = b.getOrder("Watermelom", 100);
2. System.out.println("java: " + o.getName());
3. System.out.println("java: " + o.getAmount());
Order o = b.getOrder("Watermelom", 100);
System.out.println("java: " + o.getName());
System.out.println("java: " + o.getAmount());
控制台打出
java: Watermelom
java: 100
就此,我们完成了第二个方法的测试。
鉴于篇幅的关系,第三个 第四个方法的解释放到下一篇里解释。突然发现这篇写的貌似有点长了,再写下去的话臭鸡蛋和番茄就飘过来了 , 具体的代码也会在下一篇里上传。
所谓”通信“,其实说白了无非也就是我们所说的方法调用,在上一篇的例子里介绍了如何在Java中调用本地的DLL,其实在Java代码中,除了对本地方法标注native关键字和加上要加载动态链接库之外,JNI基本上是对上层coder透明的,上层coder调用那些本地方法的时候并不知道这个方法的方法体究竟是在哪里,这个道理就像我们用JDK所提供的API一样。所以在Java中使用JNI还是很简单的,相比之下在C++中调用java,就比前者要复杂的多了。
现在来介绍下JNI里的数据类型。在C++里,编译器会很据所处的平台来为一些基本的数据类型来分配长度,因此也就造成了平台不一致性,而这个问题在Java中则不存在,因为有JVM的缘故,所以Java中的基本数据类型在所有平台下得到的都是相同的长度,比如int的宽度永远都是32位。基于这方面的原因,java和c++的基本数据类型就需要实现一些mapping,保持一致性。下面的表可以概括:
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
上面的表格是我在网上搜的,放上来给大家对比一下。对于每一种映射的数据类型,JNI的设计者其实已经帮我们取好了相应的别名以方便记忆。如果想了解一些更加细致的信息,可以去看一些jni.h这个头文件,各种数据类型的定义以及别名就被定义在这个文件中。
了解了JNI中的数据类型,下面就来看这次的例子。这次我们用Java来实现一个前端的market(以下就用Foreground代替)用CPP来实现一个后端factory(以下用backend代替)。我们首先还是来编写包含本地方法的java类。
Java代码 收藏代码
1. package com.chnic.service;
2.
3. import com.chnic.bean.Order;
4.
5. public class Business {
6. static{
7. System.loadLibrary("FruitFactory");
8. }
9.
10. public Business(){
11.
12. }
13.
14. public native double getPrice(String name);
15. public native Order getOrder(String name, int amount);
16. public native Order getRamdomOrder();
17. public native void analyzeOrder(Order order);
18.
19. public void notification(){
20. System.out.println("Got a notification.");
21. }
22.
23. public static void notificationByStatic(){
24. System.out.println("Got a notification in a static method.");
25. }
26. }
package com.chnic.service;
import com.chnic.bean.Order;
public class Business {
static{
System.loadLibrary("FruitFactory");
}
public Business(){
}
public native double getPrice(String name);
public native Order getOrder(String name, int amount);
public native Order getRamdomOrder();
public native void analyzeOrder(Order order);
public void notification(){
System.out.println("Got a notification.");
}
public static void notificationByStatic(){
System.out.println("Got a notification in a static method.");
}
}
这个类里面包含4个本地方法,一个静态初始化块加载将要生成的dll文件。剩下的方法都是很普通的java方法,等会在backend中回调这些方法。这个类需要一个名为Order的JavaBean。
Java代码 收藏代码
1. package com.chnic.bean;
2.
3. public class Order {
4.
5. private String name = "Fruit";
6. private double price;
7. private int amount = 30;
8.
9. public Order(){
10.
11. }
12.
13. public int getAmount() {
14. return amount;
15. }
16.
17. public void setAmount(int amount) {
18. this.amount = amount;
19. }
20.
21. public String getName() {
22. return name;
23. }
24.
25. public void setName(String name) {
26. this.name = name;
27. }
28.
29. public double getPrice() {
30. return price;
31. }
32.
33. public void setPrice(double price) {
34. this.price = price;
35. }
36. }
package com.chnic.bean;
public class Order {
private String name = "Fruit";
private double price;
private int amount = 30;
public Order(){
}
public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
JavaBean中,我们为两个私有属性赋值,方便后面的例子演示。到此为止除了测试代码之外的Java端的代码就全部高调了,接下来进行生成.h 头文件、建立C++工程的工作,在这里就一笔带过,不熟悉的朋友请回头看第一篇。在工程里我们新建一个名为Foctory的C++ source file 文件,去实现那些native方法。具体的代码如下。
Cpp代码 收藏代码
1. #include <iostream.h>
2. #include <string.h>
3. #include "com_chnic_service_Business.h"
4.
5. jobject getInstance(JNIEnv* env, jclass obj_class);
6.
7. JNIEXPORT jdouble JNICALL Java_com_chnic_service_Business_getPrice(JNIEnv* env,
8. jobject obj,
9. jstring name)
10. {
11. const char* pname = env->GetStringUTFChars(name, NULL);
12. cout << "Before release: " << pname << endl;
13.
14. if (strcmp(pname, "Apple") == 0)
15. {
16. env->ReleaseStringUTFChars(name, pname);
17. cout << "After release: " << pname << endl;
18. return 1.2;
19. }
20. else
21. {
22. env->ReleaseStringUTFChars(name, pname);
23. cout << "After release: " << pname << endl;
24. return 2.1;
25. }
26. }
27.
28.
29. JNIEXPORT jobject JNICALL Java_com_chnic_service_Business_getOrder(JNIEnv* env,
30. jobject obj,
31. jstring name,
32. jint amount)
33. {
34. jclass order_class = env->FindClass("com/chnic/bean/Order");
35. jobject order = getInstance(env, order_class);
36.
37. jmethodID setName_method = env->GetMethodID(order_class, "setName", "(Ljava/lang/String;)V");
38. env->CallVoidMethod(order, setName_method, name);
39.
40. jmethodID setAmount_method = env->GetMethodID(order_class, "setAmount", "(I)V");
41. env->CallVoidMethod(order, setAmount_method, amount);
42.
43. return order;
44. }
45.
46. JNIEXPORT jobject JNICALL Java_com_chnic_service_Business_getRamdomOrder(JNIEnv* env,
47. jobject obj)
48. {
49. jclass business_class = env->GetObjectClass(obj);
50. jobject business_obj = getInstance(env, business_class);
51.
52. jmethodID notification_method = env->GetMethodID(business_class, "notification", "()V");
53. env->CallVoidMethod(obj, notification_method);
54.
55. jclass order_class = env->FindClass("com/chnic/bean/Order");
56. jobject order = getInstance(env, order_class);
57. jfieldID amount_field = env->GetFieldID(order_class, "amount", "I");
58. jint amount = env->GetIntField(order, amount_field);
59. cout << "amount: " << amount << endl;
60. return order;
61. }
62.
63.
64. JNIEXPORT void JNICALL Java_com_chnic_service_Business_analyzeOrder (JNIEnv* env,
65. jclass cls,
66. jobject obj)
67. {
68. jclass order_class = env->GetObjectClass(obj);
69. jmethodID getName_method = env->GetMethodID(order_class, "getName", "()Ljava/lang/String;");
70. jstring name_str = static_cast<jstring>(env->CallObjectMethod(obj, getName_method));
71. const char* pname = env->GetStringUTFChars(name_str, NULL);
72.
73. cout << "Name in Java_com_chnic_service_Business_analyzeOrder: " << pname << endl;
74. jmethodID notification_method_static = env->GetStaticMethodID(cls, "notificationByStatic", "()V");
75. env->CallStaticVoidMethod(cls, notification_method_static);
76.
77. }
78.
79. jobject getInstance(JNIEnv* env, jclass obj_class)
80. {
81. jmethodID construction_id = env->GetMethodID(obj_class, "<init>", "()V");
82. jobject obj = env->NewObject(obj_class, construction_id);
83. return obj;
84. }
#include <iostream.h>
#include <string.h>
#include "com_chnic_service_Business.h"
jobject getInstance(JNIEnv* env, jclass obj_class);
JNIEXPORT jdouble JNICALL Java_com_chnic_service_Business_getPrice(JNIEnv* env,
jobject obj,
jstring name)
{
const char* pname = env->GetStringUTFChars(name, NULL);
cout << "Before release: " << pname << endl;
if (strcmp(pname, "Apple") == 0)
{
env->ReleaseStringUTFChars(name, pname);
cout << "After release: " << pname << endl;
return 1.2;
}
else
{
env->ReleaseStringUTFChars(name, pname);
cout << "After release: " << pname << endl;
return 2.1;
}
}
JNIEXPORT jobject JNICALL Java_com_chnic_service_Business_getOrder(JNIEnv* env,
jobject obj,
jstring name,
jint amount)
{
jclass order_class = env->FindClass("com/chnic/bean/Order");
jobject order = getInstance(env, order_class);
jmethodID setName_method = env->GetMethodID(order_class, "setName", "(Ljava/lang/String;)V");
env->CallVoidMethod(order, setName_method, name);
jmethodID setAmount_method = env->GetMethodID(order_class, "setAmount", "(I)V");
env->CallVoidMethod(order, setAmount_method, amount);
return order;
}
JNIEXPORT jobject JNICALL Java_com_chnic_service_Business_getRamdomOrder(JNIEnv* env,
jobject obj)
{
jclass business_class = env->GetObjectClass(obj);
jobject business_obj = getInstance(env, business_class);
jmethodID notification_method = env->GetMethodID(business_class, "notification", "()V");
env->CallVoidMethod(obj, notification_method);
jclass order_class = env->FindClass("com/chnic/bean/Order");
jobject order = getInstance(env, order_class);
jfieldID amount_field = env->GetFieldID(order_class, "amount", "I");
jint amount = env->GetIntField(order, amount_field);
cout << "amount: " << amount << endl;
return order;
}
JNIEXPORT void JNICALL Java_com_chnic_service_Business_analyzeOrder (JNIEnv* env,
jclass cls,
jobject obj)
{
jclass order_class = env->GetObjectClass(obj);
jmethodID getName_method = env->GetMethodID(order_class, "getName", "()Ljava/lang/String;");
jstring name_str = static_cast<jstring>(env->CallObjectMethod(obj, getName_method));
const char* pname = env->GetStringUTFChars(name_str, NULL);
cout << "Name in Java_com_chnic_service_Business_analyzeOrder: " << pname << endl;
jmethodID notification_method_static = env->GetStaticMethodID(cls, "notificationByStatic", "()V");
env->CallStaticVoidMethod(cls, notification_method_static);
}
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;
}
可以看到,在我Java中的四个本地方法在这里全部被实现,接下来针对这四个方法来解释下,一些JNI相关的API的使用方法。先从第一个方法讲起吧:
1.getPrice(String name)
这个方法是从foreground传递一个类型为string的参数到backend,然后backend判断返回相应的价格。在cpp的代码中,我们用GetStringUTFChars这个方法来把传来的jstring变成一个UTF-8编码的char型字符串。因为jstring的实际类型是 jobject,所以无法直接比较。
GetStringUTFChars方法包含两个参数,第一参数是你要处理的jstring对象,第二个参数是否需要在内存中生成一个副本对象。将 jstring转换成为了一个const char*了之后,我们用string.h中带strcmp函数来比较这两个字符串,如果传来的字符串是“Apple”的话我们返回1.2。反之返回 2.1。在这里还要多说一下ReleaseStringUTFChars这个函数,这个函数从字面上不难理解,就是释放内存用的。有点像cpp里的析构函数,只不过Sun帮我们已经封装好了。由于在JVM中有GC这个东东,所以多数java coder并没有写析构的习惯,不过在JNI里是必须的了,否则容易造成内存泄露。我们在这里在release之前和之后分别打出这个字符串来看一下效果。
粗略的解释完一些API之后,我们编写测试代码。
Java代码 收藏代码
1. Business b = new Business();
2. System.out.println(b.getPrice("Apple"));
Business b = new Business();
System.out.println(b.getPrice("Apple"));
运行这段测试代码,控制台上打出
Before release: Apple
After release: ��
1.2
在release之前打印出来的是我们“需要”的Apple,release之后就成了乱码了。由于传递的是Apple,所以得到1.2。测试成功。
2. getOrder(String name, int amount)
在foreground中可以通过这个方法让backend返回一个你“指定”的Order。所谓“指定”,其实也就是指方法里的两个参数:name和amout,在cpp的代码在中,会根据传递的两个参数来构造一个Order。回到cpp的代码里。
Java代码 收藏代码
1. jclass order_class = env->FindClass("com/chnic/bean/Order");
jclass order_class = env->FindClass("com/chnic/bean/Order");
是不是觉得这句代码似曾相识?没错,这句代码很像我们java里写的Class.forName(className)反射的代码。其实在这里 FindClass的作用和上面的forName是类似的。只不过在forName中要用完整的类名,但是在这里必须用"/"来代替“.”。这个方法会返回一个jclass的对象,其实也就是我们在Java中说的类对象。
Java代码 收藏代码
1. jmethodID construction_id = env->GetMethodID(obj_class, "<init>", "()V");
2. jobject obj = env->NewObject(obj_class, construction_id);
jmethodID construction_id = env->GetMethodID(obj_class, "<init>", "()V");
jobject obj = env->NewObject(obj_class, construction_id);
拿到"类对象"了之后,按照Java RTTI的逻辑我们接下来就要唤醒那个类对象的构造函数了。在JNI中,包括构造函数在内的所有方法都被看成Method。每个method都有一个特定的ID,我们通过GetMethodID这个方法就可以拿到我们想要的某一个java 方法的ID。GetMethodID需要传三个参数,第一个是很显然jclass,第二个参数是java方法名,也就是你想取的method ID的那个方法的方法名(有些绕口 ),第三个参数是方法签名。
在这里有必要单独来讲一讲这个方法签名,为什么要用这个东东呢?我们知道,在Java里方法是可以被重载的,比如我一个类里有public void a(int arg)和public void a(String arg)这两个方法,在这里用方法名来区分方法显然就是行不通的了。方法签名包括两部分:参数类型和返回值类型;具体的格式:(参数1类型签名 参数2类型签名)返回值类型签名。下面是java类型和年名类型的对照的一个表
Java类型 对应的签名
boolean Z
byte B
char C
shrot S
int I
long L
float F
double D
void V
Object L用/分割包的完整类名; Ljava/lang/String;
Array [签名 [I [Ljava/lang/String;
其实除了自己对照手写之外,JDK也提供了一个很好用的生成签名的工具javap,cmd进入控制台到你要生成签名的那个类的目录下。在这里用 Order类打比方,敲入: javap -s -private Order。 所有方法签名都会被输出,关于javap的一些参数可以在控制台下面输入 javap -help查看。(做coder的 毕竟还是要认几个单词的)
啰嗦了一大堆,还是回到我们刚刚的getMethodID这个方法上。因为是调用构造函数,JNI规定调用构造函数的时候传递的方法名应该为<init> ,通过javap查看 我们要的那个无参的构造函数的方法签是()V。得到方法签名,最后我们调用NewObject方法来生成一个新的对象。
拿到了对象,之后我们开始为对象jobject填充数值,还是首先拿到setXXX方法的Method ID,之后调用Call<Type>Method来调用java方法。这里的<Type>所指的是方法的返回类型,我们刚刚调用的是set方法的返回值是void,因此这里的方法也就是CallVoidMethod,这个方法的参数除了前两个要传入jobject和 jmethodID之外还要传入要调用的那个方法的参数,而且要顺序必须一致,这点和Java的反射一模一样,在这里就不多解释。(看到这一步是不是对 java 反射又有了自己新的理解?)
终于介绍完了第二个方法,下来就是测试代码测试。
Java代码 收藏代码
1. Order o = b.getOrder("Watermelom", 100);
2. System.out.println("java: " + o.getName());
3. System.out.println("java: " + o.getAmount());
Order o = b.getOrder("Watermelom", 100);
System.out.println("java: " + o.getName());
System.out.println("java: " + o.getAmount());
控制台打出
java: Watermelom
java: 100
就此,我们完成了第二个方法的测试。
鉴于篇幅的关系,第三个 第四个方法的解释放到下一篇里解释。突然发现这篇写的貌似有点长了,再写下去的话臭鸡蛋和番茄就飘过来了 , 具体的代码也会在下一篇里上传。
发表评论
-
Linux内存点滴 用户进程内存空间 stack heap text data
2013-01-19 16:54 2957Linux内存点滴 用户进程内存空间 经常使用top ... -
Java JNI由浅入深(包括:Java和C++互传ArrayList泛型对象参数)
2011-10-28 15:09 4191Java JNI由浅入深(包括:Java和C++互传Array ... -
Android jni 常用方法备忘
2011-10-28 10:44 1201Android jni 常用方法备忘 ... -
c 线程同步
2011-10-28 09:23 894#include <pthread.h> #inc ... -
c++ 双线程协议解析 链表 sleep pthread_join g++编译
2011-10-25 11:37 1713主类 testlinklist.cpp #include &l ... -
undefined reference to 'pthread_create'问题解决
2011-10-25 10:19 1292undefined reference to 'pthread ... -
strlen与sizeof的区别
2011-10-12 14:20 844strlen与sizeof的区别 最后一个int a[3 ... -
C/C++语言数组作为参数传递
2011-09-28 17:08 1311#include <stdio.h> #inclu ... -
c/c++字符函数操作
2011-09-20 17:01 852// 取得Xml元素的值 // itemName:元 ... -
C++——指针,堆栈,引用,函数
2011-09-16 14:07 1370C++——指针,堆栈,引用,函数 ... -
今天看了两个小时<<21天学通C++>>把C++中的指针、引用、传值问题弄清楚了
2011-09-16 11:13 1072今天看了两个小时 <<21 天学通 C+ ... -
STL中的"集合类"的定义使用
2011-09-16 09:23 1389STL 中的" 集合类" 的定义 ... -
c++链表操作
2011-09-15 19:22 920最后我学习一下如何在已有的链表上插入节点 我们要考虑四 ... -
c++面向对象的编程入门篇--类构造函数与析构函数
2011-09-15 16:14 979请注意,这一节内容是c++的重点,要特别注意! 我们先说一 ... -
C++实例集合类
2011-09-13 11:04 1288// 此程序通过数组来定义集合类,虽不如链表高级, ... -
C++操作符含义
2011-01-20 10:44 2124C++操作符含义 "->"是 ...
相关推荐
这篇博客“JNI编程(二) —— 让C++和Java相互调用(2)”显然深入探讨了如何利用JNI实现Java与C++之间的互调用。在Java应用程序中,有时为了性能优化或者利用已有的C/C++库,我们需要借助JNI来实现这种跨语言的通信。 ...
1. **JNI(Java Native Interface)**:是Android系统中调用C++代码的基础。JNI是一种接口,允许Java代码与其他语言(如C++)进行交互。开发者需要创建一个Java类,该类包含声明为`native`的方法,并通过`System....
在提供的压缩包文件中,1_120818224513_1.png和1_120818224513_2.png可能是示例代码或步骤截图,而"Android 调用C++代码和C++代码调用源码"这个文件可能包含了具体的代码示例。读者可以通过查看这些资源进一步理解和...
JNI编程中错误处理非常重要,因为Java层可能无法捕获C++的异常。使用`__android_log_print`进行日志输出有助于调试。 总结,本资料包通过示例详细展示了Android应用如何通过JNI调用C++代码以及C++代码如何反向调用...
### JNI编程指南书知识点概述 #### 一、JNI的基本概念及应用场景 JNI(Java Native Interface)作为Java平台的重要组成部分,其核心价值在于能够使Java程序有效地利用现有的C/C++代码,进而实现性能优化或功能扩展...
标题与描述均指向了一个具体的技术主题——通过JNI(Java Native Interface)在C语言环境中启动Java虚拟机并调用Java类。这一技术应用广泛,尤其是在需要跨语言编程或利用Java的特性(如垃圾回收、多线程)而底层...
总的来说,Java调用C语言编写的本地库需要理解JNI和JNA的工作原理,以及它们如何处理数据类型转换、参数传递和本地库的加载。通过这些技术,开发者可以充分利用C/C++的性能优势,同时享受Java的平台独立性和高级抽象...
在这个“jni例子——使用int数组”的示例中,我们将深入探讨如何在Java和C/C++之间传递和操作int数组。 1. **JNI基础知识**: - JNI接口提供了Java与本地代码(如C/C++)通信的桥梁,使得开发者可以在Java应用中...
1. **JNI (Java Native Interface)**:这是Java提供的一种标准接口,允许Java代码调用本地方法(如C/C++函数)。通过JNI,开发者可以在Java程序中嵌入C++代码以利用其高性能特性。 2. **JNA (Java Native Access)*...
本文将深入探讨“Java与C#相互调用的全新方式”,结合给定的标签“源码”和“工具”,我们将重点讨论如何通过中间件或接口实现这种交互,并提供一种可能的解决方案——AMFCall。 Java和C#的互操作性通常通过以下几...
Java中的JNI(Java Native Interface)是一种技术,允许Java代码与其他编程语言编写的代码进行交互,尤其是C和C++。JNI在许多场景下是必要的,比如访问操作系统特定的功能、提升性能或者利用已有的C/C++库。这篇博客...
Java Native Interface(JNI)是一种Java平台的标准接口,它允许Java代码和其他编程语言(如C/C++)编写的代码进行交互。在本示例中,Java程序通过JNI调用了C/C++实现的DLL(动态链接库)来执行特定的数学运算。以下...
Java编程语言虽然强大,但在某些特定情况下,如与操作系统底层交互或者使用已有的C/C++库时,我们可能需要借助JNI(Java Native Interface)来调用本地代码,比如DLL动态链接库。本教程将深入讲解如何使用Java通过...
- **JNI**: 编写 JNI 接口,让 Java 可以调用这个 C++ 函数。 #### 2. Java 代码示例 ```java public class MatrixInverse { static { System.loadLibrary("MatrixInverse"); } public native void ...
总之,Java调用C++主要依赖于JNI技术,通过JNI,开发者可以在Java应用中充分利用C++的高效性能和丰富的库资源。这涉及到Java端的接口声明、C++端的实现以及动态库的加载。通过分析提供的示例工程,你可以更深入地...
本资料“安卓Android源码——java调用C例子.zip”聚焦于如何在Android应用中通过Java调用C语言编写的原生代码,这一过程通常称为JNI(Java Native Interface)。 JNI是Java平台标准的一部分,它允许Java代码和其他...
同时,确保项目包含了JDK安装目录下的`jni.h`头文件,这是JNI编程的关键。 3. **实现本地方法** 在`hello.cpp`中实现`NativeHello`类中的`HelloWorld`方法。使用JNI函数,如`JNIEnv*`和`jstring`,来处理Java和...