无缝移植J2ME程序到OPhone平台解决方案

移动开发
1990年12月,Sun公司内部由James Gosling、Patrick Naughton及Mike Sheridan成立了一个叫做Green Team的小组。Green Team小组的主要目标,是要发展一种新架构,而这种架构必须能够 在消费性电子产品作业平台上运行,现在我们普遍认识的PDA、手机或是信息家电(IA)等,都是属于这种架构的目标平台。

为什么需要移植J2ME程序

1990年12月,Sun公司内部由James Gosling、Patrick Naughton及Mike Sheridan成立了一个叫做Green Team的小组。Green Team小组的主要目标,是要发展一种新架构,而这种架构必须能够 在消费性电子产品作业平台上运行,现在我们普遍认识的PDA、手机或是信息家电(IA)等,都是属于这种架构的目标平台。接着,Green Team在1992年9月3号,发表了一款由Java技术之父James Gosling所领导研发的名叫Star Seven(*7)的机器,研发出一部交互式的掌上型家用娱乐装置,可通过使用动画触碰式屏幕的使用者接口来控制其它电子设备。

1999年,Sun公司把Java切割成J2SE、J2ME、J2EE,所以有了J2ME这个名词的出现。经过将近20年的发展,J2ME已经渗入到嵌入式设备的各个领域,J2ME在手机设备上的已有应用程序更是数以千万计。

OPhone平台的Java虚拟机称为“Dalvik”,它是Google公司自己设计用于Android平台的Java虚拟机,通过查看OPhone SDK中的例子可以发现存在J2ME和OPhone很多类库并存的问题。#t#

那么如何移植已经存在的数以千万计的J2ME应用程序到OPhone平台就成为第三方应用程序开发者亟待解决的问题,因为如果全部重新在OPhone平台开发的话必然会花费很大的时间成本和人力成本,而且使得整个产品变得难以升级和维护,甚至错失宝贵的市场机遇。
 
如果存在这样一个解决方案,即现有的J2ME应用程序可以不用修改或者只需要做很少的修改就能运行在OPhone平台,这样开发人员只需要维护一份代码,产品升级和维护也变得简单,企业也节省了开发成本和时间成本,那么对OPhone平台的发展无疑是最有益的。
 
笔者这篇文章就致力于这样一个解决方案,使得现有的J2ME程序只需要做很少的修改就能无缝运行在OPhone平台上,笔者把这个解决方案所需要的源代码单独放入了Wrap包,下文中就统一称为“J2ME适配包”。
 
J2ME适配包之架构
 
现有的J2ME应用程序的界面显示以及按键响应基本都是通过GameCanvas类接口进行处理的,但OPhone平台下并没有这个类。OPhone平台下通过Canvas类和Paint类联合控制绘图句柄,大量的绘制工作如绘制直线、绘制矩形、绘制字符串、绘制图片等操作都在Canvas类里完成,而Paint类则负责视图的布局如文本对齐方式、笔刷填充方式、字体大小和字体样式等工作都在Paint类里完成,所以开发人员需要自己实现这样一个GameCanvas类,并提供相应的绘图句柄接口可以让OPhone平台通过简单的接口调用无缝运行原有的J2ME应用程序,整个J2ME适配包的架构如下:

 

图1 J2ME适配包架构图

适配包开发的核心思想是创建J2ME程序无缝运行的环境。在图1中,GameView类通过实例化Image类创建了J2ME程序运行的窗口,并通过Image类的getGraphics()方法获取图形上下文句柄Graphics对象canvasGraphics,最后通过方法setScreen(GameCanvas screen)传递给原有的J2ME程序主屏幕类,从而使得原有的J2ME程序可以通过图形上下文句柄canvasGraphics对象进行视图绘制和布局控制,其中一些关键的技术笔者会在下文中分述。#p#
 
J2ME适配包之界面

OPhone平台除了在一些接口上采用了和J2ME不一样的名称之外,基本的接口内容还是一样的,笔者这里利用OPhone接口重写了J2ME中的Graphics和Image等类,原有的J2ME程序就可以无缝的运行在OPhone平台上。

既然要重写J2ME下的这些接口,那么就要先了解OPhone平台和J2ME平台到底有哪些不同。
 
1:Font类

