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来回调的函数。

view plaincopy to clipboardprint?
  1. static JNINativeMethod gMethods[] = {  
  2.     { "init""([IIILjava/lang/String;)V", init},  
  3.     { "destroyApp""()V", destroyApp},  
  4.     { "keyPressed""(I)V", keyPressed},  
  5.     { "pointerPressed""(III)V", pointerPressed},  
  6.     { "playSoundCallBack""()V", playSoundCallBack},  
  7.     { "setGPS_Info""(Ljava/lang/String;I)V", setGPS_Info},  
  8.     { "initTimer""()I", initTimer},  
  9.     { "timerCall""(I)V", timerCall},  
  10.     { "returnInputWord""(Ljava/lang/String;)V", returnInputWord},  
  11.     { "callbackSMS""(I)V", callbackSMS},  
  12.     { "receiveData""(I)I", receiveData},  
  13.     { "returnContactNum""(Ljava/lang/String;)V", returnContactNum},  
  14.     { "sendNewSMS2C","(Ljava/lang/String;)Z",sendNewSMS2C},  
  15.     {"setSatelliteSatus","(Ljava/lang/String;)V",setSatelliteSatus},  
  16.     {"setLocationInfo","(Ljava/lang/String;)V",setLocationInfo}  
  17. };  

涉及C调用Java 部分

由于考虑移植性和兼容性,涉及到与系统平台上层相关的函数就采用java来实现,主要实现了对屏幕刷屏,播放mp3, 打电话,发送短信,输入法等函数,也包括网络部分要实现的函数。

view plaincopy to clipboardprint?
  1. method_drawMap = (*env)->GetMethodID(env,cls,"drawMap","(IIIII)V");  
  2. method_playSound = (*env)->GetMethodID(env,cls,"play","(Ljava/lang/String;)V");  
  3. method_callPhone = (*env)->GetMethodID(env,cls,"callPhone","(Ljava/lang/String;)V");  
  4. method_sendSMS=(*env)->GetMethodID(env,cls,"sendSMS","(Ljava/lang/String;Ljava/lang/String;)V");  
  5. method_inputWord = (*env)->GetMethodID(env,cls,"inputWord","(Ljava/lang/String;I)V");  
  6.    
  7. method_startWakelock = (*env)->GetMethodID(env,cls,"startWakelock","()V");  
  8. method_releaseWakelock = (*env)->GetMethodID(env,cls,"releaseWakelock","()V");  
  9.    
  10. method_openConnection =(*env)->GetMethodID(env,cls,"openConnection","(Ljava/lang/String;I)I");  
  11.    
  12. method_sendData = (*env)->GetMethodID(env,cls,"sendData","([B)I");  
  13. method_receiveData = (*env)->GetMethodID(env,cls,"receiveData","([B)I");  
  14. method_searchContact = (*env)->GetMethodID(env,cls,"searchContact","()V");  
  15. method_appExit = (*env)->GetMethodID(env,cls,"destroy_app","()V");  
  16. method_getIME = (*env)->GetMethodID(env,cls,"getIMEI","()Ljava/lang/String;");  

调用Linux的接口

有部分函数在C层调用的,并且能够通过调用 Cruntime函数来实现,这些函数可移植性好,并不需要上层Java来实现,就直接采用,主要包括 Timer同步的内部控制和文件系统。

  • LCD控制部分
view plaincopy to clipboardprint?
  1. U16 Mmi_MapBar_GetLCDHeight(void);  
  2. U16 Mmi_MapBar_GetLCDWidth(void);  
  • Timer控制函数
view plaincopy to clipboardprint?
  1. void MapBar_Api_StopTimer(U16 timerid);  
  2. void MapBar_Api_StartTimer(U16 timerid, U32 delay, FuncPtr funcPtr);  
  3. U32 MapBar_Api_GetTime(void);  
  4. 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系统函数
view plaincopy to clipboardprint?
  1. S32 MapBar_Api_FS_FindNext(FS_HANDLE FileHandle, BOOL *IsFile,WCHAR *FileName,UINT MaxLength );  
  2. S32 MapBar_Api_FS_FindFirst(const WCHAR *NamePattern,BOOL *IsFile,U16 *FileName,UINT MaxLength);  
  3. S32 MapBar_Api_FS_FindClose(S32 handle)   ;  
  4. S32 MapBar_Api_FS_CreateDir(const WCHAR *DirName);   
  5. S32 MapBar_Api_FS_RemoveDir(const WCHAR * DirName);               
  6. S32 MapBar_Api_FS_Rename(const WCHAR * FileFullName,const WCHAR * NewFileFullName);  
  7. S32 MapBar_Api_FS_Delete(const WCHAR * FileName);  
  8. S32 MapBar_Api_FS_Copy(const WCHAR * SrcFullPath, const WCHAR * DstFullPath);  
  9. S32 MapBar_Api_FS_Move(const WCHAR * SrcFullPath, const WCHAR * DstFullPath);  
  10. S32 Mapbar_Api_FS_CreateDir(const WCHAR *DirName);  
  11. S32 Mapbar_Api_FS_RemoveDir(const WCHAR * DirName);  
  12. S32 MapBar_Api_FS_CheckFile(const WCHAR *FileName);  
  13. S32 MapBar_Api_FS_Open(U16 * FileName, U32 Flag);  
  14. S32 MapBar_Api_FS_Close(FS_HANDLE FileHandle);  
  15. U32 MapBar_Api_FS_SetSeekHint(FS_HANDLE FileHandle, U32 HintNum, FS_FileLocationHint * Hint);  
  16. S32 MapBar_Api_FS_Read(FS_HANDLE FileHandle,void * DataPtr /* out */,U32 Length /* in */,U32 * Read /* out */);  
  17. S32 MapBar_Api_FS_Write(FS_HANDLE FileHandle,void * DataPtr /* in */,U32 Length /* in */,U32 * Written /* out */);  
  18. S32 MapBar_Api_FS_Seek(FS_HANDLE FileHandle,S32 Offset,S32 Whence);  
  19. U32 MapBar_Api_FS_GetLength(FS_HANDLE FileHandle);  
  20. BOOL MapBar_Api_FS_GetDiskFreeSpace(U32* freeSpace);  
  21. 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的方法来处理。

代码如下:

view plaincopy to clipboardprint?
  1. private class MTimer extends Thread  
  2. {  
  3.       private boolean isActive = false;  
  4.         
  5.       public MTimer()  
  6.       {  
  7.             super();  
  8.             isActive = true;  
  9.       }  
  10.         
  11.       public void destroy()  
  12.       {  
  13.             isActive = false;  
  14.             try  
  15.             {  
  16.                   this.join();  
  17.             }  
  18.             catch(InterruptedException e)  
  19.             {  
  20.                   e.printStackTrace();  
  21.             }  
  22.       }  
  23.         
  24.       public void run()  
  25.       {  
  26.             while(isActive)  
  27.             {  
  28.                   handler.sendEmptyMessage(1);  
  29.                     
  30.                   try  
  31.                   {  
  32.                        Thread.sleep(20);  
  33.                   }  
  34.                   catch(InterruptedException e)  
  35.                   {  
  36.                        e.printStackTrace();  
  37.                   }  
  38.             }  
  39.       }  
  40. }  
  41. private Handler handler = new Handler()  
  42. {  
  43.       public void handleMessage(Message msg)  
  44.       {  
  45.             switch(msg.what)  
  46.             {  
  47.                   case 1:  
  48.                   {  
  49.                        synchronized(ResultContainer.mMapImageRGB)  
  50.                        {  
  51.                              checkTimer();  
  52.                              break;  
  53.                        }  
  54.                   }  
  55.             }  
  56.             super.handleMessage(msg);  
  57.       }  
  58. };  

从上面的分析来说,是可以勉强保持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