分享
 
 
 

j2me进度条与线程化模型

王朝java/jsp·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

j2me进度条与线程化模型

作者:FavoYang Email:favoyang@yahoo.com 欢迎交流

Keyworld:线程化模型 j2me UI设计

内容提要:

本文研究如何建立一个方便使用的线程化模型,这个线程化模型由前台的进度条UI 和后台的背景线程组成。

版权声明:

本文同时发表在www.j2medev.com和我的Blog(http://blog.csdn.net/alikeboy)上,如果需要转载,有三个途径:1)联系我并经我同意;2)和www.j2medev.com有转载文章合作协议的 3)通过rss聚合我的Blog。另外转载需要全文转发(包括文章的头部),不要断章取义。

正文:

解决的问题

在j2me的UI体系中,UI操作是在一个独立的线程中运行的。往往在api doc中要求程序员对接口方法立即返回。也就是说非阻塞的。你必须开启一个独立的线程来完成你自定义的复杂的工作,比如联网等可能发生阻塞的io操作。新的线程如果不和用户交流,告诉用户线程正在工作的话,将会显现的非常不友好。用户可能执行别的操作而扰乱程序的正常运行。一个简单的方法是提供一个进度条,这样用户就会愿意等待上一会,直到程序运行出结果。为了将程序员从前台进度条与后台线程的通信中解脱出来,专心于后台线程的开发,有必要设计一个进度条线程模型。

应该注意到进度条有多种的形式:

A, 动画形式进度条,仅表示程序正在运行(自维护的)

B, 可交互增量形式的进度条,后台线程通过调用进度条的相应方法在程序运行中不断的改变进度条的状态

C, 进度条的表现形式应该灵活,不要固定其实现

D, 进度条对象要重复利用

进度调和后台线程的交流也有好几种情况:

A, 仅仅将进度条绘画在屏幕上,并等后台任务完成后,由后台线程跳转到成功画面。

B, 对于可取消的任务,用户可以通过点击进度条的按钮来试图cancel任务,后台任务应该尽快取消,并跳转到失败的画面

C, 对于不可跳转的任务,用户只有耐心等待

D, 如果背景线程运行失败,应自行跳转到失败的屏幕

进度条的设计(前台)

为了实现进度条的表现的多样性,首先抽象一个接口:

PRogressObserver.java

package com.favo.ui;

import javax.microedition.lcdui.Display;

/**

* @author Favo

*

* 这是仿照Smart Ticket制作的进度条观察者,这个模型的优点是

* 1,低耦合度。你可以通过Form,Canvas等来实现这个接口

* 2,支持可中断的任务,因为背景线程是无法强制性中断的,

* 所以就 没有了在观察者中回调背景线程相应方法的必要,

* 如果支持可中断的话,可以让背景线程来查询观察者的isStopped()

* 3,可以说进度条仅仅将自己绘画在屏幕上,他对后台线程毫不关心

*/

public interface ProgressObserver {

/**

* 将进度条复位

*/

public void reset();

/**

* 将进度条设置最大

*/

public void setMax();

/*

* 将自己绘制在屏幕上,如果进度条要开启自身的线程用于自动更新画面,

* 也在这里构造并开启绘画线程(常用于动画滚动条)

*/

public void show(Display display);

/**

* 滚动条退出命令,如果进度条曾经开启自身的线程用于自动更新画面,

* (常用于动画滚动条),在这里关闭动画线程

*/

public void exit();

/**

* 更新进度条

*/

public void updateProgress(Object param1);

public boolean isStoppable();

public void setStoppable(boolean stoppable);

public boolean isStopped();

public void setStopped(boolean stopped);

public void setTitle(String title);

public void setPrompt(String prompt);

}

每个方法都很一幕了然,我解释两点:

1)“2,支持可中断的任务,因为背景线程是无法强制性中断的, 所以就 没有了在观察者中回调背景线程相应方法的必要, 如果支持可中断的话,可以让背景线程来查询观察者的isStopped()”

如果要支持可中断线程的话,想当然的,我们希望用户按下按钮后回调后台线程的某个方法来停止线程,并且这个方法要立即返回(前面提过UI的用户响应不能够阻塞)。但是细细想想,线程是无法被强制停止的,而且即使能够被强制停止也很不安全。所以这个方法也只能够是通过设置某个flag,然后立即返回。这样的话线程就和前台的UI紧密的耦合在一起了。与其这样,倒不如让后台线程去查询UI的状态。这样UI并不关心到底是谁在后台维护他状态。

2)如果要实现一个不交互动画UI,那么显然这个UI是自维护的(也就是说UI单独有自己的绘画线程)。为了能够实现这种情况,可以在show中开启线程,在exit中结束线程。对于交互UI,可以简单的忽略exit方法。

