[Go Back]
2.3 优化范例程序
2.3.1 问题
在上述的范例里,其Calculate类别的goto_state_cal()函数:
private void goto_state_cal(){
state_var_A = 4;
int cs = NativeJniAdder.calculate(digit_1, digit_2);
int sum = cs % 10; int carry = cs / 10;
String carry_sum_str = String.valueOf(carry) + String.valueOf(sum);
ax.show("[" + carry_sum_str +"]");
}
其调用了NativeJniAdder.calculator()函数。首先要留意的是:这calculate()是类别层级(Class-level)的函数,而不是对象层级(Object-level)的函数。再仔细看看这个函数的内容:
JNIEXPORT jint JNICALL
Java_com_misoo_gx05_NativeJniAdder_calculate(JNIEnv *env, jclass c,
jint digit_1, jint digit_2){
HalfAdder* hadder = (HalfAdder*)HalfAdderNew();
hadder->set_digits(hadder, digit_1, digit_2);
hadder->run(hadder);
int k = hadder->get_carry(hadder)*10 + hadder->get_sum(hadder);
return k;
}
其中的hadder是一个函数内的区域(Local)指针变量,当此函数执行完毕时,此指针变量就消失了。亦即,放弃了其所诞生的HalfAdder对象。所以每次调用NativeJniAdder类别的函数时,都会重新诞生一个HalfAdder对象。由于calculator()是类别层级函数,不是对象层级的函数。所以无法将hadder指针变量值储存于对象里。那么,有个问题:
- Java程序如何针对单一HalfAdder对象而进行多次调用呢?
这是一个很常见的问题,在本节里,将说明其解决之方法。[歡迎光臨 高煥堂 網頁:http://www.cnblogs.com/myEIT/ ]
2.3.2 解决之道
其解决方法是:将HalfAdder对象的指针传回给Java程序。现在,以实例说明之。
Step-1. 建立一个程序开发项目
在workspace里,建立一个项目,如下:
此范例的程序架构如下图:

图2-3 execute()是一般的对象层级函数
Step-2. 撰写Java程序
撰写NativeJniAdder.java接口定义类别,其内容如下:
这个JNI接口定义类别含有2个函数:newObject()和execute()。其中,newObject()函数的目的是要诞生一个HalfAdder对象(在C模块里),并且将该对象的指针(Pointer或Reference)传递回来给Java程序。所以newObject()的回传值型态为long。而execute()函数里有个long refer参数,是用来让Java程序能将对象指针传进去给execute()函数,此时execute()就能藉由该指标而调用到先前newObject()函数所诞生的那个对象了。典型的Java程序如下述的ac01类别:
在这ac01.java类别里,指令:
long refer = NativeJniAdder.newObject();
newObject()诞生一个对象,由refer储存newObject()传回来的对象指针。
指令:
int cs = (int)NativeJniAdder.execute(refer, a, b);
将refer传进去给execute()函数。
Step-3. 编译上述的Java程序码,产出NativeJniAdder.class档案
以下步骤,必须由NativeJniAdder.class来产生.h的接口定义头文件。
Step-4. 使用javah转译NativeJniAdder.class檔
使用javah转译,而产出com_misoo_gx05_NativeJniAdder.h头文件;内容为:
/* com_misoo_gx05_NativeJniAdder.h */
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_misoo_gx05_NativeJniAdder */
#ifndef _Included_com_misoo_gx05_NativeJniAdder
#define _Included_com_misoo_gx05_NativeJniAdder
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_misoo_gx05_NativeJniAdder
* Method: newObject
* Signature: ()J
*/
JNIEXPORT jlong JNICALL Java_com_misoo_gx05_NativeJniAdder_newObject
(JNIEnv *, jclass);
/*
* Class: com_misoo_gx05_NativeJniAdder
* Method: execute
* Signature: (JII)J
*/
JNIEXPORT jlong JNICALL Java_com_misoo_gx05_NativeJniAdder_execute
(JNIEnv *, jclass, jlong, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
Step-5. 撰写com_misoo_gx05_NativeJniAdder.c程序码
现在看看如何撰写com_misoo_gx05_NativeJniAdder.c程序码,如下:
/* com_misoo_gx05_NativeJniAdder.c */
#include "HalfAdder.h"
#include "com_misoo_gx05_NativeJniAdder.h"
extern void* HalfAdderNew();
JNIEXPORT jlong JNICALL
Java_com_misoo_gx05_NativeJniAdder_newObject(JNIEnv *env, jclass c)
{
HalfAdder* hadder = (HalfAdder*)HalfAdderNew();
return (jlong)hadder;
}
JNIEXPORT jlong JNICALL
Java_com_misoo_gx05_NativeJniAdder_execute(JNIEnv *env, jclass c,
jlong refer, jint digit_1, jint digit_2)
{
HalfAdder* hadder = (HalfAdder*)refer;
hadder->set_digits(hadder, digit_1, digit_2);
hadder->run(hadder);
long k = hadder->get_carry(hadder)*10 + hadder->get_sum(hadder);
return k;
}
Step-6. 进行编译及连结(产出*.so檔)
将HalfAdder.c和com_misoo_gx05_NativeJniAdder.c编译成为*.o档案。然后对其进行连结,以便产出libNativeJniAdder.so共享程序文件。
Step-7. 执行程序
将.so文件放入Android仿真器里执行的结果是:输出二进制的1加1 等于二进制的10。如下:
以上,基于半加器的范例,介绍了JNI本地开发的过程和基本技术。
[Go Back]