OPhone平台里不再提供J2ME下面的Font类,而是以Typeface代替,这为读者提供了更多的选择空间,因为Typeface是可以自定义的,但是为了无缝移植J2ME程序到OPhone平台上,需要封装了一个类似于J2ME平台下的Font类。

定义字体格式,J2ME平台下的字体有三种风格STYLE_PLAIN、STYLE_BOLD以及STYLE_ITALIC,分别代表了普通字体、粗体和斜体。OPhone平台下通过Typeface类的静态方法defaultfromstyle()从默认的字体库中获取不同风格的字体,如下:

  1. private static final Font DEFAULT_FONT = new Font(null,22);     
  2. public static Font getFont(int face,int style,int size)     
  3. {     
  4.     switch(style)     
  5.     {     
  6.     case STYLE_PLAIN:     
  7.         return new Font(Typeface.defaultFromStyle(Typeface.NORMAL),     
  8.                 size);     
  9.     case STYLE_BOLD:     
  10.         return new Font(Typeface.defaultFromStyle(Typeface.BOLD),     
  11.                 size);     
  12.     case STYLE_ITALIC:     
  13.         return new Font(Typeface.defaultFromStyle(Typeface.ITALIC),     
  14.                 size);     
  15.     }     
  16.     return DEFAULT_FONT;     
  17. }    

笔者这里定义的默认字体DEFAULT_FONT大小为22像素,开发者可以根据屏幕大小和项目需要自定义三种风格字体的大小。除此之外,还需要定义Font类常用的接口,比如获取字符宽度以及获取字体高度等,代码如下:

  1. public int charWidth(char arg)     
  2. {     
  3.     return size;     
  4. }     
  5.     
  6. public int stringWidth(String arg)     
  7. {     
  8.     return size*(arg.length());     
  9. }    

2:Graphics类

OPhone平台下不再提供Graphics类进行“视图上下文”的控制,而是以Canvas类和Paint类联合进行控制,这边笔者在上文已经交代过。
 
Graphics类在J2ME平台下是绘图操作和布局格式控制的句柄,OPhone平台下把绘图操作和布局格式控制分别在Canvas类和Paint类里进行了实现。那么如果想无缝移植J2ME应用程序到OPhone平台,就需要利用OPhone平台的Cavans类和Paint类实现类似J2ME平台的Graphics类。
 
Canvas类需要一个Image参数作为绘制操作的窗口,那么笔者这里就传递一个Image参数到Graphics类以初始化Canvas类,如下:

  1. public Graphics(Bitmap bitmap) {     
  2.     this.bitmap = bitmap;     
  3.     this.canvas = new Canvas(bitmap);     
  4.     this.canvas.clipRect(0, 0, bitmap.getWidth(), bitmap.getHeight());     
  5.     this.canvas.save(Canvas.CLIP_SAVE_FLAG);     
  6.     this.paint = new Paint();     
  7.     this.clip = canvas.getClipBounds();     
  8. }  

接下来就封装了一下J2ME平台下Graphics类的绘制操作,比如画线、画图、画字符串等操作,代码如下:

  1. public void fillTriangle(int x1,int y1,int x2,int y2,int x3,int y3)     
  2.     {     
  3.         paint.setStyle(Style.FILL);     
  4.         canvas.drawLine(x1, y1, x2, y2, paint);     
  5.         canvas.drawLine(x2, y2, x3, y3, paint);     
  6.         canvas.drawLine(x3, y3, x1, y1, paint);     
  7.     }     
  8.          
  9. public void setFont(Font font)     
  10.     {     
  11.         paint.setTypeface(font.getTypeface());     
  12.         paint.setTextSize(font.getSize());     
  13.              
  14.         FontMetrics fontMetrics = paint.getFontMetrics();     
  15.         float height = fontMetrics.bottom-fontMetrics.top;     
  16.         font.setHeight((int)height);     
  17.     }     
  18.          
  19.     public void fillArc(int x,int y,int width,int height,     
  20.                                 int startAngle,int arcAngle)     
  21.     {     
  22.         paint.setStyle(Style.FILL);     
  23.         canvas.drawArc(new RectF(x,y,width,height),      
  24.                     startAngle, arcAngle, true, paint);     
  25.     }     
  26.          
  27.     public void drawArc(int x,int y,int width,int height,     
  28.                                     int startAngle,int arcAngle)     
  29.     {     
  30.         paint.setStyle(Style.STROKE);     
  31.         canvas.drawArc(new RectF(x,y,width,height),      
  32.                         startAngle, arcAngle, true, paint);     
  33.     }    

