public MalConnectionMidlet(){ display = Display.getDisplay(this); text = new TextBox("Message","请使用‘问候’命令发送消息",100,TextField.ANY); showCommand = new Command("问候",Command.SCREEN,1); text.addCommand(showCommand); text.setCommandListener(this); }
public void startApp() { display.setCurrent(text); }
以下是引用片段: Warning: To avoid potential deadlock, operations that may block, such as networking, should be performed in a different thread than the commandAction() handler.
这就是因为没有使用多线程造成的。下面,就来看看如何使用多线程来解决此问题。 新建类NetworkThread,它继承在Thread,并将原先commandAction()中发送,接受消息的操作移到此类中完成。代码如下: 以下是引用片段: /* * NetworkThread.java * * Created on 2006年7月20日, 下午4:16 * */ package nju.hysteria.thread.connection; import java.io.IOException; import javax.microedition.lcdui.TextBox; /** * * @author Magic */ public class NetworkThread extends Thread { private NetworkConnection connection; private TextBox text;
public NetworkThread(TextBox text) { super(); this.text = text; } public void run() { String message = null;
public ConnectionMidlet(){ display = Display.getDisplay(this); text = new TextBox("Message","请使用‘问候’命令发送消息",100,TextField.ANY); showCommand = new Command("问候",Command.SCREEN,1); text.addCommand(showCommand); text.setCommandListener(this); }
public void startApp() { display.setCurrent(text); }
public void pauseApp() { }
public void destroyApp(boolean unconditional) { } public void commandAction(Command command, Displayable displayable) { if(command==showCommand){ /** * 创建新的线程完成联网操作 */ (new NetworkThread(text)).start(); } } }
MalCameraMidlet和CameraMidlet分别是错误和正确的MIDlet,它们都创建一个CameraView(用于显示摄像头画面)的对象,唯一的不同在于前者创建MalCamera的对象,后者创建Camera的对象(MalCamera和Camera都是CameraView的子类)。SnapShot则是将摄像头捕捉到的图像显示给用户的类。 首先,我们还是来看一下未使用多线程,而造成程序没有响应的情况。如下是MalCameraMidlet类的代码,其中保存着一个指向CameraView的引用,并在startApp()中创建了MalCamera对象。 以下是引用片段: /** * MalCameraMidlet.java * */ package nju.hysteria.thread.camera; import javax.microedition.lcdui.Display; import javax.microedition.midlet.MIDlet; /** * This MIDlet create the mal camera view, so the program * will block after calling Capture command. * @author Magic * */ public class MalCameraMidlet extends MIDlet { protected Display display; private CameraView camera;
public MalCameraMidlet() { super(); display = Display.getDisplay(this); } protected void startApp() { camera = new MalCamera(this); display.setCurrent(camera); }
/** * Show current camera. */ public void showCamera(){ display.setCurrent(camera); }
import javax.microedition.lcdui.Command; import javax.microedition.lcdui.Displayable; import javax.microedition.media.MediaException; import javax.microedition.midlet.MIDlet; /** * This class display the mal camera. In commandAction(), * for capture command, just get the data without create a * new thread, and thus cause program blocked. * @author Magic * */ public class MalCamera extends CameraView {
public MalCamera(MIDlet midlet){ super(midlet); }
public void commandAction(Command cmd, Displayable displayable) { if(cmd==exitCommand){ try { player.stop(); } catch (MediaException e) { e.printStackTrace(); } player.close(); ((MalCameraMidlet)midlet).destroyApp(false); midlet.notifyDestroyed(); }else if(cmd==captureCommand){ // Do not handle in a new thread. try { byte[] data = vc.getSnapshot(null); new SnapShot(midlet,data); } catch (MediaException e) { e.printStackTrace(); } } } }
查看控制台窗口,得到如下提示: 以下是引用片段: Warning: To avoid potential deadlock, operations that may block, such as networking, should be performed in a different thread than the commandAction() handler.
同样,还是由于没有创建新的线程进行处理的原因。下面,我们就把处理的代码放到新的线程中来完成,看看情况如何。如下是修改过的MalCamera代码,Camera.java: 以下是引用片段: /** * Camera.java */ package nju.hysteria.thread.camera; import javax.microedition.lcdui.Command; import javax.microedition.lcdui.Displayable; import javax.microedition.media.MediaException; import javax.microedition.midlet.MIDlet; /** * This class displays camera. And do the right thing * for capture command, putting code in a new thread. * @author Magic * */ public class Camera extends CameraView { public Camera(MIDlet midlet){ super(midlet); }
public void commandAction(Command cmd, Displayable displayable) { if(cmd==exitCommand){ try { player.stop(); } catch (MediaException e) { e.printStackTrace(); } player.close(); ((CameraMidlet)midlet).destroyApp(false); midlet.notifyDestroyed(); }else if(cmd==captureCommand){ // Handle in a new thread. new Thread(){ public void run(){ try { byte[] data = vc.getSnapshot(null); new SnapShot(midlet,data); } catch (MediaException e) { e.printStackTrace(); } } }.start(); } } }
public CameraMidlet() { super(); display = Display.getDisplay(this); } protected void startApp() { camera = new Camera(this); display.setCurrent(camera); } /** * Show current camera. */ public void showCamera(){ display.setCurrent(camera); }
联网和拍照这两种情况都需要程序员创建新的线程来完成任务,并且这种做法对于程序员来说是显式的,即通过直接使用Thread类或Runnable接口来直接创建新线程。在MIDP的API中同样提供了隐式的方式来创建新线程,以方便程序员的编程。这就是TimerTask类,它实现了Runnable接口,用户只需创建一个继承它的类,并且实现run()方法,以此来创建新线程,而无需显示的继承Thread或Runnable。 当然,TimerTask的优点不仅于此,从它的名字来看,可以认为它是一个在特定时间执行的任务。run()方法中代码就是这任务,那么怎么控制其在特定时间执行呢?这就需要Timer这个类的帮助了。顾名思义,Timer是一个定时器,通过调用它的多个schedule(...)方法中的一个,可以控制在特定的时间,或每隔一定时间执行TimerTask。具体的方法介绍请看JDK文档。 TimerTask和Timer经常一起使用,比如在显示时间,倒计时和显示欢迎界面时会经常用到。下面,就通过一个实例来介绍这两个的用法。 这是一个计时的程序,程序从0秒开始,用户可以随时暂停或继续计时。程序是通过Timer和TimerTask来完成的,包含三个类:ClockMidlet,ClockCanvas和Clock。首先来看一下本程序最主要的类ClockCanvas: 以下是引用片段: /** * ClockCanvas.java */ package nju.hysteria.thread.clock; import java.util.Timer; import javax.microedition.lcdui.Canvas; import javax.microedition.lcdui.Command; import javax.microedition.lcdui.CommandListener; import javax.microedition.lcdui.Displayable; import javax.microedition.lcdui.Font; import javax.microedition.lcdui.Graphics; /** * This class display time to user, and also start the timer * to update time each second. * @author Magic * */ public class ClockCanvas extends Canvas implements CommandListener { private ClockMidlet midlet; private Command exitCommand; private Command stopCommand; private Command resumeCommand; private long second; private int x; private int y;
// Timer private Timer timer;
public ClockCanvas(ClockMidlet midlet){ this.midlet = midlet; exitCommand = new Command("退出",Command.EXIT,0); stopCommand = new Command("停止",Command.SCREEN,1); resumeCommand = new Command("继续",Command.SCREEN,1);