理解MIDP中的UI事件处理

首先,请看如下的一段代码:

public void commandAction(Command command, Displayable displayable) {                    
if (displayable == helloForm) {
if (command == testCommand) {
try {
Connector.open("http://forum.nokia.com");
} catch(Exception e) {}
}
}
}

你可能已经看出了其中的问题,或者你可以尝试运行一下这段代码。如果你是在WTK上执行它们,你将会得到如下的警告:

警告: 若要避免潜在的死锁,应该在 commandAction() 处理程序之外的其他线程中执行
可能会阻塞的操作(如网络连接)。

要弄清楚这个警告的由来,就需要先理解MIDP的UI事件处理机制。MIDP的Java Doc中,可以从javax.microedition.lcdui包的Overview里找到一段标题为“Event Handling”的内容,这段内容很有帮助,以下是对其中部分内容的翻译和理解。

用户交互会产生事件,虚拟机通过回调相应的方法将事件通知到应用程序。UI中的回调方法有以下几种:

  1. High-Level API中的抽象命令;
  2. Low-Level API中的按键和触屏事件(keyPressed、keyReleased等);
  3. Canvas中的paint()方法;
  4. 通过Canvas的callSerially()方法请求执行的Runnable接口;

所有的UI回调方法都是顺序执行的,它们永远都不会并行。虚拟机永远不会在一个回调方法返回前调用其他回调方法。这个特性可以很好的保证旧的事件在新事件被传递之前完成。如果多个回调都在等待执行,则后一个回调会在前一个回调返回后尽可能快的被执行。

前面的警告的原因已经很清楚了,如果commandAction方法因被网络连接操作阻塞而无法返回,则其他回调方法都会因此阻塞。

实际开发中,回调方法应被保证尽可能快的返回,不在其中执行可能阻塞或需要大量运行时间的操作,这样才能保证事件处理的流畅度。

如下的方法都是UI中的回调方法:

  • Canvas.hideNotify
  • Canvas.keyPressed
  • Canvas.keyRepeated
  • Canvas.keyReleased
  • Canvas.paint
  • Canvas.pointerDragged
  • Canvas.pointerPressed
  • Canvas.pointerReleased
  • Canvas.showNotify
  • Canvas.sizeChanged
  • CommandListener.commandAction
  • CustomItem.getMinContentHeight
  • CustomItem.getMinContentWidth
  • CustomItem.getPrefContentHeight
  • CustomItem.getPrefContentWidth
  • CustomItem.hideNotify
  • CustomItem.keyPressed
  • CustomItem.keyRepeated
  • CustomItem.keyReleased
  • CustomItem.paint
  • CustomItem.pointerDragged
  • CustomItem.pointerPressed
  • CustomItem.pointerReleased
  • CustomItem.showNotify
  • CustomItem.sizeChanged
  • CustomItem.traverse
  • CustomItem.traverseOut
  • Displayable.sizeChanged
  • ItemCommandListener.commandAction
  • ItemStateListener.itemStateChanged
  • Runnable.run resulting from a call to Display.callSerially