UI線程及Android的單線程模型原則
當(dāng)應(yīng)用啟動(dòng),系統(tǒng)會(huì)創(chuàng)建一個(gè)主線程。
這個(gè)主線程負(fù)責(zé)向UI組件分發(fā)事件(包括繪制事件)写妥,也是在這個(gè)主線程里鸳粉,你的應(yīng)用和Android的UI組件發(fā)生交互。
所以主線程也叫UI線程集畅。
系統(tǒng)不會(huì)為每個(gè)組件單獨(dú)創(chuàng)建線程近弟,在同一個(gè)進(jìn)程里的UI組件都會(huì)在UI線程里實(shí)例化,系統(tǒng)對(duì)每一個(gè)組件的調(diào)用都是從UI線程分發(fā)出去的挺智。結(jié)果就是祷愉,響應(yīng)系統(tǒng)回調(diào)的方法永遠(yuǎn)都是在UI線程里進(jìn)行的。
當(dāng)App在做一些比較繁重的操作的時(shí)候赦颇,比如網(wǎng)絡(luò)請(qǐng)求二鳄、數(shù)據(jù)庫(kù)操作等相關(guān)操作。如果這些工作都在UI線程里進(jìn)行媒怯,就會(huì)阻塞UI線程订讼,導(dǎo)致時(shí)間停止分發(fā)。對(duì)于用戶來(lái)說(shuō)扇苞,應(yīng)用看起來(lái)就像卡住了欺殿,更壞的情況是,如果UI線程阻塞時(shí)間過(guò)長(zhǎng)鳖敷,用戶就會(huì)看到應(yīng)用無(wú)響應(yīng)提示脖苏。
另外,Android UI toolkit并不是線程安全的定踱,所以你不能從非UI線程來(lái)操作UI組件棍潘。必須把所有的UI操作放在UI線程里。
所以android單線程模型有兩條原則:
1. 不要阻塞UI線程崖媚。
2. 不要再UI線程外操作UI組件亦歉。
使用worker線程
根據(jù)單線程模型的兩條原則,首先至扰,要保證應(yīng)用的響應(yīng)性鳍徽,不能阻塞UI線程,所以當(dāng)你的操作十分耗時(shí)敢课,你應(yīng)該把他們放進(jìn)另外的單獨(dú)線程中(叫做background或者叫worker線程)阶祭。
比如點(diǎn)擊按鈕后绷杜,下載一個(gè)圖片然后在ImageView中展示:
public void onClick(View v) {
new Thread(new Runnable() {
public void run() {
Bitmap b = loadImageFromNetwork("http://example.com/image.png");
mImageView.setImageBitmap(b);
}
}).start();
}
這段代碼用新的線程來(lái)處理網(wǎng)絡(luò)操作,但是它違反了第二條原則:不能從非UI線程訪問(wèn)UI組件濒募。
為了解決這個(gè)問(wèn)題鞭盟,Android提供了一些方法,從其他線程訪問(wèn)UI線程:
- Activity.runOnUiThread(Runnable)
這個(gè)函數(shù)是Activity的成員函數(shù)瑰剃,它的源碼為:
public final void runOnUiThread(Runnable action) {
if(Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
利用Activity.runOnUiThread(Runnable)把更新ui的代碼創(chuàng)建在Runnable中齿诉,然后在需要更新ui時(shí),把這個(gè)Runnable對(duì)象傳給Activity.runOnUiThread(Runnable)晌姚。 Runnable對(duì)像就能在ui程序中被調(diào)用粤剧。如果當(dāng)前線程是UI線程,那么行動(dòng)是立即執(zhí)行挥唠。如果當(dāng)前線程不是UI線程,操作是發(fā)布到事件隊(duì)列的UI線程抵恋。
new Thread(new Runnable() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "hah", Toast.LENGTH_SHORT).show();
}
});
}
}).start();
- View.post(Runnable)
在post(Runnable action)方法里,View獲得當(dāng)前線程(即UI線程)的Handler宝磨,然后將action對(duì)象post到Handler里弧关。在Handler里,它將傳遞過(guò)來(lái)的action對(duì)象包裝成一個(gè)Message(Message的callback為action)唤锉,然后將其投入U(xiǎn)I線程的消息循環(huán)中世囊。在Handler再次處理該Message時(shí),有一條分支(未解釋的那條)就是為它所設(shè)窿祥,直接調(diào)用runnable的run方法株憾。而此時(shí),已經(jīng)路由到UI線程里壁肋,因此号胚,我們可以毫無(wú)顧慮的來(lái)更新UI。
由于不是在新的線程中使用浸遗,所以千萬(wàn)別做復(fù)雜的計(jì)算邏輯。
參考自清沁
public void onClick(View v) {
new Thread(new Runnable() {
public void run() {
final Bitmap b = loadImageFromNetwork();
mImageView.post(new Runnable() {
public void run() {
mImageView.setImageBitmap(b);
}
});
}
}).start();
}
- View.postDelayed(Runnable, long)
但是箱亿,當(dāng)操作變得復(fù)雜的時(shí)候跛锌,這種代碼會(huì)變得非常復(fù)雜,為了處理非UI線程和UI線程之間更加復(fù)雜的交互届惋,可以考慮在worker線程中使用一個(gè)Handler髓帽,來(lái)處理UI線程中傳來(lái)的消息。