除此之外,还需要在Graphics类里封装J2ME下布局控制的代码,OPhone平台下利用Paint类进行布局控制,所以笔者在Graphics的构造函数里初始化了一个Paint对象,J2ME平台下常见的布局控制是文本对齐方式以及画刷填充格式等,封装如下:

  1. public void setAlign(int align)     
  2.     {     
  3.         if(LEFT == align     
  4.                 ||(Graphics.LEFT | Graphics.TOP) == align     
  5.                 ||(Graphics.LEFT | Graphics.BOTTOM) == align)     
  6.         {     
  7.             paint.setTextAlign(Align.LEFT);      
  8.         }else if(HCENTER == align     
  9.                 ||(Graphics.HCENTER|Graphics.TOP) == align)     
  10.         {     
  11.             paint.setTextAlign(Align.CENTER);      
  12.         }else if(RIGHT == align     
  13.                 ||(Graphics.RIGHT | Graphics.TOP) == align)     
  14.         {     
  15.             paint.setTextAlign(Align.RIGHT);     
  16.         }     
  17.     }    

另外,J2ME平台里设置Graphics绘图句柄的颜色有两种格式:setColor(int r,int g,int b)和setColor(0xRGB),而OPhone平台通过setColor(argb)设置画笔颜色或画刷颜色,代码封装如下:

  1. public void setColor(int rgb) {     
  2.     paint.setColor(rgb);     
  3. }     
  4.     
  5. public void setColor(int r,int g,int b) {     
  6.     int argb = (0xff000000)+(r<<16)+(g<<8)+b;     
  7.     paint.setColor(argb);     
  8. }  

 通过Graphics类的setFont(Font font)方法可以设置绘图句柄的字体,笔者这里通过上面封装的Font类设置绘图句柄的字体,代码如下:

  1. public void setFont(Font font)     
  2. {     
  3.     paint.setTypeface(font.getTypeface());     
  4.     paint.setTextSize(font.getSize());     
  5.          
  6.     FontMetrics fontMetrics = paint.getFontMetrics();     
  7.     float height = fontMetrics.bottom-fontMetrics.top;     
  8.     font.setHeight((int)height);     
  9. }    

上面的代码中,笔者通过FontMetrics类获取字体的高度信息,并调用Font类的setHeight()接口传递高度信息到Font类里。
 
最后,需要提供一些接口给原有的J2ME应用程序,以方便的获取到绘图句柄和布局控制对象,代码如下:

  1. public Canvas getGraphics() {     
  2.     return canvas;     
  3. }     
  4.     
  5. public Paint getPaint() {     
  6.     return paint;     
  7. }     
  8.     
  9. public Rect getClip() {     
  10.     return clip;     
  11. }     
  12.     
  13. public Bitmap getBitmap() {     
  14.     return bitmap;     
  15. }    

#p#

3:Connector类
OPhone平台下不再提供Connector这个静态接口类,而是提供HttpURLConnection以及URL进行网络通讯,值得注意的是HttpURLConnection对象必须调用setDoOutput(true)方法以允许打开输出流对象,调用setDoInput(true)方法以打开输入流对象,代码封装如下:
 

  1. import java.net.HttpURLConnection;     
  2. import java.net.URL;     
  3.     
  4. public class Connector {     
  5.         public static final int READ = 1;     
  6.         public static final int WRITE = 2;     
  7.         public static final int READ_WRITE = 3;     
  8.              
  9.         private static String platform;     
  10.         private static boolean j2me;     
  11.              
  12.         public static HttpConnection open(String name,int mode,     
  13.                                                 boolean timeouts)     
  14.             throws java.io.IOException     
  15.             {     
  16.             URL url = new URL(name);         
  17.                   
  18.             HttpURLConnection conn =      
  19.                     (HttpURLConnection) url.openConnection();           
  20.     
  21.                 // Allow Inputs        
  22.                 conn.setDoInput(true);        
  23.     
  24.             // Allow Outputs        
  25.                 conn.setDoOutput(true);      
  26.                  
  27.             conn.setConnectTimeout(100000);      
  28.                  
  29.             HttpConnection co = new HttpConnection();     
  30.             co.setConnEx(conn);     
  31.                  
  32.             return co;     
  33.             }     
  34. }    

