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

目录

[显示 全部]

为什么需要移植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切割成J2SEj2mej2ee , 所以有了J2ME这个名词的出现。经过将近20年的发展,J2ME已经渗入到嵌入式设备的各个领域,J2ME在手机设备上的已有应用程序 更 是数以千万计。
 
       OPhone 平 台的Java虚拟机称为“Dalvik”,它是google 公 司自己设计用于Android 平 台的Java虚拟机,通过查看OPhone SDK 中 的例子可以发现存在J2ME和OPhone很多类库并存的问题。
 
       那么如何移植已经存在的数以千万计的J2ME应用程序到OPhone平台就成为第三方应用程序开发者亟待解决的问题,因为如果全部重新在OPhone平台 开发的话必然会花费很大的时间成本和人力成本,而且使得整个产品变得难以升级和维护,甚至错失宝贵的市场机遇。
 
       如果存在这样一个解决方案,即现有的J2ME应用程序可以不用修改或者只需要做很少的修改就能运行在OPhone平台,这样开发人员只需要维护一份代码, 产品升级和维护也变得简单,企业也节省了开发成本和时间成本,那么对OPhone平台的发展无疑是最有益的。
 
       笔者这篇文章就致力于这样一个解决方案,使得现有的J2ME程序只需要做很少的修改就能无缝运行在OPhone平台上,笔者把这个解决方案所需要的源代码 单独放入了Wrap包,下文中就统一称为“J2ME适配包”。

J2ME适配包之架构


 
       现有的J2ME应用程序的界面显示以及按键响应基本都是通过GameCanvas类接口进行处理的,但OPhone平台下并没有这个类。OPhone平台 下通过Canvas类和Paint类联合控制绘图句柄,大量的绘制工作如绘制直线、绘制矩形、绘制字符串、绘制图片等操作都在Canvas类里完成,而 Paint类则负责视图的布局如文本对齐方式、笔刷填充方式、字体大小和字体样式等工作都在Paint类里完成,所以开发人员需要自己实现这样一个 GameCanvas类,并提供相应的绘图句柄接口可以让OPhone平台通过简单的接口调用无缝运行原有的J2ME应用程序,整个J2ME适配包的架构 如下:

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

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

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()从默认的字体库中获取不同风格的字体,如下:

Java代码
  1. private   static   final  Font DEFAULT_FONT =  new  Font( null , 22 );  public   static  Font getFont( int  face, int  style, int  size) {  switch (style) {  case  STYLE_PLAIN:  return   new  Font(Typeface.defaultFromStyle(Typeface.NORMAL), size);  case  STYLE_BOLD:  return   new  Font(Typeface.defaultFromStyle(Typeface.BOLD), size);  case  STYLE_ITALIC:  return   new  Font(Typeface.defaultFromStyle(Typeface.ITALIC), size); }  return  DEFAULT_FONT; }    

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

Java代码
  1. public   int  charWidth( char  arg) {  return  size; }  public   int  stringWidth(String arg) {  return  size*(arg.length()); }     

2:Graphics 类

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

Java代码
  1. public  Graphics(Bitmap bitmap) {  this .bitmap = bitmap;  this .canvas =  new  Canvas(bitmap);  this .canvas.clipRect( 00 , bitmap.getWidth(), bitmap.getHeight());  this .canvas.save(Canvas.CLIP_SAVE_FLAG);  this .paint =  new  Paint();  this .clip = canvas.getClipBounds(); }    

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

