快速切換到主線程更新UI的三種方法
- Activity.runOnUiThread(Runnable)
把更新UI的代碼創(chuàng)建在Runnable中,并傳給 Activity.runOnUiThread()如果當(dāng)前線程是UI線程,那么會(huì)立即執(zhí)行;反之,會(huì)調(diào)用UI線程handler.post()將其放入U(xiǎn)I線程的消息隊(duì)列中
new Thread(){
public void run() {
//此時(shí)在子線程
((MainActivity) context).runOnUiThread(new Runnable() {
@Override
public void run() {
//此時(shí)已經(jīng)回到主線程,注意要強(qiáng)轉(zhuǎn)到主線程的MainActivity
}
});
};
}.start();
- View.post(Runnable)
如果View已經(jīng)attach到Window,直接調(diào)用UI線程的Handler發(fā)送Runnable到MessageQueue
如果View還未attach到Window,將Runnable放入ViewRootImpl的RunQueue中,RunQueue會(huì)實(shí)現(xiàn)延遲執(zhí)行Runnable任務(wù),并且Runnable最終不會(huì)被加入到MessageQueue里,也不會(huì)被Looper執(zhí)行,而是等到ViewRootImpl的下一個(gè)performTraversals時(shí)候,把RunQueue里的所有Runnable都拿出來并執(zhí)行,接著清空RunQueue
mTextView.post(new Runnable() {
@Override
//可快速更新該view
public void run() {
mTextView.setText("xxx");
//也可以更新其他view
mIageView.setBackgroundResource(R.drawable.icon);
}
});
還有View.postDelayed(Runnable, long),其中l(wèi)ong表示延遲的毫秒數(shù),示例如下。
//5秒倒計(jì)時(shí)跳轉(zhuǎn)到下個(gè)頁面
index=5;
mTextView.postDelayed(new Runnable() {
@Override
public void run() {
mTextView.setText("" + index);
index--;
if (index == 0) {
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(intent);
} else {
mTextView.postDelayed(this, 1000);
}
}
}, 1000);
c.Handler.post(Runnable)&Handler.post(Runnable,long)
//假設(shè)在子線程,需要獲取主線程的Looper和Queue
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
//此時(shí)已經(jīng)回到主線程
}
});
當(dāng)然還可以用AsyncTask、IntentService、HandlerThread,還有許多開源框架。
Thread阻塞方式:
sleep():
進(jìn)入休眠狀態(tài),但持有對(duì)象的鎖旗標(biāo),休眠結(jié)束后,從阻塞狀態(tài)——》就緒狀態(tài)。yield():
進(jìn)入休眠狀態(tài),也持有對(duì)象的鎖旗標(biāo)和sleep相似,但是不能控制時(shí)間,也有可能剛放棄運(yùn)行就重新獲取運(yùn)行權(quán)。wait():
釋放鎖旗標(biāo),進(jìn)入wait池,等到另一個(gè)線程對(duì)等待的對(duì)象調(diào)用notify()或notifyall()后,等待線程才會(huì)從wait池進(jìn)入lock池(從一種阻塞狀態(tài)進(jìn)入另一種阻塞狀態(tài))。synchornized未獲取對(duì)象的鎖旗標(biāo)
進(jìn)入該對(duì)象lock池,等待鎖旗標(biāo)被歸還,該對(duì)象的lock池中的線程才會(huì)獲得鎖旗標(biāo)進(jìn)入就緒狀態(tài)。Threa中的isAlive(),與線程狀態(tài)有關(guān)。
線程啟動(dòng)后,并且在run()方法沒有結(jié)束前,isAlive返回true。
所以如果返回false,就知道該線程處于剛被創(chuàng)建還尚未處于strat階段,或者已處于dead狀態(tài)。
如果返回true,該線程就處于其生命周期中(運(yùn)行或阻塞)。
線程狀態(tài)流程圖