4:HttpConncetion类
J2ME应用程序利用HttpConnection进行HTTP通讯,而OPhone平台采用HttpURLConncetion进行HTTP通讯,这两个不同平台的通讯类接口基本上保持了一致,比如设置HTTP头属性以及获取属性的代码:

  1.   public void setRequestProperty(String field,String newValue)     
  2.                         throws java.io.IOException     
  3. {     
  4.     connEx.setRequestProperty(field, newValue);     
  5. }     
  6. public String getHeaderField(String key)     
  7. {     
  8.     int temp = connEx.getHeaderFieldInt(key, 1024);     
  9.     return String.valueOf(temp);     
  10. }    

但是OPhone平台下获取HTTP包长度的接口名称有所不同,在J2ME平台下是通过getLength()方法获取的,而OPhone平台下通过getContentLength()方法获取,这样命名似乎更加合理。

上面笔者利用OPhone平台的接口封装了J2ME下常用的接口,基本上利用这些接口,J2ME程序可以无缝运行在OPhone平台上。
 
J2ME适配包之按键映射
 
上节《J2ME适配包之界面》中讲解了利用OPhone的接口规范开发J2ME的适配包,从而解决了J2ME程序无法运行在OPhone平台上的技术难题,但是OPhone平台和J2ME另一个重要的不同点就是按键响应接口,如何让J2ME程序的按键响应可以无缝的移植到OPhone上呢?答案也是适配包。
 
笔者这里通过按键映射把OPhone平台的键盘码转换成J2ME平台的键盘码,或者把OPhone平台的触摸事件通过代理传递给J2ME中相应的接口。

首先,需要在OPhone平台下定义J2ME平台GameCanvas类常用的键值码,这些键值码对应J2ME平台下常用的按键:

  1. public class GameCanvas extends Screen {     
  2.     public static final int UP = 1;     
  3.     public static final int DOWN = 6;     
  4.     public static final int LEFT = 2;     
  5.     public static final int RIGHT = 5;     
  6.     public static final int FIRE = 8;     
  7.          
  8.     public static final int GAME_A = 9;     
  9.     public static final int GAME_B = 10;     
  10.     public static final int GAME_C = 11;     
  11.     public static final int GAME_D = 12;     
  12.          
  13.     public static final int KEY_NUM0 = 48;     
  14.     public static final int KEY_NUM1 = 49;     
  15.     public static final int KEY_NUM2 = 50;     
  16.     public static final int KEY_NUM3 = 51;     
  17.     public static final int KEY_NUM4 = 52;     
  18.     public static final int KEY_NUM5 = 53;     
  19.     public static final int KEY_NUM6 = 54;     
  20.     public static final int KEY_NUM7 = 55;     
  21.     public static final int KEY_NUM8 = 56;     
  22.     public static final int KEY_NUM9 = 57;     
  23.     public static final int KEY_STAR = 42;     
  24.     public static final int KEY_POUND = 35;     
  25. }    

当然,为了适配J2ME平台的按键以及触摸响应,笔者还定义了如下接口,在接收到相应的按键或者触摸消息时,会转发给相应的J2ME视图,代码如下:

  1. protected abstract void keyRepeated(int keyCode);     
  2. protected abstract void keyPressed(int keyCode);     
  3. protected abstract void keyReleased(int keyCode);     
  4. protected abstract void pointerPressed(int x, int y);     
  5. protected abstract void pointerReleased(int x, int y)     
  6. protected abstract void pointerDragged(int x, int y);    

上面定义的都是J2ME的视图类Canvas里的按键以及触摸响应接口,这里需要做的工作就是把OPhone里的按键进行映射转换成J2ME里的标准键值后传递给这些接口,并由继承自GameCanvas的界面类进行实现,完全和J2ME里一样了,基本不用修改任何代码。
 
