您的位置:首页 > 移动开发 > Android开发

移植心得---android平台

2009-01-12 16:29 507 查看

移植心得---android平台

目前的情况:
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应用程序打好基础了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: