移植心得---android平台

来源:互联网 发布:神机妙算软件培训 编辑:程序博客网 时间:2024/05/16 05:23

目前的情况:

    android平台的UI开发语言最好选择是JAVA,也是google推荐使用的;

    OCR引擎是C编写的,用JAVA改写显然不现实


平台环境:

    OS: Windows XP SP2

JDK: 1.6.0_06

Toolchins: arm-2008q3-41-arm-none-linux-gnueabi + cygwin

SDK: android-sdk-windows-1.0_r1

IDE: eclipse v3.4.1


应对的策略:

    考虑采用JNI(Java Native Interface)来解决这个问题,由于要用Java来调用引擎,所以需要将引擎编译成so文件(shared library)


由于手头上没有Android的真机,所以以下步骤都是在模拟器上验证:

1.    Android环境搭建

a)       安装JAVA

安装jdk并设置相应的环境变量。我在这里是安装到D:/Java/jdk1.6.0_06 (为了避免路径的问题,在路径的选择上尽量避免使用到空格)


b)       安装emulator

解压缩SDK到硬盘上(我是解压到J:/android目录下,如J:/android/emulator/android-sdk-windows-1.0_r1),添加相应的环境变量(path= J:/android/emulator/android-sdk-windows-1.0_r1/tools),并在C:/Documents and Settings/chongxishen/Local Settings/Application Data目录下创建一个Android的文件夹(假设C是系统盘的盘符),然后到J:/android/emulator/android-sdk-windows-1.0_r1/tools目录下用emulator -wipe-data启动模拟器来完成初始化。之所以在启动之前创建Android文件夹是为了避免模拟器启动过程中创建文件夹失败。


c)       安装IDE

解压缩eclipse,并安装android的插件,然后设置Android的SDK路径。详细请参考http://code.google.com/android/intro/installing.html

 

d)       安装编译器

安装arm-2008q3-41-arm-none-linux-gnueabi到D:/CodeSourcery/SourceryGppLite(同样,路径尽量避免出现空格)


2.    Native C application for Android

我们可以模仿Benno的方法来确认。详细请参考http://benno.id.au/blog/2007/11/13/android-native-apps


3.    Native C Shared library for Android

