OPhone平台的导航软件移植
来源:互联网 发布:男人喜欢丝袜调查数据 编辑:程序博客网 时间:2024/04/30 12:49
OPhone平台的导航软件移植
导航软件,在OPhone平台的移植,相对其他的平台的移植,有着很多的特殊性,其中最主要的一个原因,OPhone采用Java作为开发语言,而一般的导航软件,为了性能和跨平台的方便,都采用C或者C++语言,这必然增加了移植的复杂度,不得不考虑采用JNI技术,通过JNI来实现java 和 C的互操作和互调用。本文主要介绍导航软件的移植要点,通过对这些要点的介绍,来了解导航软件的移植过程,并能动手处理相关的问题。
移植背景
软件架构是采用单线程架构来完成,很多需要实时处理的,是采用同步Timer的机制来实现的,包括对GPS的处理,网络的处理等,所以所有移植过程中引入多线程模块必须是独立的,与其他模块没有耦合性的,后面有详细的介绍。
在平台层上,有很多涉及不同平台的函数,为了屏蔽实现平台的差异性,都做了转接层,也就是我们所说的适配层,而在OPhone平台的实现,可能要要复杂些,有些函数需要在C runtime层次实现,有些需要在OPhone的Java层实现,并最好融合在一起,下面有分析。
平台相关的函数
在软件移植中,主要考虑的就是与平台相关的函数,涉及到平台的方方面面,为了说明方便,统称系统适配层。软件适配层根据现有的标准机制,大概可以分为如下几层,如图-1所示:
图-1
涉及到模块有很多,其中有些java来实现,有些采用linux C 接口,有些通过java来调用有些通过C 接口来调用 java,为了描述方便,分为三大类:
涉及java 与 C 互操作部分
由于整个架构采用C API来驱动,Java只是一个驱动器,他只负责处理APP的初始化和需要APP来驱动的函数,这样的函数,是很好控制,包括初始化工作,以及Event事件处理,也包括需要APP来回调的函数。
- static JNINativeMethod gMethods[] = {
- { "init", "([IIILjava/lang/String;)V", init},
- { "destroyApp", "()V", destroyApp},
- { "keyPressed", "(I)V", keyPressed},
- { "pointerPressed", "(III)V", pointerPressed},
- { "playSoundCallBack", "()V", playSoundCallBack},
- { "setGPS_Info", "(Ljava/lang/String;I)V", setGPS_Info},
- { "initTimer", "()I", initTimer},
- { "timerCall", "(I)V", timerCall},
- { "returnInputWord", "(Ljava/lang/String;)V", returnInputWord},
- { "callbackSMS", "(I)V", callbackSMS},
- { "receiveData", "(I)I", receiveData},
- { "returnContactNum", "(Ljava/lang/String;)V", returnContactNum},
- { "sendNewSMS2C","(Ljava/lang/String;)Z",sendNewSMS2C},
- {"setSatelliteSatus","(Ljava/lang/String;)V",setSatelliteSatus},
- {"setLocationInfo","(Ljava/lang/String;)V",setLocationInfo}
- };
涉及C调用Java 部分
由于考虑移植性和兼容性,涉及到与系统平台上层相关的函数就采用java来实现,主要实现了对屏幕刷屏,播放mp3, 打电话,发送短信,输入法等函数,也包括网络部分要实现的函数。
- method_drawMap = (*env)->GetMethodID(env,cls,"drawMap","(IIIII)V");
- method_playSound = (*env)->GetMethodID(env,cls,"play","(Ljava/lang/String;)V");
- method_callPhone = (*env)->GetMethodID(env,cls,"callPhone","(Ljava/lang/String;)V");
- method_sendSMS=(*env)->GetMethodID(env,cls,"sendSMS","(Ljava/lang/String;Ljava/lang/String;)V");
- method_inputWord = (*env)->GetMethodID(env,cls,"inputWord","(Ljava/lang/String;I)V");
- method_startWakelock = (*env)->GetMethodID(env,cls,"startWakelock","()V");
- method_releaseWakelock = (*env)->GetMethodID(env,cls,"releaseWakelock","()V");
- method_openConnection =(*env)->GetMethodID(env,cls,"openConnection","(Ljava/lang/String;I)I");
- method_sendData = (*env)->GetMethodID(env,cls,"sendData","([B)I");
- method_receiveData = (*env)->GetMethodID(env,cls,"receiveData","([B)I");
- method_searchContact = (*env)->GetMethodID(env,cls,"searchContact","()V");
- method_appExit = (*env)->GetMethodID(env,cls,"destroy_app","()V");
- method_getIME = (*env)->GetMethodID(env,cls,"getIMEI","()Ljava/lang/String;");
调用Linux的接口
有部分函数在C层调用的,并且能够通过调用 Cruntime函数来实现,这些函数可移植性好,并不需要上层Java来实现,就直接采用,主要包括 Timer同步的内部控制和文件系统。
- LCD控制部分
- U16 Mmi_MapBar_GetLCDHeight(void);
- U16 Mmi_MapBar_GetLCDWidth(void);
- Timer控制函数
- void MapBar_Api_StopTimer(U16 timerid);
- void MapBar_Api_StartTimer(U16 timerid, U32 delay, FuncPtr funcPtr);
- U32 MapBar_Api_GetTime(void);
- void MapBar_Api_GetDateTime(U32 *rtc_year, U32* rtc_mon, U32 * rtc_wday,U32* rtc_day, U32* rtc_hour, U32* rtc_min, U32 *rtc_sec);
- FS系统函数
- S32 MapBar_Api_FS_FindNext(FS_HANDLE FileHandle, BOOL *IsFile,WCHAR *FileName,UINT MaxLength );
- S32 MapBar_Api_FS_FindFirst(const WCHAR *NamePattern,BOOL *IsFile,U16 *FileName,UINT MaxLength);
- S32 MapBar_Api_FS_FindClose(S32 handle) ;
- S32 MapBar_Api_FS_CreateDir(const WCHAR *DirName);
- S32 MapBar_Api_FS_RemoveDir(const WCHAR * DirName);
- S32 MapBar_Api_FS_Rename(const WCHAR * FileFullName,const WCHAR * NewFileFullName);
- S32 MapBar_Api_FS_Delete(const WCHAR * FileName);
- S32 MapBar_Api_FS_Copy(const WCHAR * SrcFullPath, const WCHAR * DstFullPath);
- S32 MapBar_Api_FS_Move(const WCHAR * SrcFullPath, const WCHAR * DstFullPath);
- S32 Mapbar_Api_FS_CreateDir(const WCHAR *DirName);
- S32 Mapbar_Api_FS_RemoveDir(const WCHAR * DirName);
- S32 MapBar_Api_FS_CheckFile(const WCHAR *FileName);
- S32 MapBar_Api_FS_Open(U16 * FileName, U32 Flag);
- S32 MapBar_Api_FS_Close(FS_HANDLE FileHandle);
- U32 MapBar_Api_FS_SetSeekHint(FS_HANDLE FileHandle, U32 HintNum, FS_FileLocationHint * Hint);
- S32 MapBar_Api_FS_Read(FS_HANDLE FileHandle,void * DataPtr /* out */,U32 Length /* in */,U32 * Read /* out */);
- S32 MapBar_Api_FS_Write(FS_HANDLE FileHandle,void * DataPtr /* in */,U32 Length /* in */,U32 * Written /* out */);
- S32 MapBar_Api_FS_Seek(FS_HANDLE FileHandle,S32 Offset,S32 Whence);
- U32 MapBar_Api_FS_GetLength(FS_HANDLE FileHandle);
- BOOL MapBar_Api_FS_GetDiskFreeSpace(U32* freeSpace);
- U32 MapBar_Api_FS_Tell(FS_HANDLE FileHandle);
TTS处理
由于OPhone 1.0的版本不支持对内存流的语音播报,只能播放url文件为参数进行语音播放,在底层移植的TTS时,只能每次生成一个文件,然后调用上层java 去播报该文件,其中对于涉及到TTS语音合成部分,采用内存映射的方法来处理,这种方法效率还行,如果直接支持内存流播放,那效果更好,希望下个版本的OPhone能提供相关的接口。
网络模块
导航软件采用单线程架构设计,为了更多地利用支援,将数据联网下载数据这种业务都是拿到单独的线程来处理,每个新应用启动,都启动一个新的线程来来下载,不过现在有一种更好的方法来处理该问题,采用Asyntask对象的方法来处理。
同步 Timer 模拟
由于导航系统大量地使用了,Timer 机制,采用linux的底层机制的timer,发现timer太多了,不好控制,同时也会导致异步问题,采用java 的timer,架构设计不好做,10几个timer ,关闭和开启,管理稍微有点差错,就可能导致各种不同的问题,为了同一管理,timer的调度机制,采用线程结合Handler的方法来处理。
代码如下:
- private class MTimer extends Thread
- {
- private boolean isActive = false;
- public MTimer()
- {
- super();
- isActive = true;
- }
- public void destroy()
- {
- isActive = false;
- try
- {
- this.join();
- }
- catch(InterruptedException e)
- {
- e.printStackTrace();
- }
- }
- public void run()
- {
- while(isActive)
- {
- handler.sendEmptyMessage(1);
- try
- {
- Thread.sleep(20);
- }
- catch(InterruptedException e)
- {
- e.printStackTrace();
- }
- }
- }
- }
- private Handler handler = new Handler()
- {
- public void handleMessage(Message msg)
- {
- switch(msg.what)
- {
- case 1:
- {
- synchronized(ResultContainer.mMapImageRGB)
- {
- checkTimer();
- break;
- }
- }
- }
- super.handleMessage(msg);
- }
- };
从上面的分析来说,是可以勉强保持timer的实时性,从实践的效果来看,这种消息处理加线程驱动的方法能满足导航中对时间精度的要求,不过如果在java层直接支持同步式的timer方式,可能效率更高,能避免很多无效的调用,特别是JNI的大量损耗。
调试方法
- trace 方法
这种方法是最常用的方法,主要是java和底层C代码都能在同一的框架下测试,对于一般性功能和重复性高的bug,是一种很管用的方法。
- gdb
OPhone 带有 gdb 调试器,在我们早期的开发过程中,采用过,使用也很方便,但一旦出现多线程,gdb就不太好用了,而且调试起来也麻烦。
- 堆栈分析
有些bug,特别是少概率的异常退出的高级别bug,有时候,是很难通过trace或者gdb 去跟踪的,只要在退出时,通过trace文件,还是很方便地定位到退回时的函数堆栈,从而确定问题所在。
性能评估
Java与 C 的互操作是通过JNI,发现JNI对性能的损耗还是很明显,在移植导航时,做了两个版本导航,其中一个版本采用移植的UI,没有采用OPhone的GUI,而另外一个版本采用Java, 两个版本,进行测试,采用java做UI, 并通过大量JNI接口来调用C接口,明显反应时间慢,基本操作大概2~3倍相差的性能,所以在移植产品时一定要做好性能评测,并确定是否可接受的范围内,具体的性能损耗,笔者没有做过详细的评测。
总结
在OPhone中,移植软件可能比一般平台更复杂,但只要确定方案,确定哪些功能需要java层处理,那些需要C层处理,并相应确定调用方式和顺序,这样就可以达到事半功倍,减少很多的调试时间。同时,在开发中,能够有一个即对OPhone, java 平台熟悉,又对Linux低层平台熟悉的人员参与开发,可以少走很多的弯路,提高效率。
作者
毛灵飞 北京图为先科技有限公司 开发经理 有6年的开发经验,一直从事导航软件的开发和平台,开发平台包括 linux, mobile, symbian, 以及现在的OPhone。
陈良宏 北京图为先科技有限公司 开发经理
http://www.ophonesdn.com/article/show/150
- OPhone平台的导航软件移植
- Android应用移植到OPhone平台指南
- J2ME游戏移植OPhone平台要点
- 无缝移植J2ME程序到OPhone平台解决方案
- OPhone平台的JNI机制探索
- J2ME移植到ophone
- J2ME移植到ophone
- J2ME移植到ophone
- 将J2ME游戏移植到OPhone上的指导
- 移植软件到云平台
- 在Win xp下搭建OPhone开发平台的方法
- Ophone的程序:Hello, OPhone!
- H264解码器移植到OPhone
- J2ME如何移植到ophone
- J2ME如何移植到ophone
- 了解OPhone平台---OPhone平台架构和主要开发组件
- 导航软件的未来
- Ophone平台蓝牙编程基础
- Java面试宝典2010(二。算法题)
- 一份很有意思的面试过程纪要
- Widget API 接口实例演示(二) ——Telephony类和PIM类
- Java面试宝典2010(三. html&JavaScript&ajax部分)
- Java 正则表达式
- OPhone平台的导航软件移植
- Java面试宝典2010(五. 数据库部分)
- 记录使用BCB6出现的问题
- Java面试宝典2010(六. XML部分)
- CSS利用负margin左边固定右边自适应布局
- Java面试宝典2010(八. 软件工程与设计模式)
- HLSL研究学习 之一
- 无刷新重新获取验证码 php
- SharePoint随笔