Java代码
  1. public   void  fillTriangle( int  x1, int  y1, int  x2, int  y2, int  x3, int  y3) { paint.setStyle(Style.FILL); canvas.drawLine(x1, y1, x2, y2, paint); canvas.drawLine(x2, y2, x3, y3, paint); canvas.drawLine(x3, y3, x1, y1, paint); }  ublic  void  setFont(Font font)      
  2.     {  paint.setTypeface(font.getTypeface());    

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

Java代码
  1. public   void  setAlign( int  align) {  if (LEFT == align ||(Graphics.LEFT | Graphics.TOP) == align ||(Graphics.LEFT | Graphics.BOTTOM) == align) { paint.setTextAlign(Align.LEFT); } else   if (HCENTER == align ||(Graphics.HCENTER|Graphics.TOP) == align) { paint.setTextAlign(Align.CENTER); } else   if (RIGHT == align ||(Graphics.RIGHT | Graphics.TOP) == align) { paint.setTextAlign(Align.RIGHT); } }   

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

Java代码
  1. public   void  setColor( int  rgb) { paint.setColor(rgb); }  public   void  setColor( int  r, int  g, int  b) {  int  argb = ( 0xff000000 )+(r<< 16 )+(g<< 8 )+b; paint.setColor(argb); }    

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

Java代码
  1. public   void  setFont(Font font) { paint.setTypeface(font.getTypeface()); paint.setTextSize(font.getSize()); FontMetrics fontMetrics = paint.getFontMetrics();  float  height = fontMetrics.bottom-fontMetrics.top; font.setHeight(( int )height); }    

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

Java代码
  1. public  Canvas getGraphics() {  return  canvas; }  public  Paint getPaint() {  return  paint; }  public  Rect getClip() {  return  clip; }  public  Bitmap getBitmap() {  return  bitmap; }    

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

Java代码
  1. import  java.net.HttpURLConnection;  import  java.net.URL;  public   class  Connector {  public   static   final   int  READ =  1public   static   final   int  WRITE =  2public   static   final   int  READ_WRITE =  3private   static  String platform;  private   static   boolean  j2me;  public   static  HttpConnection open(String name, int  mode,  boolean  timeouts)  throws  java.io.IOException { URL url =  new  URL(name); HttpURLConnection conn = (HttpURLConnection) url.openConnection();  // Allow Inputs conn.setDoInput(true); // Allow Outputs conn.setDoOutput(true); conn.setConnectTimeout(100000); HttpConnection co = new HttpConnection(); co.setConnEx(conn); return co; } }     

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

Java代码
  1. public   void  setRequestProperty(String field,String newValue)  throws  java.io.IOException { connEx.setRequestProperty(field, newValue); }  public  String getHeaderField(String key) {  int  temp = connEx.getHeaderFieldInt(key,  1024 );  return  String.valueOf(temp); }     

但是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平台下常用的按键:

Java代码
  1. public   class  GameCanvas  extends  Screen {  public   static   final   int  UP =  1public   static   final   int  DOWN =  6public   static   final   int  LEFT =  2public   static   final   int  RIGHT =  5public   static   final   int  FIRE =  8public   static   final   int  GAME_A =  9public   static   final   int  GAME_B =  10public   static   final   int  GAME_C =  11public   static   final   int  GAME_D =  12public   static   final   int  KEY_NUM0 =  48public   static   final   int  KEY_NUM1 =  49public   static   final   int  KEY_NUM2 =  50public   static   final   int  KEY_NUM3 =  51public   static   final   int  KEY_NUM4 =  52public   static   final   int  KEY_NUM5 =  53public   static   final   int  KEY_NUM6 =  54public   static   final   int  KEY_NUM7 =  55public   static   final   int  KEY_NUM8 =  56public   static   final   int  KEY_NUM9 =  57public   static   final   int  KEY_STAR =  42public   static   final   int  KEY_POUND =  35 ; }  

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

Java代码
  1. protected   abstract   void  keyRepeated( int  keyCode);  protected   abstract   void  keyPressed( int  keyCode);  protected   abstract   void  keyReleased( int  keyCode);  protected   abstract   void  pointerPressed( int  x,  int  y);  protected   abstract   void  pointerReleased( int  x,  int  y)  protected   abstract   void  pointerDragged( int  x,  int  y);     

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

Java代码
  1. public   int  keyActual =  0public   int  keyAction =  0public   void  keyPreparse( int  keyCode,KeyEvent e) {  if (keyCode == KeyEvent.KEYCODE_0) { keyActual = GameCanvas.KEY_NUM0; } else   if (keyCode == KeyEvent.KEYCODE_1) { keyActual = GameCanvas.KEY_NUM1; } else   if (keyCode == KeyEvent.KEYCODE_2) { keyActual = GameCanvas.KEY_NUM2; } else   if (keyCode == KeyEvent.KEYCODE_3) { keyActual = GameCanvas.KEY_NUM3; } else   if (keyCode == KeyEvent.KEYCODE_4) { keyActual = GameCanvas.KEY_NUM4; } else   if (keyCode == KeyEvent.KEYCODE_5) { keyActual = GameCanvas.KEY_NUM5; } else   if (keyCode == KeyEvent.KEYCODE_6) { keyActual = GameCanvas.KEY_NUM6; } else   if (keyCode == KeyEvent.KEYCODE_7) { keyActual = GameCanvas.KEY_NUM7; } else   if (keyCode == KeyEvent.KEYCODE_8) { keyActual = GameCanvas.KEY_NUM8; } else   if (keyCode == KeyEvent.KEYCODE_9) { keyActual = GameCanvas.KEY_NUM9; } else   if (keyCode == KeyEvent.KEYCODE_POUND) { keyActual = GameCanvas.KEY_POUND; } else   if (keyCode == KeyEvent.KEYCODE_STAR) { keyActual = GameCanvas.KEY_STAR; } else   if (keyCode == KeyEvent.KEYCODE_DPAD_UP) { keyActual = GameCanvas.UP; keyAction = GameCanvas.UP; } else   if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { keyActual = GameCanvas.DOWN; keyAction = GameCanvas.DOWN; } else   if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) { keyActual = GameCanvas.LEFT; keyAction = GameCanvas.LEFT; } else   if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) { keyActual = GameCanvas.RIGHT; keyAction = GameCanvas.RIGHT; } else   if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) { keyActual = GameCanvas.FIRE; keyAction = GameCanvas.FIRE; }  else   if (keyCode == KeyEvent.KEYCODE_SOFT_LEFT) { keyActual = Globe.softKeyLeft; }  else   if (keyCode == KeyEvent.KEYCODE_SOFT_RIGHT) { keyActual = Globe.softKeyRight; } }     

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