我们可以参考Motz的文章(详细请参考http://honeypod.blogspot.com/2007/12/shared-library-hello-world-for-android.html)。首先我们需要编写Shared library相应的makefile文件。由于是在windows环境,所以我们需要安装一个交叉编译环境,这里我选择cygwin。


好了,通过上面的步骤,我们已经验证了所编译的so文件是可以运行在android平台上的,到这里我们已经迈出了Android平台移植的一小步,但对我们来说已经是一大步了。


4.    JNI(关键)

当然,我们的目标是通过JAVA来调用这个so文件,所以我们还要move on。这样我们就要涉及到JNI了。通过网上查找资料,我们了解到,对于android 1.0,google是不支持JNI的,但我们知道android的内核是linux,所以应该是有办法达到我们的目标的。


先到官方网站上了解下JNI到底长什么样子(详细请参考http://java.sun.com/developer/onlineTraining/Programming/JDCBook/jniexamp.html)。对于JNI来说,困难的是类型的匹配。这里我只是解释我碰到的问题。


好了,开始编写我们自己的JNI了

C的函数如下:

#if defined(__cplusplus)

extern "C" {

#endif


EXPORT int HC_StartBCR(BEngine **ppEngine,

                       char *pDataPath,

                       char *pConfigFile,

                       int nLanguage);

EXPORT int HC_CloseBCR(BEngine **pEngine);

EXPORT  int HC_DoImageBCR(BEngine *pEngine,

                          BImage *pImage,

                          BField **ppField);


/// 省略其他函数


#if defined(__cplusplus)

}

#endif


其中BEngine,BImage和BField都是自定义结构体,JAVA中没有匹配的类型,而还涉及到指针,甚至二级指针。在这里,我们需要做些变通,因为JAVA中没有指针,但考虑到指针无非就是个地址,在32-bit机中相当于int,考虑到以后64-bit机,我在JAVA中采用long来对应C中的一级指针,对于二级指针,则采用数组。由于char在JAVA中是占用2bytes,所以对于char *我采用byte[]来对应。底下是我写的对应OCR的JAVA类。


public class OCR

{

    static {

        try {

            System.load("/data/hcbcr/libocrengine.so");

        } catch (Exception e) {

            //Log.e("JNI", e.toString());

        }

    }

 

    /*

     * NOTE:

     *  Use long type instead of Pointer value of the real C pointer.

     *  Type int is also OK, but Use long to be 64-bit safe.

     *

     *  Here just list all APIs of OCR Engine, MAYBE will write a class

     *  to wrap this class for better encapsulation and easy usage.

     */

 

    /// native function list

    public native int startBCR(long[] ppEngine, byte[] pDataPath, byte[] pConfigFile, int nLanguage);

    public native int closeBCR(long[] ppEngine);

    public native int doImageBCR(long pEngine, long pImage, long[] ppField);

 

    // … 这里部分函数省略

 

///

    public static void main(String args[])

    {

        OCR ocr = new OCR();

 

        //ocr.startBCR()

    }

 

}

注意其中加粗体部分:使用System.load能够加载非系统路径(/System/lib)下的so文件,因为默认/System下权限都是read-only(当然我们可以用adb remount来更改为有写的权限);使用static可以避免so被多次加载到内存,这点跟C++类中的static变量类似,所有的instances都共享。


采用javac OCR.java编译出OCR.class,接下来采用javah –jni OCR产生OCR.h对应C的H文件,添加相应的OCR.c文件,并完成相应函数编写。

 

这里有2点需要注意:

1. 很多资料上都提到由javah产生的OCR.h不能改动,但在android平台,我发现需要添加2个函数:

JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved);

JNIEXPORT void JNI_OnUnload(JavaVM *vm, void *reserved);


代码实现如下:

#define PACKAGE_NAME        "com.android.hcbcr.OCR"

#define ARRAY_SIZE(arr)     (sizeof(arr)/sizeof(arr[0]))


static JNINativeMethod g_jm[] = {

    { "startBCR", "([J[B[BI)I", (void *)Java_OCR_startBCR },

    { "closeBCR", "([J)I",(void *) Java_OCR_closeBCR },

    { "doImageBCR", "(JJ[J)I",  (void *)Java_OCR_doImageBCR },

    // 省略部分函数

};


/*

* In order to use the JNI functions introduced in J2SE release 1.2,

* in addition to those that were available in JDK/JRE 1.1, a native

* library must export a JNI_OnLoad function that returns JNI_VERSION_1_2.


* In order to use the JNI functions introduced in J2SE release 1.4,

* in addition to those that were available in release 1.2, a native

* library must export a JNI_OnLoad function that returns JNI_VERSION_1_4.


* If the native library does not export a JNI_OnLoad function,

* the VM assumes that the library only requires JNI version JNI_VERSION_1_1.

* If the VM does not recognize the version number returned by JNI_OnLoad,

* the native library cannot be loaded.

*/

JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved)

{

    jint r;

    jclass k;

    JNIEnv *env;

    jint i, size = ARRAY_SIZE(g_jm);


    r = (*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_4);

    k = (*env)->FindClass(env, PACKAGE_NAME);


    for (i = 0; i < size; i++) {

        r = (*env)->RegisterNatives(env, k, &g_jm[i], i+1);

    }


    return JNI_VERSION_1_4;

}


JNIEXPORT void JNI_OnUnload(JavaVM *vm, void *reserved)

{

    jint r;

    jclass k;

    JNIEnv *env;


    r = (*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_2);

    k = (*env)->FindClass(env, PACKAGE_NAME);

    (*env)->UnregisterNatives(env, k);

}


其中com.android.hcbcr是我这个例子的包。至于这2个函数的含义细节可以参考http://java.sun.com/javase/6/docs/technotes/guides/jni/spec/jniTOC.html


2. 由于在windows平台,如果采用D:/Java/jdk1.6.0_06/include/win32下的*.h文件,无法编译通过(__int64是windows定义的类型,我们采用的GCC ARM编译器不支持),我们可以用JAVA的linux版本中jdk1.6.0_06/include/linux下的*.h文件


最后将OCR.h/.c文件跟引擎一起编译,这样就可以得到我们想要的Android平台的so文件了。


5.    验证新的so文件

用Eclipse创建一个测试的Android工程(详细请参考http://code.google.com/android/intro/hello-android.html),需要将OCR.java添加到工程中(其中main函数需要屏蔽掉),在onCreate中添加测试代码:

OCR ocr = new OCR();

ocr. startBCR(…);

// …. 省略部分代码

 

这样,我们就完成了目标,就可以为下一步开发高效的Android应用程序打好基础了。

原创粉丝点击