再议j2me进度条与线程化模型

作者:FavoYang Email:favoyang@yahoo.com 欢迎交流
Keywords:线程化模型 j2me UI设计

内容提要:
本文是《j2me进度条与线程化模型》一文的续(以后简称原文,没看过的建议看一下)。
讨论了原文中使用的线程模型的不足,并针对她的缺点提出了新的改进办法并给出了改进后的实现。因原文中UI部分有灵活的扩展性,未作更改。

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

正文:

前台UI如何和后台线程交互
原文中模型,是一个前台的ProgressGaugeUI与后台线程无关的模型。这样设计的时候最大程度上的化简了通信的复杂性,实际上是一种单方向的模型(由BackgroundTask 向 PGUI通信)。按照这种模式的要求,程序员在Override BackgroundTask 的runTask()方法时,有义务定期的去查训前台的PGUI的运行情况,并根据这种情况做出反映。这样这种模式完全相信后台线程,将是否响应用户cancel命令的权利交给了后台线程,如果后台线程陷入麻烦没有响应了(比如访问一个很昂贵的网络连接),此时用户试图cancel也没有用,程序将会暂时的死锁,直到后台线程有时间去检查前台的状态。并且在实际情况中,到底什么时候去查询,多大的频率都是问题。在代码段中过多的此类代码,会影响对正常的流程的理解。

从下面的这个顺序图,可以看到这个具体流程:

我们需要一个方法,让我们能够强制的结束Task。这个方法由背景线程自己提供,取名叫做cancel()。当然没有任何一个方法可以强迫线程立即结束(曾经有,因为安全性问题而被取消)。所以cancel()方法往往通过关闭的资源(一个连接,一个流等)来迫使runTask发生异常被中断,runTask有义务根据自己的约定捕捉此类异常并立即退出。一图胜千言,让我们看看这种方法的流程。

很显然的,关键在于前台的线程对后台的线程进行了回调,这样就可以解决问题了。但是新的问题来了,这样做迫使我们将前台与后台线程紧密的耦合在了一起(因为要回调嘛)。能不能既实现回调又避免前台UI与后台线程的紧密耦合呢?

通过Cancelable接口降低耦合度
幸好,我门可以利用接口来实现这一点。
先前的模型是这样的:

为了降低耦合,我们建立一个接口
public interface Cancelable {
/**
* 本方法非阻塞,应该立即返回(如有必要开启新的线程)
* 此外应避免对此方法的重复调用
*/
public void cancel();
}
接下来在ProgressObserver加入对这个方法的支持
public interface ProgressObserver {
……
……
/**
* 设置取消Task时回调的函数对象
* @param co
*/
public void setCancelalbeObject(Cancelable co);
}

这样,就可以在用户按下取消按钮的时候,就可以进行对Cancelable.cancel()的回调。这样灵活性大大增强了。

新代码
更新后的代码如下,除了改用以上的模型外,还对部分的BUG进行了更正,更改的地方会用不同的颜色表示。详细的用法可参见注释

/////////////////////////////////////////////////////////////////
Cancelable.java
package com.favo.ui;

/**
* @author Favo
*
* TODO To change the template for this generated type comment go to
* Window - Preferences - Java - Code Style - Code Templates
*/
public interface Cancelable {
/**
* 此方法非阻塞,应该立即返回(如果有必要开启新的线程)
* 此外应避免对此方法的重复调用
*/
public void cancel();
}

ProgressObserver.java
/*
* Created on 2005-2-26
*
* TODO To change the template for this generated file go to
* Window - Preferences - Java - Code Style - Code Templates
*/
package com.favo.ui;

import javax.microedition.lcdui.Display;

/**
* @author Favo
*
* 这是仿照Smart Ticket制作的进度条观察者,这个模型的优点是
* 1,低耦合度。你可以通过Form,Canvas等来实现这个接口
* 2,可中断任务的支持。是通过在内部设置flag并回调cancelObject的cancel()来实现的。后台线程可以通过查询这个flag从而知道用户是否中断过Task。
*/
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();

/**
* 设置进度条是否可以暂停
* @param stoppable
*/
public void setStoppable(boolean stoppable);

