MIDP本身就有许多线程的实例,比如用户界面中的事件监听器。在MIDP应用设计中,如果遇到涉及复杂计算,或者需要较长执行时间的操作,都可以使用线程。比如设计动画,可能需要多个线程同时对Canvas对象进行操作。另一个典型应用就是网络程序设计中,比如应用程序连接网络、获取数据可能需要较长时间,这时可利用多线程技术允许用户同时进行其他操作。或者设计一个动画屏幕,提示正在进行连网操作,这是很重要的,否则用户无从知道应用程序的工作状态,这是应用程序可用性的一个重要方面。线程还可以用做后台工作,比如设置一个线程监听某种状态(可以是来自网络上的股票信息,或者邮件信息等),当触发某种条件时,就以Alert的方式警示用户。
这里使用一个动画的例子,例子的原型就是第7章“高级用户界面”中7.8.9小节最后一个例子,不过这里使用低级用户界面API实现。( 作者注:本例子是《J2ME无线通信应用技术开发》(北京希望电子出版社2002年8月版)中的一个例子)
AnimatorCanvas类实现了一个用户自定义的Canvas对象,同时又实现了Runnable接口,Image数组保存了动画中不同的帧。它的paint方法首先刷新屏幕,然后在屏幕指定位置绘制当前的帧,并把当前帧设置为Iamge数组中下一元素,达到最后一个元素时就设置为第一个元素。
Image数组通过构造函数从外部获取,interval参数指定动画帧之间的时间间隔。它的run方法就是循环地调用repaint方法,由于每次重画都是前一次绘制时的下一帧,因此实现了动画效果。代码如下:
[pre]
//程序名AnimatorCanvas.java , 项目Animator
//用线程实现动画
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class AnimatorCanvas extends Canvas implements Runnable {
Image[] frames;
int left, top;
boolean alive;
int interval;
int currentFrame;
int numFrames;
MIDlet midlet;
Thread thread;
public AnimatorCanvas(MIDlet midlet, Image[] frames, int left, int top, int interval) {
this.frames = frames;
this.left = left;
this.top = top;
this.interval = interval;
this.alive = true;
this.currentFrame = 0;
this.numFrames = frames.length;
this.midlet = midlet;
thread= new Thread(this);
thread.start();
}
public AnimatorCanvas(MIDlet midlet, Image[] frames, int interval) {
this(midlet, frames, 0, 0, interval );
this.left = ( this.getWidth() - frames[0].getWidth() ) / 2;
this.top = ( this.getHeight() - frames[0].getHeight() ) / 2;
}
public void paint(Graphics g) {
g.setColor(0x00000000);
g.fillRect(0,0,getWidth(),getHeight());
g.drawImage(frames[currentFrame++], left, top, Graphics.LEFT | Graphics.TOP);
if (currentFrame >= numFrames) {
currentFrame = 0;
}
}
public void keyPressed(int keyCode) {
if (getGameAction(keyCode) == FIRE) {
alive = !alive;
} else {
thread = null;
((Animator)midlet).exit();
}
}
public void run() {
while(true) {
if (alive) {
repaint();
try {
Thread.sleep(interval);
} catch (InterruptedException e) {
}
}
}
}
}
[/pre]
在run方法的while循环中,使用alive参数对它的行为进行控制。在MIDlet主类中生成这个Canvas类的实例,并设置为当前屏幕即可,比如下面的代码:
[pre]
//程序名Animator.java , 项目Animator
//用线程实现动画
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class Animator extends MIDlet {
protected void startApp() {
try {
Image[] images = new Image[] {
Image.createImage("/Duke2.png"),
Image.createImage("/Duke1.png"),
Image.createImage("/Duke3.png"),
Image.createImage("/Duke1.png")
};
Display.getDisplay(this).setCurrent(new AnimatorCanvas(this,images,100));
} catch (java.io.IOException e) {
}
}
public void exit(){
destroyApp(true);
notifyDestroyed();
}
protected void destroyApp( boolean unconditional ){}
protected void pauseApp() {}
}
[/pre]
callSerially方法
前面的例子使用一个while循环语句,反复调用repaint()方法,容易造成的问题就是可能与其他线程发生冲突。Display类提供了一个事件序列化的方法callSerially,使Runnable对象在一个重画周期完整后立即调用它的run()方法,利用这种特性取代run方法中的循环语句,把上一小节AnimatorCanvas类的run方法更改如下:
[pre]
//程序名AnimatorCanvas.java , 项目Animator
public void run() {
//while(true) {
if (alive) {
repaint();
try {
Thread.sleep(interval);
} catch (InterruptedException e) {
}
}
Display.getDisplay(midlet).callSerially(this); //增加的代码
//}
}
[/pre]
然后重新编译并运行这个程序,其效果与11.1.2小节中例子类似。实际上这也是一个循环,相当于在run方法中调用了run方法(本身)。这里把callSerially调用放在if语句之外,这样使用按键事件停止动画时,线程仍在运行(循环调用run方法)。如果放在if语句之内,则当从if语句跳出(alive == false)时,循环也会结束,线程也随之结束(重新设置alive == true时也不会启动线程)。因此使用run方法时应该注意,避免造成意外情况。
这里使用一个动画的例子,例子的原型就是第7章“高级用户界面”中7.8.9小节最后一个例子,不过这里使用低级用户界面API实现。( 作者注:本例子是《J2ME无线通信应用技术开发》(北京希望电子出版社2002年8月版)中的一个例子)
AnimatorCanvas类实现了一个用户自定义的Canvas对象,同时又实现了Runnable接口,Image数组保存了动画中不同的帧。它的paint方法首先刷新屏幕,然后在屏幕指定位置绘制当前的帧,并把当前帧设置为Iamge数组中下一元素,达到最后一个元素时就设置为第一个元素。
Image数组通过构造函数从外部获取,interval参数指定动画帧之间的时间间隔。它的run方法就是循环地调用repaint方法,由于每次重画都是前一次绘制时的下一帧,因此实现了动画效果。代码如下:
[pre]
//程序名AnimatorCanvas.java , 项目Animator
//用线程实现动画
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class AnimatorCanvas extends Canvas implements Runnable {
Image[] frames;
int left, top;
boolean alive;
int interval;
int currentFrame;
int numFrames;
MIDlet midlet;
Thread thread;
public AnimatorCanvas(MIDlet midlet, Image[] frames, int left, int top, int interval) {
this.frames = frames;
this.left = left;
this.top = top;
this.interval = interval;
this.alive = true;
this.currentFrame = 0;
this.numFrames = frames.length;
this.midlet = midlet;
thread= new Thread(this);
thread.start();
}
public AnimatorCanvas(MIDlet midlet, Image[] frames, int interval) {
this(midlet, frames, 0, 0, interval );
this.left = ( this.getWidth() - frames[0].getWidth() ) / 2;
this.top = ( this.getHeight() - frames[0].getHeight() ) / 2;
}
public void paint(Graphics g) {
g.setColor(0x00000000);
g.fillRect(0,0,getWidth(),getHeight());
g.drawImage(frames[currentFrame++], left, top, Graphics.LEFT | Graphics.TOP);
if (currentFrame >= numFrames) {
currentFrame = 0;
}
}
public void keyPressed(int keyCode) {
if (getGameAction(keyCode) == FIRE) {
alive = !alive;
} else {
thread = null;
((Animator)midlet).exit();
}
}
public void run() {
while(true) {
if (alive) {
repaint();
try {
Thread.sleep(interval);
} catch (InterruptedException e) {
}
}
}
}
}
[/pre]
在run方法的while循环中,使用alive参数对它的行为进行控制。在MIDlet主类中生成这个Canvas类的实例,并设置为当前屏幕即可,比如下面的代码:
[pre]
//程序名Animator.java , 项目Animator
//用线程实现动画
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class Animator extends MIDlet {
protected void startApp() {
try {
Image[] images = new Image[] {
Image.createImage("/Duke2.png"),
Image.createImage("/Duke1.png"),
Image.createImage("/Duke3.png"),
Image.createImage("/Duke1.png")
};
Display.getDisplay(this).setCurrent(new AnimatorCanvas(this,images,100));
} catch (java.io.IOException e) {
}
}
public void exit(){
destroyApp(true);
notifyDestroyed();
}
protected void destroyApp( boolean unconditional ){}
protected void pauseApp() {}
}
[/pre]
callSerially方法
前面的例子使用一个while循环语句,反复调用repaint()方法,容易造成的问题就是可能与其他线程发生冲突。Display类提供了一个事件序列化的方法callSerially,使Runnable对象在一个重画周期完整后立即调用它的run()方法,利用这种特性取代run方法中的循环语句,把上一小节AnimatorCanvas类的run方法更改如下:
[pre]
//程序名AnimatorCanvas.java , 项目Animator
public void run() {
//while(true) {
if (alive) {
repaint();
try {
Thread.sleep(interval);
} catch (InterruptedException e) {
}
}
Display.getDisplay(midlet).callSerially(this); //增加的代码
//}
}
[/pre]
然后重新编译并运行这个程序,其效果与11.1.2小节中例子类似。实际上这也是一个循环,相当于在run方法中调用了run方法(本身)。这里把callSerially调用放在if语句之外,这样使用按键事件停止动画时,线程仍在运行(循环调用run方法)。如果放在if语句之内,则当从if语句跳出(alive == false)时,循环也会结束,线程也随之结束(重新设置alive == true时也不会启动线程)。因此使用run方法时应该注意,避免造成意外情况。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/10294527/viewspace-127032/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/10294527/viewspace-127032/