下面给一个利用Form和Gauge实现的交互式UI(非自维护的),读者可以看看其中的细节,参照他可以设计自己的用Canvas实现的,或者自维护的等等不同的实现。

ProgressGaugeUI.java

package com.favo.ui;

import javax.microedition.lcdui.Command;

import javax.microedition.lcdui.CommandListener;

import javax.microedition.lcdui.Display;

import javax.microedition.lcdui.Displayable;

import javax.microedition.lcdui.Form;

import javax.microedition.lcdui.Gauge;

/**

* @author Favo

* Preferences - Java - Code Style - Code Templates

*/

public class ProgressGaugeUI implements ProgressObserver, CommandListener {

private static final int GAUGE_MAX = 8;

private static final int GAUGE_LEVELS = 4;

private static ProgressGaugeUI pgUI;

private Form f;

private Gauge gauge;

private Command stopCMD;

boolean stopped;

boolean stoppable;

int current;

protected ProgressGaugeUI() {

f = new Form("");

gauge = new Gauge("", false, GAUGE_MAX, 0);

stopCMD = new Command("Cancel", Command.STOP, 10);

f.append(gauge);

f.setCommandListener(this);

}

public static ProgressGaugeUI getInstance() {

if (pgUI == null) {

return new ProgressGaugeUI();

}

return pgUI;

}

public void reset() {

current=0;

gauge.setValue(0);

stopped=false;

setStoppable(false);

setTitle("");

setPrompt("");

}

public void updateProgress(Object param1) {//这里的参数设计为提示语

current=(current+1)%GAUGE_LEVELS;

gauge.setValue(current * GAUGE_MAX/GAUGE_LEVELS);

if(param1!=null && param1 instanceof String){

setPrompt((String)param1);

}

}

public boolean isStoppable() {

return stoppable;

}

public void setStoppable(boolean stoppable) {

this.stoppable = stoppable;

if(stoppable){

f.addCommand(stopCMD);

}else{

f.removeCommand(stopCMD);

}

}

public boolean isStopped() {

return stopped;

}

public void setStopped(boolean stopped) {

this.stopped=stopped;

}

public void setTitle(String title) {

f.setTitle(title);

}

public void setPrompt(String prompt) {

gauge.setLabel(prompt);

}

public void commandAction(Command arg0, Displayable arg1) {

if(arg0==stopCMD){

if(isStoppable())

stopped=true;

else{

setPrompt("can't stop!");

}

}

}

public void show(Display display) {

display.setCurrent(f);

}

public void exit() {

// 忽略

}

public void setMax() {

gauge.setValue(GAUGE_MAX);

}

}

后台线程的设计

后台线程替我们作以下的内容:

1)执行我们的任务runTask()

2)如果用户中断线程,那么runTask()运行完后,将会跳转到我们指定的失败屏幕

3)在最后替我们调用UI.exit()

我们需要做的:

1)提供一个前台的UI,提供失败后跳转的画面,提供Display的实例

2)在runTask()中,如果任务完成,手工跳转失败画面

3)在runTask()中,如果任务失败,手工跳转失败画面

4)在runTask()中改变进度栏的状态。

5)在runTask()中查询用户是否取消,如果用户取消,应该尽快退出runTask()

这种模型职责清晰,便于使用。但也有一个缺点:如果用户取消了任务,但是此时任务接近完成,或者已经完成。后台线程依然会显示用户取消了任务,并将会跳转到我们指定的失败屏幕。这时候会产生不一致的情况。为了解决整个问题,程序员可以在runTask()中调用taskComplete()来强制完成任务。这样即使用户取消了任务,依然回显示任务成功。当然你也可以不掉用taskComplete()遵循默认的行为特点。

BackgroundTask.java

package com.favo.ui;

import javax.microedition.lcdui.AlertType;

import javax.microedition.lcdui.Displayable;

import javax.microedition.lcdui.Display;

import javax.microedition.lcdui.Alert;

/**

* @author Favo

* Preferences - Java - Code Style - Code Templates

*/