- NEW:創(chuàng)建狀態(tài),線程創(chuàng)建之后,但是還未啟動(dòng)。
- RUNNABLE:運(yùn)行狀態(tài),處于運(yùn)行狀態(tài)的線程,但有可能處于等待狀態(tài),例如等待CPU、IO等。
- WAITING:等待狀態(tài),一般是調(diào)用了wait()、join()、LockSupport.spark()等方法。
- TIMED_WAITING:超時(shí)等待狀態(tài),也就是帶時(shí)間的等待狀態(tài)。一般是調(diào)用了wait(time)、join(time)、LockSupport.sparkNanos()、LockSupport.sparkUnit()等方法。
- BLOCKED:阻塞狀態(tài),等待鎖的釋放,例如調(diào)用了synchronized增加了鎖。
- TERMINATED:終止?fàn)顟B(tài),一般是線程完成任務(wù)后退出或者異常終止。
NEW、WAITING、TIMED_WAITING都比較好理解,我們重點(diǎn)說一說RUNNABLE運(yùn)行態(tài)和BLOCKED阻塞態(tài)。
線程進(jìn)入RUNNABLE運(yùn)行態(tài)一般分為五種情況:
- 線程調(diào)用sleep(time)后查出了休眠時(shí)間
- 線程調(diào)用的阻塞IO已經(jīng)返回,阻塞方法執(zhí)行完畢
- 線程成功的獲取了資源鎖
- 線程正在等待某個(gè)通知,成功的獲得了其他線程發(fā)出的通知
- 線程處于掛起狀態(tài),然后調(diào)用了resume()恢復(fù)方法,解除了掛起。
線程進(jìn)入BLOCKED阻塞態(tài)一般也分為五種情況:
- 線程調(diào)用sleep()方法主動(dòng)放棄占有的資源
- 線程調(diào)用了阻塞式IO的方法,在該方法返回前,該線程被阻塞。
- 線程視圖獲得一個(gè)資源鎖,但是該資源鎖正被其他線程鎖持有。
- 線程正在等待某個(gè)通知
- 線程調(diào)度器調(diào)用suspend()方法將該線程掛起
我們?cè)賮砜纯春途€程狀態(tài)相關(guān)的一些方法。
- sleep()方法讓當(dāng)前正在執(zhí)行的線程在指定時(shí)間內(nèi)暫停執(zhí)行,正在執(zhí)行的線程可以通過Thread.currentThread()方法獲取。
- yield()方法放棄線程持有的CPU資源,將其讓給其他任務(wù)去占用CPU執(zhí)行時(shí)間。但放棄的時(shí)間不確定,有可能剛剛放棄,馬上又獲得CPU時(shí)間片。
- wait()方法是當(dāng)前執(zhí)行代碼的線程進(jìn)行等待,將當(dāng)前線程放入預(yù)執(zhí)行隊(duì)列,并在wait()所在的代碼處停止執(zhí)行,知道接到通知或者被中斷為止。該方法可以使得調(diào)用該方法的線程釋放共享資源的鎖, 然后從運(yùn)行狀態(tài)退出,進(jìn)入等待隊(duì)列,直到再次被喚醒。該方法只能在同步代碼塊里調(diào)用,否則會(huì)拋出IllegalMonitorStateException異常。wait(long millis)方法等待某一段時(shí)間內(nèi)是否有線程對(duì)鎖進(jìn)行喚醒,如果超過了這個(gè)時(shí)間則自動(dòng)喚醒。
- notify()方法用來通知那些可能等待該對(duì)象的對(duì)象鎖的其他線程,該方法可以隨機(jī)喚醒等待隊(duì)列中等同一共享資源的一個(gè)線程,并使該線程退出等待隊(duì)列,進(jìn)入可運(yùn)行狀態(tài)。
- notifyAll()方法可以是所有正在等待隊(duì)列中等待同一共享資源的全部線程從等待狀態(tài)退出,進(jìn)入可運(yùn)行狀態(tài),一般會(huì)是優(yōu)先級(jí)高的線程先執(zhí)行,但是根據(jù)虛擬機(jī)的實(shí)現(xiàn)不同,也有可能是隨機(jī)執(zhí)行。
- join()方法可以讓調(diào)用它的線程正常執(zhí)行完成后,再去執(zhí)行該線程后面的代碼,它具有讓線程排隊(duì)的作用。