Android中使用线程Thread的方法和Java SE相同。和大多数OS系统一样,Android中也有称为UI Thread的主线程。UI Thread 主要用来给相应的Widget分发消息,包括绘制(Drawing)事件。UI Thread 也是用来处理用户交互事件的线程。比如:如果你按下屏幕上某个按钮,UI 线程则将Touch 事件通知对应的控件(Widgets),Widget 则将其状态设置成“按下”,并把“重绘”(Invalidate)事件发到Event Queue中去。 UI线程从Event Queue中读取事件后通知Widgets重画自身。
如果你的应用设计不好的话, UI线程的这种单线程模式就会导致非常差的用户响应性能。特别是你将一些费时的操作如网络访问或数据库访问也放在UI线程中,这些操作会造成用户界面无反应,最糟糕的是,如果UI线程阻塞超过几秒(5秒),著名的ANR对话框就会出现:
所以在设计应用时,需要把一些费时的任务使用单独的工作线程来运行避免阻塞UI线程,但是如果在工作线程中想更新UI线程的话,不能直接在工作线程中更新UI,这是因为UI线程不是“Thread Safe”。因此所有UI相关的操作一般必须在UI Thread中进行。
Android OS提供了多种方法可以用在非UI线程访问UI线程。
Bezier 示例动态显示Bezier曲线,使用了Activity.runOnUiThread 来更新屏幕,完整代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
|
public
class
Bezier
extends
Graphics2DActivity
implements
OnClickListener,Runnable{
/**
* The animation thread.
*/
private
Thread thread;
private
volatile
boolean
stopThread=
false
;
private
boolean
stopOrNot=
false
;
boolean
drawn;
/**
* The random number generator.
*/
static
java.util.Random random =
new
java.util.Random();
/**
* The animated path
*/
Path path =
new
Path();
/**
* Red brush used to fill the path.
*/
SolidBrush brush =
new
SolidBrush(Color.RED);
private
static
final
int
NUMPTS =
6
;
private
int
animpts[] =
new
int
[NUMPTS *
2
];
private
int
deltas[] =
new
int
[NUMPTS *
2
];
long
startt, endt;
private
Button btnOptions;
@Override
protected
void
drawImage() {
drawDemo(
100
,
100
);
}
public
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.beziers);
graphic2dView
= (GuidebeeGraphics2DView) findViewById(R.id.graphics2dview);
btnOptions = (Button) findViewById(R.id.btnStopStart);
btnOptions.setOnClickListener(
this
);
reset(
100
,
100
);
if
(thread ==
null
) {
thread =
new
Thread(
this
);
thread.start();
}
}
@Override
public
void
onClick(View view) {
if
(!stopOrNot){
btnOptions.setText(
"Start"
);
stopThread=
true
;
}
else
{
stopThread=
false
;
btnOptions.setText(
"Stop"
);
if
(thread ==
null
) {
thread =
new
Thread(
this
);
thread.start();
}
}
stopOrNot=!stopOrNot;
}
/**
* Generates new points for the path.
*/
private
void
animate(
int
[] pts,
int
[] deltas,
int
i,
int
limit) {
int
newpt = pts[i] + deltas[i];
if
(newpt <=
0
) {
newpt = -newpt;
deltas[i] = (random.nextInt() &
0x00000003
)
+
2
;
}
else
if
(newpt >= limit) {
newpt =
2
* limit - newpt;
deltas[i] = -((random.nextInt() &
0x00000003
)
+
2
);
}
pts[i] = newpt;
}
/**
* Resets the animation data.
*/
private
void
reset(
int
w,
int
h) {
for
(
int
i =
0
; i < animpts.length; i +=
2
) {
animpts[i +
0
]
= (random.nextInt() &
0x00000003
)
* w /
2
;
animpts[i +
1
]
= (random.nextInt() &
0x00000003
)
* h /
2
;
deltas[i +
0
]
= (random.nextInt() &
0x00000003
)
*
6
+
4
;
deltas[i +
1
]
= (random.nextInt() &
0x00000003
)
*
6
+
4
;
if
(animpts[i +
0
] > w /
2
) {
deltas[i +
0
] = -deltas[i +
0
];
}
if
(animpts[i +
1
] > h /
2
) {
deltas[i +
1
] = -deltas[i +
1
];
}
}
}
final
Runnable updateCanvas =
new
Runnable() {
public
void
run() {
int
offsetX = (graphic2dView.getWidth() -
SharedGraphics2DInstance.CANVAS_WIDTH) /
2
;
int
offsetY = (graphic2dView.getHeight()
- SharedGraphics2DInstance.CANVAS_HEIGHT) /
2
;
graphic2dView.invalidate(offsetX,offsetY,
offsetX+
100
,offsetY+
100
);
}
};
/**
* Sets the points of the path and draws and fills the path.
*/
private
void
drawDemo(
int
w,
int
h) {
for
(
int
i =
0
; i < animpts.length; i +=
2
) {
animate(animpts, deltas, i +
0
, w);
animate(animpts, deltas, i +
1
, h);
}
//Generates the new pata data.
path.reset();
int
[] ctrlpts = animpts;
int
len = ctrlpts.length;
int
prevx = ctrlpts[len -
2
];
int
prevy = ctrlpts[len -
1
];
int
curx = ctrlpts[
0
];
int
cury = ctrlpts[
1
];
int
midx = (curx + prevx) /
2
;
int
midy = (cury + prevy) /
2
;
path.moveTo(midx, midy);
for
(
int
i =
2
; i <= ctrlpts.length; i +=
2
) {
int
x1 = (curx + midx) /
2
;
int
y1 = (cury + midy) /
2
;
prevx = curx;
prevy = cury;
if
(i < ctrlpts.length) {
curx = ctrlpts[i +
0
];
cury = ctrlpts[i +
1
];
}
else
{
curx = ctrlpts[
0
];
cury = ctrlpts[
1
];
}
midx = (curx + prevx) /
2
;
midy = (cury + prevy) /
2
;
int
x2 = (prevx + midx) /
2
;
int
y2 = (prevy + midy) /
2
;
path.curveTo(x1, y1, x2, y2, midx, midy);
}
path.closePath();
// clear the clipRect area before production
graphics2D.clear(Color.WHITE);
graphics2D.fill(brush, path);
this
.runOnUiThread(updateCanvas);
}
public
void
run() {
Thread me = Thread.currentThread();
if
(!drawn) {
synchronized
(
this
) {
graphics2D.clear(Color.WHITE);
graphics2D.fill(brush, path);
graphic2dView.refreshCanvas();
drawn =
true
;
}
}
while
(thread == me && !stopThread) {
drawDemo(
100
,
100
);
}
thread =
null
;
}
}
|
除了上述的方法外,Android还提供了AsyncTask类以简化工作线程与UI线程之间的通信。这里不详述。此外,上面Bezier曲线动画在屏幕上显示时有闪烁的现象,这是动态显示图像的一个常见问题,后面将专门讨论。