public abstract class BackgroundTask extends Thread {

ProgressObserver poUI;

protected Displayable preScreen;

protected boolean needAlert;

protected Alert alertScreen;

private Display display;

public BackgroundTask(ProgressObserver poUI, Displayable pre,

Display display) {

this.poUI = poUI;

this.preScreen = pre;

this.display = display;

this.needAlert = false;

}

public void run() {

try {

runTask();

} catch (Exception e) {

Alert al = new Alert("undefine exception",

e.getMessage(), null,

AlertType.ALARM);

al.setTimeout(Alert.FOREVER);

display.setCurrent(al);

} finally {

if (poUI.isStoppable()) {

if (poUI.isStopped()) {//如果用户中断了程序

if (needAlert) {

display.setCurrent(alertScreen, preScreen);

} else {

display.setCurrent(preScreen);

}

}

}

poUI.exit();

}

}

/*

* 如果任务可中断,查看pgUI.isStopped().并尽快退出此方法;

* 如果任务需要更新进度栏,调用pgUI.updateProgress(“进度提示”).

* 习惯上此方法的最后手动调用taskComplete()以防止用户在任务接近

* 完成时取消

*/

public abstract void runTask();

/**

* 这是一个偷懒的办法,当你构造好BackgroundTask对象后,直接调用这个方法, *可以帮助你初始化进度UI,并显示出来。之后启动你的任务线程

*/

public static void runWithProgressGauge(BackgroundTask BTask, String title,

String prompt, boolean stoppable, Display display) {

ProgressObserver po = btask.getProgressObserver();

po.reset();

po.setStoppable(stoppable);

po.setTitle(title);

po.setPrompt(prompt);

po.show(display);

btask.start();

}

public ProgressObserver getProgressObserver() {

return poUI;

}

public void taskComplete(){

getProgressObserver().setStopped(false);

}

}

如何使用

1)产生一个ProgressObserver 对象poUI

如果用默认的,通过调用ProgressGaugeUI.getInstance();

2)构造BackgroundTask对象bkTask,一般可以用匿名类来实现。

3)初始化poUI-->设置后字段-->显示你的poUI-->开启bkTask线程。

第三步可以用一步完成,通过调用静态方法

BackgroundTask.runWithProgressGauge(bkTask, "标题","提示", 是否可以暂停, display);

下面一个例子,看看你是否理解了,并且会使用了。

TestProgressGauge.java

package com.favo.ui;

import javax.microedition.lcdui.Alert;

import javax.microedition.lcdui.AlertType;

import javax.microedition.lcdui.Command;

import javax.microedition.lcdui.CommandListener;

import javax.microedition.lcdui.Display;

import javax.microedition.lcdui.Displayable;

import javax.microedition.lcdui.Form;

import javax.microedition.midlet.MIDlet;

import javax.microedition.midlet.MIDletStateChangeException;

/**

* @author Favo

* Preferences - Java - Code Style - Code Templates

*/

public class TestProgressGauge extends MIDlet implements CommandListener {

Display display;

Command workCmd;

Command exitCmd;

Form f;

public TestProgressGauge() {

super();

// TODO Auto-generated constrUCtor stub

display = Display.getDisplay(this);

workCmd = new Command("compute", Command.OK, 10);

exitCmd = new Command("exit", Command.EXIT, 10);

f = new Form("Test");

f.setCommandListener(this);

f.addCommand(workCmd);

f.addCommand(exitCmd);

}

protected void startApp() throws MIDletStateChangeException {

// TODO Auto-generated method stub

display.setCurrent(f);

}

protected void pauseApp() {

// TODO Auto-generated method stub

}

protected void destroyApp(boolean arg0) throws MIDletStateChangeException {

}

public void commandAction(Command arg0, Displayable arg1) {

// TODO Auto-generated method stub

if (arg0 == workCmd) {

ProgressObserver poUI = ProgressGaugeUI.getInstance();

BackgroundTask bkTask = new BackgroundTask(poUI, arg1, display) {

public void runTask() {

alertScreen = new Alert(

"user cancel",

"you press the cancel button and the screen will jump to the main Form",

null, AlertType.ERROR);

alertScreen.setTimeout(Alert.FOREVER);

needAlert = true;

//do something first

getProgressObserver().updateProgress(null);

try {

Thread.sleep(3000);

} catch (Exception e) {

e.printStackTrace();

}

getProgressObserver().updateProgress("sleepd 3s...");

if (getProgressObserver().isStopped())

return;

getProgressObserver().updateProgress(null);

//do something second

try {

Thread.sleep(3000);

} catch (Exception e) {

e.printStackTrace();

}

getProgressObserver().setMax();

display.setCurrent(new Form("complete"));

taskComplete();

}

};

BackgroundTask.runWithProgressGauge(bkTask, "Sleep 6s",

"Sleep now...", true, display);

}else if(arg0==exitCmd){

try {

destroyApp(false);

} catch (MIDletStateChangeException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

notifyDestroyed();

}

}

}

运行流程画面

按下compute-->
用户取消-->

回到前一屏幕-->
按下compute-->

-->
-->
完成

希望这个模型可以加快你的开发速度。如果你有更好的解决办法,能够更清晰的解决问题或是问题的细节,欢迎讨论。

(出处:http://www.knowsky.com)

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有