/**
* 查询用户是否暂停了任务
* @return
*/
public boolean isStopped();

/**
* 设置任务暂停标记
*/
public void setStopped(boolean stopped);

/**
* 设置标题
*/
public void setTitle(String title);

/**
* 设置提示
*/
public void setPrompt(String prompt);

/**
* 设置是否取消Task时回调的函数对象
* @param co
*/
public void setCancelalbeObject(Cancelable co);
}

ProgressGaugeUI.java
/*
* Created on 2005-2-26
* Window - Preferences - Java - Code Style - Code Templates
*/
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
* 新版本的pgUI,主要是增加了cancel task的能力,通过回调CancelableObject的
* cancel方法实现。
* 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;

Cancelable cancelableObject;

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;
}

/*
* (non-Javadoc)
*
* @see com.favo.ui.ProgressObserver#reset(java.lang.Object)
*/
public void reset() {
current=0;
gauge.setValue(0);
stopped=false;
setStoppable(false);
setTitle("");
setPrompt("");
cancelableObject=null;
}

/*
* (non-Javadoc)
*
* @see com.favo.ui.ProgressObserver#updateProgress(java.lang.Object)
*/
public void updateProgress(Object param1) {
// TODO Auto-generated method stub
current=(current+1)%GAUGE_LEVELS;
gauge.setValue(current * GAUGE_MAX/GAUGE_LEVELS);
if(param1!=null && param1 instanceof String){
setPrompt((String)param1);
}
}

/*
* (non-Javadoc)
*
* @see com.favo.ui.ProgressObserver#isStoppable()
*/
public boolean isStoppable() {
return stoppable;
}

/*
* (non-Javadoc)
*
* @see com.favo.ui.ProgressObserver#setStoppable(boolean)
*/
public void setStoppable(boolean stoppable) {
this.stoppable = stoppable;
if(stoppable){
f.addCommand(stopCMD);
}else{
f.removeCommand(stopCMD);
}
}

/*
* (non-Javadoc)
*
* @see com.favo.ui.ProgressObserver#isStopped()
*/
public boolean isStopped() {
// TODO Auto-generated method stub
return stopped;
}

/*
* (non-Javadoc)
*
* @see com.favo.ui.ProgressObserver#setTitle(java.lang.String)
*/
public void setTitle(String title) {
// TODO Auto-generated method stub
f.setTitle(title);
}

/*
* (non-Javadoc)
*
* @see com.favo.ui.ProgressObserver#setPrompt(java.lang.String)
*/
public void setPrompt(String prompt) {
gauge.setLabel(prompt);
}

/*
* (non-Javadoc)
*
* @see javax.microedition.lcdui.CommandListener#commandAction(javax.microedition.lcdui.Command,
* javax.microedition.lcdui.Displayable)
*/
public void commandAction(Command arg0, Displayable arg1) {
if(arg0==stopCMD){
if(isStoppable())
if(!isStopped()){//保证仅被调用一次
setStopped(true);
if(cancelableObject!=null)
cancelableObject.cancel();
}
else{
setPrompt("can't stop!");
}
}
}

/* (non-Javadoc)
* @see com.favo.ui.ProgressObserver#show(javax.microedition.lcdui.Display)
*/
public void show(Display display) {
display.setCurrent(f);
}

/* (non-Javadoc)
* @see com.favo.ui.ProgressObserver#exit()
*/
public void exit() {
cancelableObject=null;
}

/* (non-Javadoc)
* @see com.favo.ui.ProgressObserver#setMax()
*/
public void setMax() {
gauge.setValue(GAUGE_MAX);
}

/* (non-Javadoc)
* @see com.favo.ui.ProgressObserver#setStopped(boolean)
*/
public void setStopped(boolean stopped) {
this.stopped=stopped;
}

public void setCancelalbeObject(Cancelable co){
this.cancelableObject=co;
}

}

BackgroundTask.java
/*
* Created on 2005-2-26
*
* TODO To change the template for this generated file go to
* Window - Preferences - Java - Code Style - Code Templates
*/
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
*
* TODO To change the template for this generated type comment go to Window -
* Preferences - Java - Code Style - Code Templates
*/
public abstract class BackgroundTask extends Thread implements Cancelable {

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;
}

