MIDlet工作原理

时间:2021-01-05 07:20:23

题记 :  现在的J2ME用户已经是日益减少 , 开发也在转型! 无奈之下也不得不写下这系列文章来别了j2me ,也是对过去的一些总结吧!

①: 所有Kjava必须会继承自javax.microedition.midlet.MIDlet类的。其中定义了三个抽象方法(abstract),因此我们撰写的MIDlet必须实现它们。这三个抽象方法是:

1.     startApp()   转到运作状态

2.     pauseApp()   转到停止状态

3.     destroyApp() 转到消灭状态

 应用程序管理器通过这三个抽象方法来控制MIDlet的生命周期。因此,所有的MIDlet都必须实现这三个方法 !

②:两种控制应用程序的生命周期的方式

   1.  由应用程序管理器来控制(硬控制)

MIDlet工作原理

生命周期

如上图可知midlet在进入某个状态之前都会调用相应的函数然后进入该状态。 这都是应用程序管理器调用的,但是设计者考虑到了如果程序员自己调用了这里面的情况。

 讨论一: 如果程序员自己调用了这里面的情况的话应用程序管理器会做如何的改变呢?

官方文档如此回答:  通常不会发生错误,但是也不会造成状态的转换,只是当成一个单纯的函数调用而已

       在此做了一个小的案例来说明一下 :

情况一 :

  1. public class MidTest extends MIDlet {
  2. public  MidTest() {
  3. System.out.println("MidTest()");
  4. }
  5. protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
  6. System.out.println("destroyApp()");
  7. }
  8. protected void pauseApp() {
  9. System.out.println("pauseApp()");
  10. }
  11. protected void startApp() throws MIDletStateChangeException {
  12. System.out.println("startApp()");
  13. destroyApp(true);
  14. }
  15. }

当我们程序员自己在startApp() 里面调用destroyApp(true);(这个函数的参数后面会讲到) 时,它的结果如下:

MidTest()

    startApp()

    destroyApp()

但是程序并没有退出! 说明了一个情况就是 : 自己调用的时候是不会进行状态的切换的

情况二:当我们关闭程序的时候得到的结果是: destroyApp() 说明了当关闭程序的时候应用程序管理器会调用此方法的!

讨论二 :如何合理利用startApp()?pauseApp()  ? destroyApp(boolean arg0) ?

 从上图的生命周期可以看到startApp()这个函数是不止一次被调用的(第一次new) 当恢复了pause后也会进入这种状态。所以做为开发人员的我们那些没有必要重新初始化的操作(特别是加载一些资源的时候)不要放到这里面,以免浪费时间! 最好的做法就是:放在构造函数做初始化动作

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

 当MIDlet进入停止状态时,不应该使用任何资源。如果应用程序管理器调用pauseApp()时产生异常,MIDlet就应该立刻进入消灭状态。同样的情况也发生在destroyApp(),通常调用此方法时,表示MIDlet要被关闭了。所以,应该在这里释放MIDlet所分配的资源。只要MIDlet进入消灭状态,就无法再回头。如果时系统自己调用destroyApp(),那么在其执行时万一发生异常,这些异常将被忽略,MIDlet一样会被关闭。

2. 由程序员来控制(软控制) :由程序员来决定是否退出程序

   MIDlet工作原理

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

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

 实例如下: 

  1. public class MidTest extends MIDlet {
  2. public  MidTest() {
  3. System.out.println("MidTest()");
  4. }
  5. protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
  6. System.out.println("destroyApp()");
  7. }
  8. protected void pauseApp() {
  9. System.out.println("pauseApp()");
  10. }
  11. protected void startApp() throws MIDletStateChangeException {
  12. System.out.println("startApp()");
  13. notifyPaused() ;
  14. }

结果是 :

   MIDlet工作原理

  MidTest()

  startApp()

  表明了程序员自己调用了notifyPaused() 其实并没有调用pauseApp()  所以这里最好的写法是在notifyPaused() 之前调用pauseApp()  ;

同样的情况也发生在notifyDestroyed()与destroyApp()。除非时系统强制关闭MIDlet,否则最好MIDlet先调用destroyApp(),然后再调用notifyDestroyed(),请应用程序管理器帮我们将MIDlet转换到消灭状态,最后结束MIDlet的运作。destoryApp()有个布尔值作为参数,根据MIDP规范,如果传入true,那么MIDlet不管如何应该强制无条件释放所有资源,然后让应用程序管理器结束MIDlet的运作,这属于系统或硬件强制关闭MIDlet的情形。如果用户调用notifyDestoryed()来结束MIDlet,那么在调用destroyApp()时,最好传入false,代表这并非系统或硬件强制关闭,这时如果MIDlet不希望结束执行,可以通过抛出MIDletStateChangeException异常告知调用者:我还不想被消灭,稍后再试。

 ③:总结: 

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

 明天继续这个系列的系统原理,谢谢大家! 

补充: 今天看到论坛上有朋友提出了这样一个问题 : 两个midlet的切换!

我们知道应用程序初始化后就会在建立一个对应的虚拟机,并创建一个对应的虚拟机实例! 如果向如下来创建的话:

  1. public class Test extends MIDlet {
  2. static Display display ;
  3. public Test()
  4. {
  5. display = Display.getDisplay(this) ;
  6. System.out.println("Test()");
  7. }
  8. protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
  9. System.out.println("destroyApp ");
  10. }
  11. protected void pauseApp() {
  12. }
  13. protected void startApp() throws MIDletStateChangeException {
  14. System.out.println("startApp ");
  15. new Test2() ;
  16. }
  17. }

那么在new Test2() 这一步的时候其实原来的虚拟机实例是没有消失的。这时就是试图在一个虚拟机里创建两个实例那是不允许的! 系统就会抛出:

java.lang.SecurityException: Application not authorized to access the restricted API(访问受限的API)

那么另一个问题来了。你可能会说那么我们先将Test 使用notifyDestroyed() ;进行销毁。然后再次创建就是一个实例了

其实不然,正如我上面讲解软控制的时候提到的。notifyDestroyed()  这个东西只是提醒应用程序管理器,我要死了。把资源都释放了吧。但是应用程序管理器会去检测一下还有没有存活的东西,然后才回去摧毁。如果你这样来调用的话!

  1. protected void startApp() throws MIDletStateChangeException {
  2. System.out.println("startApp ");
  3. notifyDestroyed() ;
  4. new Test2() ;
  5. }

那么当Test()的应用程序管理器检查到了notifyDestroyed() 正准备释放资源了。才发现new Test2() 这个东西并不是我的,但是却要我去摧毁,那么肯定也会抛出上面的异常了!