J2ME系列学习(三)

         1 MIDlet的基本程序结构

    当我们撰写Java Applet时,必须继承自java.applet.Applet这个类,撰写Java Servlet,则必须继承自javax.servlet.http.HttpServlet这个类。同理,要撰写MIDP应用程序的基本执行单位MIDlet,必须继承自javax.microedition.midlet.MIDlet类。

        javax.microedition.midlet.MIDlet类中定义了三个抽象方法(abstract method),因此我们自己撰写MIDlet必须实现他们,这三个抽象方法分别是:

  startApp()  至运作状态;

  pauseApp()  至停止状态;  

 destroyApp()  至消灭状态。

    应用程序管理器通过这三个抽象方法来控制MIDlet的生命周期。因此,所有的MIDlet都必须实现这三个方法,才保证能正常运作。因此一个MIDlet的外观至少要如下:

import javax.microedition.midlet.*;

public class HelloMIDlet extends MIDlet

{

    public HelloMIDlet()

    {

        //构造函数

    }

    public void startApp()

    {

    }

    public void pauseApp()

    {

    }

    public void destroyApp(boolean unconditional)

    {

    }

}

根据MIDP规范,MIDlet中不应该有public static void main(String[] args)这个方法,如果有的话,应用程序管理器会忽略不管。

一旦MIDletJAM加载之后,首先会先调用MIDlet中没有参数的构造函数以进行初始化的工作。Java编译器有一样特性,就是如果您没有在程序中加入任何构造函数,编译器会自动帮您加入一个缺省构造函数(default constructor),但是如果您撰写了自己的构造函数(只要有参数),那么编译器将不会自动帮您加上缺省构造函数。某些特定情况下,万一有必要撰写带有参数的构造函数时,别忘了再加上一个没有参数的构造函数,否则MIDlet将无法正确地初始化,也就无法顺利地启动。

当我们的MIDlet被应用程序管理器产生之后,MIDlet就开始运作,这时我们会使用Display.getDisplay(this)来取得代表该设备显示画面的Display对象。从应用程序管理器调用startApp()MIDlet结束运作这段时间之内,不管何时调用Display.getDisplay(this),取得的都是同一份Display对象的引用。所以通常我们会将取得的Display对象保留下来,供往后使用。

每个MIDlet都有一份逻辑上属于自己的Display(这就是为什么要传入MIDlet引用作为getDisplay()参数的原因),就算是同一个MIDlet Suite里头的MIDlet,或是同一时间在同一个VM运作的MIDlet,所取得的也会是不同的Display对象。

要设定显示在屏幕上的画面,我们会使用Display对象的引用,并调用其setCurrent()方法:display.setCurrent( Displayable类的子类实体)

 

2  MIDlet的生命周期

MIDlet被应用程序管理器成功地初始化之后,就开始展开了它的生命周期。MIDlet的生命周期完全由应用程序管理器控制,也就是说,当MIDlet要从一个状态变成另外一个状态时,应用程序管理器会调用对应的回调函数(call back,也就是MIDlet类定义的那三个抽象方法)。基本上,MIDlet有三种状态,分别是停止状态(Paused)、激活状态(Active)以及消灭状态(Destroyed)。MIDlet一开始一定是先进入停止状态,然后应用程序管理器再将它转换成激活状态,然后调用startApp()

请在心里树立一个概念,那就是:只有当应用程序管理器认为MIDlet的状态必须改变时,才会调用图中的相关函数。以Active状态来说,MIDlet先进入运作状态,然后才调用startApp()。而MIDlet会先调用pauseApp()destroyApp(),然后再进入停止状态和消灭状态。这就是之所以Active没有被动式(字尾没有加ed),而PausedDestroyed都是被动式(字尾加ed)的真正涵义。

如果MIDlet自己调用这些函数,通常不会发生错误(除非程序本身有逻辑上的错误),但是也不会造成状态的转换,只能当成一个单纯的函数调用而已。如果MIDlet在状态转换回调函数执行时发生错误,那么就应该抛出MIDletStateChangeException异常,让应用程序管理器知道该如何处理。