OPhone下进行键值映射首先需要对接收到的按键消息KeyEvent对象进行预处理,然后转换成上面定义的J2ME平台下的标准键值并传递给GameCanvas对象,代码如下:

  1. public int keyActual = 0;     
  2. public int keyAction = 0;     
  3.     
  4. public void keyPreparse(int keyCode,KeyEvent e)     
  5. {     
  6.     if(keyCode == KeyEvent.KEYCODE_0)     
  7.     {     
  8.         keyActual = GameCanvas.KEY_NUM0;     
  9.     }else if(keyCode == KeyEvent.KEYCODE_1)     
  10.     {     
  11.         keyActual = GameCanvas.KEY_NUM1;     
  12.     }else if(keyCode == KeyEvent.KEYCODE_2)     
  13.     {     
  14.         keyActual = GameCanvas.KEY_NUM2;     
  15.     }else if(keyCode == KeyEvent.KEYCODE_3)     
  16.     {     
  17.         keyActual = GameCanvas.KEY_NUM3;     
  18.     }else if(keyCode == KeyEvent.KEYCODE_4)     
  19.     {     
  20.         keyActual = GameCanvas.KEY_NUM4;     
  21.     }else if(keyCode == KeyEvent.KEYCODE_5)     
  22.     {     
  23.         keyActual = GameCanvas.KEY_NUM5;     
  24.     }else if(keyCode == KeyEvent.KEYCODE_6)     
  25.     {     
  26.         keyActual = GameCanvas.KEY_NUM6;     
  27.     }else if(keyCode == KeyEvent.KEYCODE_7)     
  28.     {     
  29.         keyActual = GameCanvas.KEY_NUM7;     
  30.     }else if(keyCode == KeyEvent.KEYCODE_8)     
  31.     {     
  32.         keyActual = GameCanvas.KEY_NUM8;     
  33.     }else if(keyCode == KeyEvent.KEYCODE_9)     
  34.     {     
  35.         keyActual = GameCanvas.KEY_NUM9;     
  36.     }else if(keyCode == KeyEvent.KEYCODE_POUND)     
  37.     {     
  38.         keyActual = GameCanvas.KEY_POUND;     
  39.     }else if(keyCode == KeyEvent.KEYCODE_STAR)     
  40.     {     
  41.         keyActual = GameCanvas.KEY_STAR;     
  42.     }else if(keyCode == KeyEvent.KEYCODE_DPAD_UP)     
  43.     {     
  44.         keyActual = GameCanvas.UP;     
  45.         keyAction = GameCanvas.UP;     
  46.     }else if(keyCode == KeyEvent.KEYCODE_DPAD_DOWN)     
  47.     {     
  48.         keyActual = GameCanvas.DOWN;     
  49.         keyAction = GameCanvas.DOWN;     
  50.     }else if(keyCode == KeyEvent.KEYCODE_DPAD_LEFT)     
  51.     {     
  52.         keyActual = GameCanvas.LEFT;     
  53.         keyAction = GameCanvas.LEFT;     
  54.     }else if(keyCode == KeyEvent.KEYCODE_DPAD_RIGHT)     
  55.     {     
  56.         keyActual = GameCanvas.RIGHT;     
  57.         keyAction = GameCanvas.RIGHT;     
  58.     }else if(keyCode == KeyEvent.KEYCODE_DPAD_CENTER)     
  59.     {     
  60.         keyActual = GameCanvas.FIRE;     
  61.         keyAction = GameCanvas.FIRE;     
  62.     }     
  63.     else if(keyCode == KeyEvent.KEYCODE_SOFT_LEFT)     
  64.     {     
  65.         keyActual = Globe.softKeyLeft;     
  66.     }     
  67.     else if(keyCode == KeyEvent.KEYCODE_SOFT_RIGHT)     
  68.     {     
  69.         keyActual = Globe.softKeyRight;     
  70.     }     
  71. }    

笔者对OPhone里常用的键1~9以及*、#、上下左右、确定键进行了映射,映射后为标准J2ME下的键值,其中keyActual变量保存的即为J2ME平台下的标准键值,接下来只需要把这些键值传递给原来J2ME里的接口即可,代码如下:

  1. public boolean onTouchUp(MotionEvent e) {     
  2.     if (isEvent) {     
  3.         return isEvent;     
  4.     }     
  5.     pointerReleased((int)(e.getX()),(int)(e.getY()));     
  6.     return isEvent;     
  7. }     
  8.     
  9. public boolean onTouchDown(MotionEvent e) {     
  10.     if (isEvent) {     
  11.         return isEvent;     
  12.     }     
  13.     pointerPressed((int)(e.getX()),(int)(e.getY()));     
  14.     return isEvent;     
  15. }     
  16.     
  17. public boolean onKeyDown(int keyCode, KeyEvent e) {     
  18.     keyPreparse(keyCode,e);     
  19.     keyPressed(keyActual);     
  20.     return true;     
  21. }     
  22.     
  23. public boolean onKeyUp(int keyCode, KeyEvent e) {     
  24.     keyPreparse(keyCode,e);     
  25.     keyReleased(keyActual);     
  26.     return true;     
  27. }    

