服务热线:13616026886

技术文档 欢迎使用技术文档,我们为你提供从新手到专业开发者的所有资源,你也可以通过它日益精进

位置:首页 > 技术文档 > JAVA > 新手入门 > 基础入门 > 查看文档

教你一招:优化j2me程序大小的方法


  要把j2me程序与j2se程序区分开来,其依据就是j2me运行所处的受限环境。多数j2me系统的主要受限条件就是可以存储和运行程序所需内存的大小。例如,许多midp设备限制应用程序的尺寸不大于50k,这远远不及server端j2se运行环境下那些成兆的程序。实际应用中,程序会很容易超出这些限制条件。通过本篇您将学到一些减小程序尺寸大小的技巧,并在下面的例子中实践这些技术。这个例子midlet仅仅显示一个文本框并在其内容改变时发声。
  package com.j2medeveloper.techtips;
  import javax.microedition.lcdui.*;
  public class beforesizeoptimization extends
  basicmidlet {
  public static final command exitcommand =
  new command( "exit",
  command.exit, 1 );
  public beforesizeoptimization(){
  }
  protected void initmidlet(){
  getdisplay().setcurrent( new mainform() );
  }
  public class mainform extends form {
  public mainform(){
  super( "mainform" );
  addcommand( exitcommand );
  append( textf );
  setcommandlistener( new commandlistener(){
  public void commandaction( command c,
  displayable d ){
  if( c == exitcommand ){
  exitmidlet();
  }
  }
  }
  );
  setitemstatelistener(
  new itemstatelistener() {
  public void itemstatechanged(
  item item ){
  if( item == textf ){
  alerttype.info.playsound(
  getdisplay() );
  }
  }
  }
  );
  }
  
  private textfield textf =
  new textfield( "type anything", null,
  20, 0 );
  }
  }
  虽然这个midlet在此仅作为一个例子,但使用的尺寸优化技巧可以适用于任一j2me的profile上。
  注意,上面的midlet类需要下面的辅助类:
  package com.j2medeveloper.techtips;
  import javax.microedition.lcdui.*;
  import javax.microedition.midlet.*;
  public abstract class basicmidlet extends midlet {
  private display display;
  public basicmidlet(){
  }
  protected void destroyapp( boolean unconditional )
  throws midletstatechangeexception {
  exitmidlet();
  }
  public void exitmidlet(){
  notifydestroyed();
  }
  public display getdisplay(){ return display; }
  protected abstract void initmidlet();
  protected void pauseapp(){
  }
  protected void startapp()
  throws midletstatechangeexception {
  if( display == null ){
  display = display.getdisplay( this );
  initmidlet();
  }
  }
  }
  用j2me wtk打包时,本例子midlet占用4k空间。
  
  减小尺寸的首要步骤就是通过修正程序的功能实现来去掉多余的类。程序的所有功能确实必须都实现吗?用户可以不需要这些“附属功能”吗?要设计尽可能小的程序,这里的midlet例子已经相当小了。
  
  第二步就是深入考察程序定义的内部类,特别是匿名类。记住,每个类文件都有一定量的与之相关的系统开销。即便最普通的类也有系统开销。
  public class foo {
  // nothing here
  }
  编译上边的类,生成的类文件大约200byte大小。比如实现一个事件监听器,就是对匿名类的常见使用。在例子midlet中就定义了两个此类的监听器。接下来进行的最简单的优化就是,让主midlet类实现commandlistener和itemstatelistener接口,并把监听器代码移至此处。记住,多个对象可以使用同样的监听器。必要时,可以使用传递至commandaction和itemstatechanged方法的参变量来区分它们。
  
  内部类也可使代码过大,因为编译器必须生成特殊的变量和方法,以便内部类可以访问包含它们的类的私有内容。请参考内部类的规范以获取更多信息。
  
  第三步,尽量使用现有的类。例如,基于cldc的profile没有构造集合类,所以我们可以用内建的hashtable和vector类来实现之。构造midp程序时也可采用此法。例子midlet中定义了一个form字类来生成主表,可以容易的如下直接生成:
  
  mainform = new form( "mainform" );
  mainform.addcommand( okcommand );
  mainform.setcommandlistener( listener );
  
  这里没有正确或者错误的答案,只是要推敲。
  
  第四步就是破坏程序的继承关系。你也许把相关的代码放到一个或多个抽象类中,这是ood中为提高程序间代码重用的推荐做法。虽然破坏继承关系与你所学知识相违背,但简化的继承关系更有意义。特别的,当你的的抽象类?d?d可能来自其他项目?d?d仅仅被继承一次时,破坏继承关系的结果不言而喻。例如,例子midlet继承了basicmidlet类,但两者合并为一个类。
  
  第五步就是要缩短名字长度,如包名、类名、方法名和数据元素名。看起来有些蠢,但一个类文件确实包含太多的符号信息。缩短各量的名字可以缩小生成的类文件尺寸。这种节省不会特别明显,但多个类中进行总加的结果还是可观的。包名对减小尺寸来讲特别合适。midp程序是完全自我包容的,完全可以不使用包名,因为在手持设备上包名根本不可能与其他类名冲突。例子midlet中,可以把com.j2medeveloper.tchtips包名去掉。
  
  注意,一般来讲,缩短名字不需要手工去做,要用一个“混淆器”去做。“混淆器”的主要功能是“隐藏”程序代码,使之不能通过反编译读出。它的副作用是减小了程序的尺寸。因为隐藏过程主要通过更改方法和数据成员的名字来完成。有一个开源的混淆器称为retroguard,可以免费从http://www.retrologic.com得到。也有一些商业包可用。(当为基于cldc的profile混淆时,记得在预校验之前混淆,否则混淆器将使类文件中的预校验数据失效。)
  
  最后,深入数组的初始化。(例子midlet没有做数组初始化,但对程序来说初始化是重要的一步) 在编译时,一个数组初始化声明如下所示:
  
  int arr[] = { 0, 1, 2, 3 };
  而实际生成代码的过程如下所示:
  arr[0] = 0;
  arr[1] = 1;
  arr[2] = 2;
  arr[3] = 3;
  
  这个过程可以通过使用java 2 sdk中附带的javap工具把二进制代码反编译成类文件去看(使用-c选项)。也许你会诧异于看到的内容,特别当你希望看到的是一排排二进制常数时。有两种方法可以让你看不到反编译的程序代码,(1)把数据编码为字符串,运行时解码之,或者(2)把数据存为二进制文件并与程序打包,用类装载器的getresourceasstream方法在运行时存取之。
  
  以上只是一些指导性的方法,对每个j2me程序而言,这里没有具体到步骤。但是多数方法可以应用的本例。优化后的midlet如下所示:
  
  import javax.microedition.lcdui.*;
  import javax.microedition.midlet.*;
  public class aso extends midlet
  implements commandlistener,
  itemstatelistener {
  private display display;
  private form mainform;
  private textfield mainformtf =
  new textfield( "type anything", null,
  20, 0 );
  public static final command exitcommand =
  new command( "exit",
  command.exit, 1 );
  public aso(){
  }
  public void commandaction( command c,
  displayable d ){
  if( c == exitcommand ){
  exitmidlet();
  }
  }
  protected void destroyapp( boolean unconditional )
  throws midletstatechangeexception {
  exitmidlet();
  }
  public void exitmidlet(){
  notifydestroyed();
  }
  public display getdisplay(){ return display; }
  protected void initmidlet(){
  mainform = new form( "mainform" );
  mainform.addcommand( exitcommand );
  mainform.setcommandlistener( this );
  mainform.setitemstatelistener( this );
  mainform.append( mainformtf );
  getdisplay().setcurrent( mainform );
  }
  public void itemstatechanged( item item ){
  if( item == mainformtf ){
  alerttype.info.playsound( getdisplay() );
  }
  }
  protected void pauseapp(){
  }
  protected void startapp()
  throws midletstatechangeexception {
  if( display == null ){
  display = display.getdisplay( this );
  initmidlet();
  }
  }
  }

扫描关注微信公众号