startApp()很可能不光只被调用一次而已,而是每次从停止状态重新回到运作状态的时候都会被应用程序管理器调用。所以只需要被初始化一次的动作就不适合放在startApp()之中,请改用构造函数做初始化动作。如果startApp()抛出MIDletStateChangeExceptionRuntimeException或两者的子类,那么会立刻进入消灭状态,而且系统会自动调用destroyApp(true)

MIDP规范告诉我们,startApp()的执行时间应该尽可能的短。如果程序在执行时,发生的错误是可以过一阵子就解决的(很可能是系统资源暂时不足),那么程序员就该直接抛出MIDletStateChangeException,拦截之后,再调用notifyPaused(),稍待一会再经由异步事件调用resumeRequest(),重新试试看。如果发生错误即使稍待一会也无法解决,那么程序员就应该直接调用notifyDestroyed()来结束程序。

应用程序管理器会因为某些状况,必须请MIDlet停止运作,例如手机突然来电,或者闹铃响了,或者用户切换到其它程序执行,在这些情况下,为了避免MIDlet占用太多系统资源,所以应用程序管理器就会调用该MIDletpauseApp(),这时程序员应该在pauseApp()之中适时释放一些非必需的资源,等到往后回到运作状态时,应用程序管理器会重新调用startApp(),这时我们再将这些之前被pauseApp()释放的资源重新加载。

MIDlet进入停止状态,不应该使用任何资源,如果应用程序管理器调用pauseApp()时产生异常情形,MIDlet就应该立刻进入消灭状态。

同样的情形也发生在destroyApp(),通常此方法被调用的时候,代表MIDlet要被关闭了,所以程序员应该在这里释放自己所分配的资源。只要MIDlet进入了消灭状态,就无法再回头。如果是系统自己调用destroyApp(),那么在destroyApp()执行时万一发生异常,这些异常将被忽略,MIDlet一样会被关闭。根据规范,我们不能在MIDlet之中直接调用System.exit()Runtime.exit()来结束程序达到执行,如果这样做的话,会引发java.lang.SecurityException异常。

3 MIDlet自己管理自己的生命周期

除了由应用程序管理器来控制MIDlet的生命周期之外,MIDlet本身也可以软性地决定自己的状态,但不是自己改变自己的状态,而是MIDlet先调用上述相对应的状态改变函数,这些函数会发出信息通知应用程序管理器,请它来帮我们改变MIDlet的状态,但是决定权在于应用程序管理器,不保证一定可行。

假设今天是MIDlet主动要将MIDlet的状态由运作状态变成停止状态,那么我们直接调用pauseApp()函数,只会执行pauseApp()之中的程序代码而已,无法改变MIDlet的状态, MIDlet必须调用notifyPaused()以通知应用程序管理器,应用程序管理器收到通知之后,才会判断是否要让MIDlet进入停止状态。

MIDlet调用notifyPaused(),与应用程序管理器主动要求停止,两者是有所差别的,主要在于应用程序管理器主动要求停止时,pauseApp()会被调用;由MIDlet调用notifyPaused()pauseApp()不会被调用。但是两者都会让MIDlet进入停止状态,所以在MIDlet自己动手调用notifyPaused()之前,最好自己也先调用pauseApp()比较合适。

同样的情况也发生在notifyDestroyed()destroyApp()。除非是系统强制关闭MIDlet,否则最好MIDlet先调用destroyApp(),然后再调用notifyDestroyed(),请应用程序管理器帮我们将MIDlet转换到消灭状态,最后结束MIDlet的运作。单单MIDlet自己调用destroyApp()是没有用的。

destroyApp()有个布尔值作为参数,根据MIDP的规范,如果传入true,那么MIDlet不管如何应该无条件释放所有资源,然后让应用程序管理器结束MIDlet的运作,这属于系统或硬件强制关闭MIDlet的情形。如果用户调用notifyDestroyed()来结束MIDlet,那么在调用destroyApp()时,最好传入false,代表这并非系统或硬件强制关闭,这时如果MIDlet不希望结束执行,它可以通过抛出MIDletStateChangeException异常告知调用它的人:“我还不想被消灭”,请待会儿再试试。

从这里我们可以看出startApp()pauseApp()以及destroyApp()并非控制MIDlet生命周期的函数,它们只是一个提供我们初始化资源、释放资源的地方而已。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值