Java代码
  1. public   boolean  onTouchUp(MotionEvent e) {  if  (isEvent) {  return  isEvent; } pointerReleased(( int )(e.getX()),( int )(e.getY()));  return  isEvent; }  public   boolean  onTouchDown(MotionEvent e) {  if  (isEvent) {  return  isEvent; } pointerPressed(( int )(e.getX()),( int )(e.getY()));  return  isEvent; }  public   boolean  onKeyDown( int  keyCode, KeyEvent e) { keyPreparse(keyCode,e); keyPressed(keyActual);  return   true ; }  public   boolean  onKeyUp( int  keyCode, KeyEvent e) { keyPreparse(keyCode,e); keyReleased(keyActual);  return   true ; }    

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

Java代码
  1. public   int  getKeyStates() {  return  keyActual; }  public   int  getGameAction( int  keyCode) {  return  keyAction; }     

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上下文。利用文件进行数据读写操作的代码如下:

Java代码
  1. public  String read(String file) { String data =  ""try  { FileInputStream stream = System.getSystemHandler().getContext().openFileInput(file); StringBuffer sb =  new  StringBuffer();  int  c;  while  ((c = stream.read()) != - 1 ) { sb.append(( char ) c); } stream.close(); data = sb.toString(); }  catch  (FileNotFoundException e) { e.printStackTrace(); }  catch  (IOException e) { e.printStackTrace(); }  return  data; }  public   void  write(String file,  byte [] msg) {  try  { FileOutputStream stream = System.getSystemHandler().getContext().openFileOutput(file, Context.MODE_WORLD_WRITEABLE); stream.write(msg); stream.flush(); stream.close(); }  catch  (FileNotFoundException e) { e.printStackTrace(); }  catch  (IOException e) { e.printStackTrace(); } }    

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

Java代码
  1. public   boolean  put(String key,  byte [] value) {  boolean  bSaveOk =  falsethis .searchKey = key;  byte [] data =  nullif  (value ==  null ) {  throw   new  NullPointerException(); } ByteArrayOutputStream bout =  null ; DataOutputStream dout =  nulltry  { bout =  new  ByteArrayOutputStream(); dout =  new  DataOutputStream(bout); dout.writeUTF(key); dout.writeInt(value.length); dout.write(value,  0 , value.length); data = bout.toByteArray(); write(dbName,data); bSaveOk =  true ; }  catch  (Exception e) { bSaveOk =  false ; e.printStackTrace(); } closeDb();  return  bSaveOk; }  public   byte [] getByteArray(String key) { ByteArrayInputStream bin =  null ; DataInputStream din =  nullbyte [] data =  nulltry  { String valueKey = read(dbName); din =  new  DataInputStream(  new  ByteArrayInputStream(valueKey.getBytes()));  while (din.available() >  0 ) { String getKey = din.readUTF();  int  getLength = din.readInt(); data =  new   byte [getLength];  int  bytesRead =  0while  (bytesRead < data.length) {  int  count = din.read(data, bytesRead, data.length - bytesRead);  if  (count == - 1break ; bytesRead += count;      
  2.                     }      
  3.                           
  4.                      if (getKey.equals(key))      
  5.                          break ;      
  6.                 }      
  7.                                   
  8.                 din.close();      
  9.                 din =  null ;      
  10.         }  catch  (Exception e) {      
  11.             e.printStackTrace();      
  12.             data =  null ;      
  13.         }       
  14.               
  15.         closeDb();      
  16.          return  data;      
  17.     }      
  18. }     

J2ME适配包总结


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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值