/*
* (non-Javadoc)
*
* @see java.lang.Thread#run()
*/
public void run() {
boolean taskComplete=false;
try {
taskComplete=runTask();
} catch (Exception e) {
Alert al = new Alert("undefine exception", e.getMessage(), null,
AlertType.ALARM);
al.setTimeout(Alert.FOREVER);
display.setCurrent(al);
} finally {
if (!taskComplete&&poUI.isStoppable()) {
if (poUI.isStopped()) {//如果用户中断了程序
if (needAlert) {
display.setCurrent(alertScreen, preScreen);
} else {
display.setCurrent(preScreen);
}
}
}
poUI.exit();
}
}

/**
* 须由用户定义的任务
* 注意!!!
* 任务如果成功的运行,应该由此方法内部负责跳转至成功画面,并返回true.
* 若任务运行失败,请设置needAlert(是否需要警报),AlertScreen(警报画面),preScreen(跳转回的前一个具体屏幕)
* 手动更新进度栏,请调用pgUI.updateProgress().
* 请确保当cancel()调用时,此方法会立即退出,并返回false(如果因为异常跳出此函数是可以接受的行为).
*/
public abstract boolean 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);
if(stoppable){
po.setCancelalbeObject(btask);
}
po.setTitle(title);
po.setPrompt(prompt);
po.show(display);
btask.start();
}

public ProgressObserver getProgressObserver() {
return poUI;
}

//取消了taskComplete方法,因为runTask已经有了返回值
//
// public void taskComplete(){
// getProgressObserver().setStopped(false);
// }
}

TestProgressGauge.java
/*
* Created on 2005-2-26
*
* TODO To change the template for this generated file go to
* Window - Preferences - Java - Code Style - Code Templates
*/
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
*
* TODO To change the template for this generated type comment go to Window -
* 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);
}

/*
* (non-Javadoc)
*
* @see javax.microedition.midlet.MIDlet#startApp()
*/
protected void startApp() throws MIDletStateChangeException {
// TODO Auto-generated method stub
display.setCurrent(f);
}

/*
* (non-Javadoc)
*
* @see javax.microedition.midlet.MIDlet#pauseApp()
*/
protected void pauseApp() {
// TODO Auto-generated method stub

}

/*
* (non-Javadoc)
*
* @see javax.microedition.midlet.MIDlet#destroyApp(boolean)
*/
protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
// TODO Auto-generated method stub

}

/*
* (non-Javadoc)
*
* @see javax.microedition.lcdui.CommandListener#commandAction(javax.microedition.lcdui.Command,
* javax.microedition.lcdui.Displayable)
*/
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 boolean runTask() {
System.out.println("task start!");
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();
return false;
}
getProgressObserver().updateProgress("sleepd 3s...");//手动更新
//取消了此处的手动查询点
// if (getProgressObserver().isStopped())
// return;
getProgressObserver().updateProgress(null);//手动更新
//do something again
try {
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
return false;
}
getProgressObserver().setMax();//手动更新
display.setCurrent(new Form("complete"));//跳转成功画面
return true;
}

public void cancel() {
this.interrupt();
}
};
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();
}
}

}
/////////////////////////////////////////////////////////////////

时间: 2024-10-03 13:53:39

再议j2me进度条与线程化模型的相关文章

j2me进度条与线程化模型

j2me进度条与线程化模型作者:FavoYang Email:favoyang@yahoo.com 欢迎交流 Keyworld:线程化模型 j2me UI设计 内容提要: 本文研究如何建立一个方便使用的线程化模型,这个线程化模型由前台的进度条UI 和后台的背景线程组成. 版权声明: 本文同时发表在www.j2medev.com和我的Blog(blog.csdn.net/alikeboy)上,如果需要转载,有三个途径:1)联系我并经我同意:2)和www.j2medev.com有转载文章合作协议的

Android 高手进阶之自定义View,自定义属性(带进度的圆形进度条)

