[Go Back] 

[請指教:高老師的免費on-line教學視頻] 

 

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]