Apache Pivot background Task 和 UI thread
来源:互联网 发布:淘宝全球战略 编辑:程序博客网 时间:2024/05/17 02:07
Apache Pivot后台线程与UI线程
文章中用到的一些术语的说明:
UI操作:修改了UI组件的某些属性或则特性,比如修改按钮显示的文本或则图标等或读取UI组件的属性或者特性。
非UI操作:不能有修改或则读取任何与UI组件相关的属性或者特性。
background Task(后台任务): 用于执行非UI操作的线程。
UI thread:用于执行UI操作的线程,一般一个应用程序只有一个UI线程。
很多UI框架,用户界面都有一个独立的线程,称作 UI thread。用户在UI上的操作有些很快就完成,然而有些操作需要耗时比较长的时间(比如同文件加载数据或者从数据库查询数据或者从网络请求数据等),在这种情况下,如果把操作放在UI thread中执行,整个用户界面就会被"挂起"了,直到操作完成用户才可以进行其它的UI操作,当然这种低效率的操作是开发者所不允许的,很多UI框架都采用了另外一个独立于UI thread的线程来执行耗时比较长的由UI操作引入的非UI操作。
在Pivot中,用于执行耗时较长的操作的,一般使用后台任务(background task)来实现,后台任务在Pivot中由类org.apache.pivot.util.concurrent.Task表示。
对于后台任务有一些限制:
1. 由于Pivot的对UI 组件的操作只能在UI thread中,因此所有后台任务是不能操作任何UI组件的。
2.后台任务的执行会有一个输出结果,但是UI thread并不知道后台任务何时执行结束以及执行结果?
为了解决上面2个后台任务的限制,Pivot提供了不同的方法来解决这些问题:
1.在后台任务(非UI线程)操作UI组件时,通过ApplicationContext.queueCallback方法,把要执行的内容加入到UI可执行队列中,该队列的可执行体由UI thread负责轮询调用,类似与其他UI框架中的消息机制。
2. UI thread为了知道UI组件操作所创建的后台任务的执行结果,可以使用TaskAdapter和TaskListener监听后台任务的执行结果,并把任务执行后要执行的操作加入UI thread,由UI thread负责执行实际的代码。
下面我们开看一下Pivot tutorial的Meter代码,并分析后台线程和UIthread之间的互操作。
后台线程与UI Thread之间的互操作包括: UI thread 创建执行后台线程和后台线程修改UI 组件。
1. UI界面的WTKX代码
<
Window
title
=
"Meters"
maximized
=
"true"
02
xmlns:wtkx
=
"http://pivot.apache.org/wtkx"
03
xmlns
=
"org.apache.pivot.wtk"
>
04
<
content
>
05
<
TablePane
>
06
<
columns
>
07
<
TablePane.Column
width
=
"1*"
/>
08
</
columns
>
09
<
rows
>
10
<
TablePane.Row
height
=
"1*"
>
11
<
Border
styles
=
"{padding:2}"
>
12
<
content
>
13
<
BoxPane
styles
=
"{horizontalAlignment:'center', verticalAlignment:'center'}"
>
14
<
Label
text
=
"Progress:"
/>
15
<
Meter
wtkx:id
=
"meter"
preferredWidth
=
"200"
preferredHeight
=
"16"
/>
16
</
BoxPane
>
17
</
content
>
18
</
Border
>
19
</
TablePane.Row
>
20
<
TablePane.Row
height
=
"-1"
>
21
<
BoxPane
styles
=
"{horizontalAlignment:'center', padding:6}"
>
22
<
PushButton
wtkx:id
=
"progressButton"
styles
=
"{minimumAspectRatio:3}"
/>
23
</
BoxPane
>
24
</
TablePane.Row
>
25
</
rows
>
26
</
TablePane
>
27
</
content
>
28
</
Window
>
UI界面的Java 代码:
在下面的代码中,我们实现了一个Task的类,叫SampleTask,用于模拟长时间允许的后台任务,并在任务的执行过程中修改UI组件Meter的相关操作。
package
org.apache.pivot.tutorials.progress;
002
import
org.apache.pivot.collections.Map;
003
import
org.apache.pivot.util.concurrent.Task;
004
import
org.apache.pivot.util.concurrent.TaskExecutionException;
005
import
org.apache.pivot.util.concurrent.TaskListener;
006
import
org.apache.pivot.wtk.Application;
007
import
org.apache.pivot.wtk.ApplicationContext;
008
import
org.apache.pivot.wtk.Button;
009
import
org.apache.pivot.wtk.ButtonPressListener;
010
import
org.apache.pivot.wtk.DesktopApplicationContext;
011
import
org.apache.pivot.wtk.Display;
012
import
org.apache.pivot.wtk.Meter;
013
import
org.apache.pivot.wtk.PushButton;
014
import
org.apache.pivot.wtk.TaskAdapter;
015
import
org.apache.pivot.wtk.Window;
016
import
org.apache.pivot.wtkx.WTKXSerializer;
017
public
class
Meters
implements
Application {
018
public
class
SampleTask
extends
Task<Void> {
019
private
int
percentage =
0
;
020
@Override
021
public
Void execute()
throws
TaskExecutionException {
022
// 模拟一个长时间的操作任务
023
percentage =
0
;
024
while
(percentage <
100
025
&& !abort) {
026
try
{
027
Thread.sleep(
100
);
028
percentage++;
029
// 在UI thread中更新meter的进度,在这里不能直接调用组件的代码,因为该线程是非UI线程,不能操作任何UI组件。通过queueCallback把要执行的代码加入到UI thread的执行队列中,由UI thread负责执行。
030
ApplicationContext.queueCallback(
new
Runnable() {
031
@Override
032
public
void
run() {
033
meter.setPercentage((
double
)percentage /
100
);
034
}
035
});
036
}
catch
(InterruptedException exception) {
037
throw
new
TaskExecutionException(exception);
038
}
039
}
040
return
null
;
041
}
042
}
043
private
Window window =
null
;
044
private
Meter meter =
null
;
045
private
PushButton progressButton =
null
;
046
private
SampleTask sampleTask =
null
;
047
@Override
048
public
void
startup(Display display, Map<String, String> properties)
049
throws
Exception {
050
WTKXSerializer wtkxSerializer =
new
WTKXSerializer();
051
window = (Window)wtkxSerializer.readObject(
this
,
"meters.wtkx"
);
052
meter = (Meter)wtkxSerializer.get(
"meter"
);
053
progressButton = (PushButton)wtkxSerializer.get(
"progressButton"
);
054
progressButton.getButtonPressListeners().add(
new
ButtonPressListener() {
055
@Override
056
public
void
buttonPressed(Button button) {
057
if
(sampleTask ==
null
) {
058
// 创建并驱动一个模型的后台任务; 封装在一个task adapter对象中,
059
// 使得UI thread可以调用后台任务执行完成后的回调代码。
060
// 如果不能用Adpater时,执行完成后的回调代码不能调用任何UI操作。
061
sampleTask =
new
SampleTask();
062
sampleTask.execute(
new
TaskAdapter<Void>(
new
TaskListener<Void>() {
063
@Override taskExecuted在task任务执行结束并且没有抛出异常时被调用
064
public
void
taskExecuted(Task<Void> task) {
065
reset();
066
}
067
@Override executeFailed在task任务执行结束并且抛出异常时被调用
068
public
void
executeFailed(Task<Void> task) {
069
reset();
070
}
071
private
void
reset() {
072
// Reset the meter and button
073
sampleTask =
null
;
074
meter.setPercentage(
0
);
075
updateProgressButton();
076
}
077
}));
078
}
else
{
079
// Cancel the task
080
sampleTask.abort();
081
}
082
updateProgressButton();
083
}
084
});
085
updateProgressButton();
086
window.open(display);
087
}
088
@Override
089
public
boolean
shutdown(
boolean
optional) {
090
if
(window !=
null
) {
091
window.close();
092
}
093
return
false
;
094
}
095
@Override
096
public
void
suspend() {
097
}
098
@Override
099
public
void
resume() {
100
}
101
private
void
updateProgressButton() {
102
if
(sampleTask ==
null
) {
103
progressButton.setButtonData(
"Start"
);
104
}
else
{
105
progressButton.setButtonData(
"Cancel"
);
106
}
107
}
108
public
static
void
main(String[] args) {
109
DesktopApplicationContext.main(Meters.
class
, args);
110
}
111
}
从上面的代码我们看到了UI 线程如何启动一个后台线程,并且要求后台线程执行结束后通知UI线程 和后台线程如何执行UI操作的方法。注意:这里所的后台线程执行UI的操作,并不是指UI操作的代码在后台线程中执行,而是后台线程把要执行的UI操作的代码加入UI thread的执行队列,由UI thread轮询执行(因为UI thread本质上就是一个轮询UI事件的线程)。
task对象在调用execute方法时,如果不传递任何参数,那么等同于在UI thread直接执行操作一样,没有创建任何后台线程。
如果task对象在调用execute时,传递的参数为TaskListener的一个实现的对象(非TaskAdapter)时,在taskExecuted和executeFailed回调方法中不能直接调用UI组件的任何操作代码,除非传递的参数对象为TaskAdapter才可以直接调用,或者在传递TaskListener的实现(非TaskAdapter)的实例时,如果一定要操作UI组件,那么可以使用ApplicationContext.queueCallback对要执行的相关UI操作进行封装前转UI操作到UI thread执行。
示例中使用的就是传递一个TaskAdapter的一个实例作为execute的参数。因此可以在TaskListener的实现中直接调用UI操作,因为TaskAdapter的taskExecuted和executeFailed已经为我们封装了相关的代码,使得操作可以被前转到UI thread执行。
如果熟悉了解SWT的,可能会更容易理解上面的代码。其实很多UI都是类似的做法只是表示方式不同而已,比如古老的MFC 使用的是消息
- Apache Pivot background Task 和 UI thread
- 关于Apache Pivot UI tooltip属性
- Error about updating UI in background thread
- thread和task
- Main Thread Checker: UI API called on a background thread:
- C#中的thread和task之Task
- Window Background 和 UI Speed
- apache-pivot
- 使用Task代替ThreadPool和Thread
- 使用Task代替ThreadPool和Thread
- 使用Task代替ThreadPool和Thread
- 使用Task代替ThreadPool和Thread
- thread和task的句柄区别
- iOS合理利用Local Push和Long-running background task
- ios background task
- DEBUG in background task
- ios background task
- C#中的thread和task之 Thread & ThreadPool
- web.xml配置
- asp.net mvc 1.0
- SQL 2008 截断事务日志不能用了??
- Linux内核源代码的阅读和工具介绍
- 决定将来的故事
- Apache Pivot background Task 和 UI thread
- JS 打开新窗口的方法
- cms中角色,权限,用户组的设计
- post与get的区别
- SQL like 带变量的 查询
- WIN XP蓝屏代码大全
- 宏
- Webvz安装教程
- GridView 删除事件的执行顺序