很多的时候,系统自带的View满足不了我们功能的需求,那么我们就需要自己来自定义一个能满足我们需求的View,自定义View我们需要先继承View,添加类的构造方法,重写父类View的一些方法,例如onDraw,为了我们自定义的View在一个项目中能够重用,有时候我们需要自定义其属性,举个很简单的例子,我在项目中的多个界面使用我自定义的View,每个界面该自定义View的颜色都不相同,这时候如果没有自定义属性,那我们是不是需要构建不同颜色的View出来呢,这样子我们的代码就会显得很沉厄,所以这时

Android仿水波纹流量球进度条控制器_Android

仿水波纹流球进度条控制器,Android实现高端大气的主流特效,供大家参考,具体内容如下 效果图: CircleView 这里主要是实现中心圆以及水波特效 package com.lgl.circleview; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.gra

Android带进度的圆形进度条_Android

我们还是用一个小例子来看看自定义View和自定义属性的使用,带大家来自己定义一个带进度的圆形进度条,我们还是先看一下效果吧 从上面可以看出,我们可以自定义圆环的颜色,圆环进度的颜色,是否显示进度的百分比,进度百分比的颜色,以及进度是实心还是空心等等,这样子是不是很多元化很方便呢?接下来我们就来教大家怎么来定义 1.在values下面新建一个attrs.xml,现在里面定义我们的属性,不同的属性对应不同的format,接下来我贴上我在自定义这个进度条所用到的属性 <?xml version="

Android特效专辑(十一)——仿水波纹流量球进度条控制器,实现高端大气的主流特效

Android特效专辑(十一)--仿水波纹流球进度条控制器,实现高端大气的主流特效 今天看到一个效果挺不错的,就模仿了下来,加上了一些自己想要的效果,感觉还不错的样子,所以就分享出来了,话不多说,上图 截图 CircleView 这里主要是实现中心圆以及水波特效 package com.lgl.circleview; import android.content.Context; import android.graphics.Canvas; import android.graphics.Co

Focusky怎么显示底部播放进度条?Focusky显示底部播放进度条方法

Focusky显示/.取消底部播放进度条的使用教程: 1. 我们打开软件,然后新建工程,如在菜单中点击"选项": 2. 点击"选项"-"展示设置"之后在界面中再找到"底部进度条设置"效果如下所示: 3. 现在我们要显示进度条只要选中"显示底部进步条",不显示则不勾选该选项,如图3所示: 4. 最后点击"保存",即可.

自定义Android六边形进度条(附源码)_Android

本文实例讲述了Android自定义圆形进度条,分享给大家供大家参考.具体如下: 大家也可以参考这两篇文章进行学习: <自定义Android圆形进度条(附源码)>   <Android带进度的圆形进度条> 运行效果截图如下: 主要代码: package com.sxc.hexagonprogress; import java.util.Random; import android.content.Context; import android.content.res.ColorSta

Android带进度的圆形进度条

我们还是用一个小例子来看看自定义View和自定义属性的使用,带大家来自己定义一个带进度的圆形进度条,我们还是先看一下效果吧 从上面可以看出,我们可以自定义圆环的颜色,圆环进度的颜色,是否显示进度的百分比,进度百分比的颜色,以及进度是实心还是空心等等,这样子是不是很多元化很方便呢?接下来我们就来教大家怎么来定义 1.在values下面新建一个attrs.xml,现在里面定义我们的属性,不同的属性对应不同的format,接下来我贴上我在自定义这个进度条所用到的属性 <?xml version="

混合开发(一)——WebView开发高级技巧之加载网页以及JavaScript,加载进度条

混合开发(一)--WebView开发高级技巧之加载网页以及JavaScript,加载进度条 现在关于混合开发也越来越多了,很多人喜欢跟随,比如HB,比如RN,其实这东西很早就有这么一个概念了,而且说实话,这方面的需求目前来讲,还是只针对一个别的应用的,不过日后会发展成什么样,那我就不知道了,不过在此之前,我们的WebView,还是用的比较多的,包括他浏览新闻,以及加载一些动作,也就是加载JS,这样的话,我们就可以拿出来讲一讲了,说真的,学习android也挺久的了,感觉很多东西,一出来的时候都哇