Android 中使用線程 Thread 的方法和 Java SE 相同。和大多數(shù) OS 系統(tǒng)一樣,Android 中也有稱為 UI Thread 的主線程。UI Thread 主要用來給相應(yīng)的 Widget 分發(fā)消息,包括繪制(Drawing)事件。UI Thread 也是用來處理用戶交互事件的線程。比如:如果你按下屏幕上某個(gè)按鈕,UI 線程則將Touch 事件通知對(duì)應(yīng)的控件(Widgets),Widget 則將其狀態(tài)設(shè)置成“按下”,并把“重繪”(Invalidate)事件發(fā)到 Event Queue 中去。 UI 線程從 Event Queue 中讀取事件后通知Widgets 重畫自身。
如果你的應(yīng)用設(shè)計(jì)不好的話, UI 線程的這種單線程模式就會(huì)導(dǎo)致非常差的用戶響應(yīng)性能。特別是你將一些費(fèi)時(shí)的操作如網(wǎng)絡(luò)訪問或數(shù)據(jù)庫訪問也放在 UI 線程中,這些操作會(huì)造成用戶界面無反應(yīng),最糟糕的是,如果 UI 線程阻塞超過幾秒(5秒),著名的 ANR 對(duì)話框就會(huì)出現(xiàn):
http://wiki.jikexueyuan.com/project/android-development-tutorial/images/29.png" alt="" />
所以在設(shè)計(jì)應(yīng)用時(shí),需要把一些費(fèi)時(shí)的任務(wù)使用單獨(dú)的工作線程來運(yùn)行避免阻塞 UI 線程,但是如果在工作線程中想更新 UI 線程的話,不能直接在工作線程中更新 UI,這是因?yàn)?UI 線程不是“Thread Safe”。因此所有 UI 相關(guān)的操作一般必須在 UI Thread 中進(jìn)行。
Android OS 提供了多種方法可以用在非 UI 線程訪問 UI 線程。
Bezier 示例動(dòng)態(tài)顯示 Bezier 曲線,使用了 Activity.runOnUiThread 來更新屏幕,完整代碼如下:
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;
}
}
http://wiki.jikexueyuan.com/project/android-development-tutorial/images/30.png" alt="" />
除了上述的方法外,Android 還提供了 AsyncTask 類以簡化工作線程與 UI 線程之間的通信。這里不詳述。此外,上面 Bezier 曲線動(dòng)畫在屏幕上顯示時(shí)有閃爍的現(xiàn)象,這是動(dòng)態(tài)顯示圖像的一個(gè)常見問題,后面將專門討論。