onTouchUp()方法、onTouchDown()方法对触摸事件进行映射处理,把处理后的键值传递给pointerPressed()方法,pointerPressed()方法再派发给相应的GameCanvas视图,onKeyDown()方法、onKeyUp()方法是对按键事件进行的处理,当然,如果有必要还可以实现getGameAction()方法,代码如下:

  1. public int getKeyStates()     
  2. {     
  3.     return keyActual;     
  4. }     
  5. public int getGameAction(int keyCode)     
  6. {     
  7.     return keyAction;     
  8. }    

#p#

J2ME适配包之数据持久存储
 
笔者在前面两节《J2ME适配包之界面》、《J2ME适配包之按键映射》中分别讲了无缝移植J2ME程序到OPhone平台上对界面和用户按键交互所做的适配接口,原则上利用这些接口原有的J2ME程序基本不用做任何的修改就可以运行在OPhone平台上。
 
笔者本节要讲述的是J2ME平台和OPhone平台另外一个重要的不同点,那就是数据持久存储系统。
 
J2ME平台里采用RMS系统进行数据的持久存储,而OPhone平台则提供了丰富的接口进行数据的持久存储,但任何持久存储的本质无非就是数据串行化后被保存到磁盘空间上,仔细研究J2ME平台RMS系统的实现源码可以看到,J2ME是通过一个叫做RecordStoreFile的类进行数据持久化存储的,而这个RecordStoreFile类封装IO对数据进行操作存储的。
 
由此可见,RMS系统也是通过IO把数据串行化后存储应用程序的空间内的,绝大多数的J2ME程序都需要利用RMS来进行出具的持久存储的,比如游戏积分、系统设置等。那么为了无缝移植J2ME到OPhone平台,笔者这里自己写了一个简易的RMS系统,能满足绝大多数应用程序的需要。
 
相对于RMS系统,笔者更喜欢SQLite套件,这个开源的数据库软件使用起来极为方便并未已经为很多平台支持,比如iPhone OS支持的SQLite3套件,更多关于SQLite的信息可以在http://www.sqlite.org/ 网站上找到,这个开源数据库软件是一种文件型数据库,一个数据库就对应一个文件,无需安装数据库服务器端软件。OPhone也同样支持SQLite套件,而且使用起来也非常方便,为开发者进行数据持久存储提供了更方便的途径,但是限于笔者这篇文章集中于无缝移植J2ME应用程序到OPhone平台,所以这里就不再累述,有兴趣的读者可以查找相关的资料。
 
OPhone平台下对文件的操作和J2ME基本一样,但是需要绑定一个Context上下文,以把文件保存到当前应用程序的目录下,这个目录在打开DDMS窗口后可以看到,具体位置是data/data/PACKAGE_NAME/files下面。
 
利用文件操作可以进行数据的读写,其中System.getSystemHandler().getContext()方法为笔者自己定义的全局静态方法,用以保存当前应用程序的Context上下文。利用文件进行数据读写操作的代码如下:

  1.    public String read(String file) {     
  2.     String data = "";     
  3.     try {     
  4.         FileInputStream stream =     
  5.                 System.getSystemHandler().getContext().openFileInput(file);     
  6.         StringBuffer sb = new StringBuffer();     
  7.         int c;     
  8.         while ((c = stream.read()) != -1) {     
  9.             sb.append((char) c);     
  10.         }     
  11.         stream.close();     
  12.         data = sb.toString();     
  13.     
  14.     } catch (FileNotFoundException e) {     
  15.         e.printStackTrace();     
  16.     } catch (IOException e) {     
  17.         e.printStackTrace();     
  18.     }     
  19.     return data;     
  20. }     
  21.     
  22. public void write(String file, byte[] msg) {     
  23.     try {     
  24.         FileOutputStream stream =      
  25.             System.getSystemHandler().getContext().openFileOutput(file,     
  26.                                         Context.MODE_WORLD_WRITEABLE);     
  27.         stream.write(msg);     
  28.         stream.flush();     
  29.         stream.close();     
  30.     } catch (FileNotFoundException e) {     
  31.         e.printStackTrace();     
  32.     } catch (IOException e) {     
  33.         e.printStackTrace();     
  34.     }     
  35. }  

