操作是很有可能並發的,而界面只有一個
這個和買票排隊是一回事
買票的人太多了,賣票的只有一個,只能一個一個來
如果你開多線程,讓100個人同時去買票,而且不排隊,那么后果會怎么樣- -
同理,你開多線程,讓100個線程去設置同一個TextView的顯示內容,每個顯示內容都不一樣,它該聽誰的?
那為什么不直接new一個新線程而要使用一個所謂的handler?
就是因為new了一個子線程才要用handler的,
不然在主線程里更新UI要handler干什么?多此一舉
就好比只有1個人來買票,賣票的難道會跟他說:同志,請你排隊!?
handle是主線程 ,Thread是從線程。控件數據更改只能在主線程 里,所以要用handle
更新UI只能在主線程里進行,否則會報錯。但有時我們在子線程里進行操作需要更新UI,handler就登場了,它可以把子線程的數據傳給主線程,讓主線程同步操作來更新UI。
先來看這樣一個例子
package com.hua;
import android.app.Activity;
import android.os.Bundle;
public class UpdateUInum1Activity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
new Thread(new Runnable() {
@Override
public void run() {
setTitle("fengyi.hua");
}
});
}
}
上面就是為了實現用一個Thread來更新Title,可以實現這個功能,刷新UI界面。但是這樣是不對的,因為它違背了單線程模型:Android UI操作並不是線程安全的並且這些操作必須在UI線程中執行。
有些人覺得這個方法確實也多余,為什么呢,因為既然是刷新一次,我完全可以在主線程中執行刷新Title的操作的,為什么還要開啟線程。這是因為可能涉及到延時或者其它。比如說等待1min后再進行刷新操作,這個時間段要保證主UI線程是可操作的,所以要用到Thread來更新。但是這確實對於android的單線程模型有沖突,不建議使用。使用錯誤的例子如下:
package com.hua;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
public class UpdateTitleActivity extends Activity {
private void updateTitle() {
Date date = new Date();
int hour, minute, second;
String shour, sminute, ssecond;
hour = (date.getHours() + 8) % 24;
minute = date.getMinutes();
second = date.getSeconds();
if (hour < 10) {
shour = "0" + hour;
} else {
shour = "" + hour;
}
if (minute < 10) {
sminute = "0" + minute;
} else {
sminute = "" + minute;
}
if (second < 10) {
ssecond = "0" + second;
} else {
ssecond = "" + second;
}
setTitle("當前時間:" + shour + ":" + sminute + ":" + ssecond);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Timer timer = new Timer();
timer.scheduleAtFixedRate(new myTask(), 1, 1000);//這里是利用Timer,跟使用Thread是一個效果。表現
//的效果就是多次更新Title,看會不會出問題。
}
private class myTask extends TimerTask {
@Override
public void run() {
updateTitle();
}
}
}
上面的代碼用來每1s刷新一次Title,用來顯示當前時間。但是由於android是單線程模型,存在線程安全問題,所以當第二次刷新的時候,出現錯誤。
正確的做法
上面所述兩種方法,分別是Thread方法,和TimerTask方法。在Java中是常用的,因為線程安全。但是在單線程模型的android中,是不能用的。正確的方法有2個。
1.Thread+handler
2.TimerTask+handler
3.Runnable+Handler.postDelayed(runnable,time)
例子:Timertask+handler
package com.hua;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
public class UpdateTitleActivity extends Activity {
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
updateTitle();
break;
default:
break;
}
}
};
private void updateTitle() {
Date date = new Date();
int hour, minute, second;
String shour, sminute, ssecond;
hour = (date.getHours() + 8) % 24;
minute = date.getMinutes();
second = date.getSeconds();
if (hour < 10) {
shour = "0" + hour;
} else {
shour = "" + hour;
}
if (minute < 10) {
sminute = "0" + minute;
} else {
sminute = "" + minute;
}
if (second < 10) {
ssecond = "0" + second;
} else {
ssecond = "" + second;
}
setTitle("當前時間:" + shour + ":" + sminute + ":" + ssecond);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Timer timer = new Timer();
timer.scheduleAtFixedRate(new myTask(), 1, 10000);
}
private class myTask extends TimerTask {
@Override
public void run() {
Message msg = new Message();
msg.what = 1;
mHandler.sendMessage(msg);
}
}
}
記住,處理都是在handleMessage里面,當然也可以不在,可以在handler的內類Callback的方法handleMessage里面。Handler跟其Callback也是學問,可以以后講。