有了基本的读写数据操作,就可以封装RMS中常用的key-value的保存和读取了,因为RMS系统支持的是字节流的读写,所以笔者这里的value都为字节数组byte[],读者可以根据自己的需要进行功能扩展,代码实现如下:

  1. public boolean put(String key, byte[] value) {     
  2.         boolean bSaveOk = false;     
  3.         this.searchKey = key;     
  4.         byte[] data = null;     
  5.         if (value == null) {     
  6.             throw new NullPointerException();     
  7.         }     
  8.         ByteArrayOutputStream bout = null;     
  9.         DataOutputStream dout = null;     
  10.         try {     
  11.             bout = new ByteArrayOutputStream();     
  12.             dout = new DataOutputStream(bout);     
  13.             dout.writeUTF(key);     
  14.             dout.writeInt(value.length);     
  15.             dout.write(value, 0, value.length);     
  16.             data = bout.toByteArray();     
  17.     
  18.             write(dbName,data);          
  19.             bSaveOk = true;     
  20.         } catch (Exception e) {     
  21.             bSaveOk = false;     
  22.             e.printStackTrace();     
  23.         }     
  24.              
  25.         closeDb();     
  26.         return bSaveOk;     
  27.     }     
  28.     
  29.     public byte[] getByteArray(String key) {     
  30.         ByteArrayInputStream bin = null;     
  31.         DataInputStream din = null;     
  32.         byte[] data = null;          
  33.         try {     
  34.                 String valueKey = read(dbName);     
  35.                 din = new DataInputStream(     
  36.                         new ByteArrayInputStream(valueKey.getBytes()));     
  37.     
  38.                 while(din.available() > 0)     
  39.                 {     
  40.                     String getKey = din.readUTF();     
  41.                     int getLength = din.readInt();     
  42.          
  43.                     data = new byte[getLength];     
  44.                     int bytesRead = 0;     
  45.                     while (bytesRead < data.length) {     
  46.                         int count = din.read(data, bytesRead, data.length     
  47.                                 - bytesRead);     
  48.                         if (count == -1)     
  49.                             break;     
  50.                         bytesRead += count;     
  51.                     }     
  52.                          
  53.                     if(getKey.equals(key))     
  54.                         break;     
  55.                 }     
  56.                                  
  57.                 din.close();     
  58.                 din = null;     
  59.         } catch (Exception e) {     
  60.             e.printStackTrace();     
  61.             data = null;     
  62.         }      
  63.              
  64.         closeDb();     
  65.         return data;     
  66.     }     
  67. }    

J2ME适配包总结
 
在上面的三个小节中,笔者分别总结了OPhone平台下无缝移植J2ME应用程序需要修改的三个核心的地方:界面适配、按键映射以及数据持久存储,基本上把这些代码加入到原有的J2ME应用程序,重新导入这些自己实现的J2ME封装类,原有的J2ME程序无需任何修改就可以无缝运行在OPhone平台。

责任编辑:chenqingxiang 来源: ophonesdn
相关推荐

2009-10-10 13:54:20

OPhone

2009-03-22 09:38:03

Android移植J2ME

2010-10-09 16:04:22

J2ME代码优化

2010-09-29 08:57:04

J2ME前景

2010-09-29 12:45:50

J2ME

2009-03-26 09:25:14

J2MEJCPJSR

2010-02-04 13:15:59

Android J2M

2009-06-10 16:27:54

Eclipse调试J2

2010-09-29 13:09:48

OTAJ2ME程序

2010-09-30 13:48:10

J2ME游戏引擎

2010-10-09 15:40:19

CookieJ2ME

2010-09-29 09:19:39

J2ME开发工具

2010-10-09 16:13:10

J2ME应用程序

2010-09-29 09:54:09

J2ME应用程序

2011-05-12 14:34:55

cookieJ2ME

2009-10-19 13:59:39

J2ME编程开发平台

2009-06-11 09:19:38

netbeans实例J2ME游戏

2010-09-29 10:10:06

J2ME代码优化

2010-07-23 16:08:38

OPhone平台

2010-09-30 13:28:55

J2ME图形
点赞
收藏

51CTO技术栈公众号