當(dāng)某個(gè)應(yīng)用組件啟動(dòng)且該應(yīng)用沒(méi)有運(yùn)行其他任何組件時(shí)蒂培,Android 系統(tǒng)會(huì)使用單個(gè)執(zhí)行線(xiàn)程為應(yīng)用啟動(dòng)新的 Linux 進(jìn)程戏羽。默認(rèn)情況下,同一應(yīng)用的所有組件在相同的進(jìn)程和線(xiàn)程(稱(chēng)為“主”線(xiàn)程)中運(yùn)行绒极。 如果某個(gè)應(yīng)用組件啟動(dòng)且該應(yīng)用已存在進(jìn)程(因?yàn)榇嬖谠搼?yīng)用的其他組件)与柑,則該組件會(huì)在此進(jìn)程內(nèi)啟動(dòng)并使用相同的執(zhí)行線(xiàn)程。 但是芦缰,您可以安排應(yīng)用中的其他組件在單獨(dú)的進(jìn)程中運(yùn)行企巢,并為任何進(jìn)程創(chuàng)建額外的線(xiàn)程。
應(yīng)用啟動(dòng)時(shí)让蕾,系統(tǒng)會(huì)為應(yīng)用創(chuàng)建一個(gè)名為“主線(xiàn)程”的執(zhí)行線(xiàn)程浪规。 此線(xiàn)程非常重要,因?yàn)樗?fù)責(zé)將事件分派給相應(yīng)的用戶(hù)界面小部件探孝,其中包括繪圖事件罗丰。 此外,它也是應(yīng)用與 Android UI 工具包組件(來(lái)自 android.widget 和 android.view軟件包的組件)進(jìn)行交互的線(xiàn)程再姑。因此萌抵,主線(xiàn)程有時(shí)也稱(chēng)為 UI 線(xiàn)程。
系統(tǒng)不會(huì)為每個(gè)組件實(shí)例創(chuàng)建單獨(dú)的線(xiàn)程元镀。運(yùn)行于同一進(jìn)程的所有組件均在 UI 線(xiàn)程中實(shí)例化绍填,并且對(duì)每個(gè)組件的系統(tǒng)調(diào)用均由該線(xiàn)程進(jìn)行分派。 因此栖疑,響應(yīng)系統(tǒng)回調(diào)的方法始終在進(jìn)程的 UI 線(xiàn)程中運(yùn)行讨永。
在應(yīng)用執(zhí)行繁重的任務(wù)以響應(yīng)用戶(hù)交互時(shí),除非正確實(shí)現(xiàn)應(yīng)用遇革,否則這種單線(xiàn)程模式可能會(huì)導(dǎo)致性能低下卿闹。
具體地講,如果 UI 線(xiàn)程需要處理所有任務(wù)萝快,則執(zhí)行耗時(shí)很長(zhǎng)的操作(例如锻霎,網(wǎng)絡(luò)訪(fǎng)問(wèn)或數(shù)據(jù)庫(kù)查詢(xún))將會(huì)阻塞整個(gè) UI。 一旦線(xiàn)程被阻塞揪漩,將無(wú)法分派任何事件旋恼,包括繪圖事件。 從用戶(hù)的角度來(lái)看奄容,應(yīng)用顯示為掛起冰更。 更糟糕的是,如果 UI 線(xiàn)程被阻塞超過(guò)幾秒鐘時(shí)間(目前大約是 5 秒鐘)昂勒,用戶(hù)就會(huì)看到一個(gè)讓人厭煩的“應(yīng)用無(wú)響應(yīng)”(ANR) 對(duì)話(huà)框蜀细。如果引起用戶(hù)不滿(mǎn),他們可能就會(huì)決定退出并卸載此應(yīng)用戈盈。
此外奠衔,Android UI 工具包并非線(xiàn)程安全工具包。因此,您不得通過(guò)工作線(xiàn)程操縱 UI涣觉,而只能通過(guò) UI 線(xiàn)程操縱用戶(hù)界面痴荐。 因此,Android 的單線(xiàn)程模式必須遵守兩條規(guī)則:
- 不要阻塞 UI 線(xiàn)程
- 不要在 UI 線(xiàn)程之外訪(fǎng)問(wèn) Android UI 工具包
所以官册,如果要執(zhí)行耗時(shí)的操作生兆,我們需要另起線(xiàn)程執(zhí)行。
1. new Thread()
new Thread(new Runnable() {
@Override
public void run() {
}
}).start();
這是Android系統(tǒng)里開(kāi)線(xiàn)程最簡(jiǎn)單的方式膝宁,也只能應(yīng)用于最簡(jiǎn)單的場(chǎng)景鸦难,簡(jiǎn)單的好處卻伴隨不少的隱患。
這種方式僅僅是起動(dòng)了一個(gè)新的線(xiàn)程员淫,沒(méi)有任務(wù)的概念合蔽,不能做狀態(tài)的管理。start之后介返,run當(dāng)中的代碼就一定會(huì)執(zhí)行到底拴事,無(wú)法中途取消。Runnable作為匿名內(nèi)部類(lèi)還持有了外部類(lèi)的引用圣蝎,在線(xiàn)程退出之前刃宵,該引用會(huì)一直存在,阻礙外部類(lèi)對(duì)象被GC回收徘公,在一段時(shí)間內(nèi)造成內(nèi)存泄漏牲证。沒(méi)有線(xiàn)程切換的接口,要傳遞處理結(jié)果到UI線(xiàn)程的話(huà)关面,需要寫(xiě)額外的線(xiàn)程切換代碼坦袍。
如果從UI線(xiàn)程啟動(dòng),則該線(xiàn)程優(yōu)先級(jí)默認(rèn)為Default等太,歸于default cgroup捂齐,會(huì)平等的和UI線(xiàn)程爭(zhēng)奪CPU資源。在對(duì)UI性能要求高的場(chǎng)景下要記得Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
澈驼。雖說(shuō)處于background group的線(xiàn)程總共只能爭(zhēng)取到5~10%的CPU資源辛燥,但這對(duì)絕大部分的后臺(tái)任務(wù)處理都綽綽有余了筛武,1ms和10ms對(duì)用戶(hù)來(lái)說(shuō)缝其,都是快到無(wú)法感知,所以我們一般都偏向于在background group當(dāng)中執(zhí)行工作線(xiàn)程任務(wù)徘六。
2. HandlerThread
Android主線(xiàn)程包含一個(gè)消息隊(duì)列(MessageQueue)内边,該消息隊(duì)列里面可以存入一系列的Message或Runnable對(duì)象。通過(guò)一個(gè)Handler你可以往這個(gè)消息隊(duì)列發(fā)送Message或者Runnable對(duì)象待锈,并且處理這些對(duì)象漠其。每次你新創(chuàng)建一個(gè)Handle對(duì)象,它會(huì)綁定于創(chuàng)建它的線(xiàn)程(也就是UI線(xiàn)程)以及該線(xiàn)程的消息隊(duì)列,從這時(shí)起和屎,這個(gè)handler就會(huì)開(kāi)始把Message或Runnable對(duì)象傳遞到消息隊(duì)列中拴驮,并在它們出隊(duì)列的時(shí)候執(zhí)行它們。
- HandlerThread本質(zhì)是一個(gè)線(xiàn)程類(lèi)柴信,它繼承了Thread套啤;
- HandlerThread有自己的內(nèi)部Looper對(duì)象,可以進(jìn)行l(wèi)ooper循環(huán)随常;
- 通過(guò)獲取HandlerThread的looper對(duì)象傳遞給Handler對(duì)象潜沦,可以在handlerMessage方法中執(zhí)行異步任務(wù);
- 優(yōu)點(diǎn)是不會(huì)有堵塞绪氛,減少對(duì)性能的消耗唆鸡,缺點(diǎn)是不能同時(shí)進(jìn)行多任務(wù)的處理,需要等待進(jìn)行處理枣察,處理效率較低争占;
- HandlerThread是一個(gè)串行隊(duì)列,背后只有一個(gè)線(xiàn)程序目。
Handler可以把一個(gè)Message對(duì)象或者Runnable對(duì)象壓入到消息隊(duì)列中燃乍,進(jìn)而在UI線(xiàn)程中獲取Message或者執(zhí)行Runnable對(duì)象,Handler把壓入消息隊(duì)列有兩類(lèi)方式宛琅,Post和sendMessage刻蟹。
- 對(duì)于Handler的Post方式來(lái)說(shuō),它會(huì)傳遞一個(gè)Runnable對(duì)象到消息隊(duì)列中嘿辟,在這個(gè)Runnable對(duì)象中舆瘪,重寫(xiě)run()方法。一般在這個(gè)run()方法中寫(xiě)入需要在UI線(xiàn)程上的操作红伦。
- Handler如果使用sendMessage的方式把消息入隊(duì)到消息隊(duì)列中英古,需要傳遞一個(gè)Message對(duì)象,而在Handler中昙读,需要重寫(xiě)handleMessage()方法召调,用于獲取工作線(xiàn)程傳遞過(guò)來(lái)的消息,此方法運(yùn)行在UI線(xiàn)程上蛮浑。Message是一個(gè)final類(lèi)唠叛,所以不可被繼承。
HandlerThread背后只有一個(gè)線(xiàn)程沮稚,所以任務(wù)是串行執(zhí)行的艺沼。串行相對(duì)于并行來(lái)說(shuō)更安全,各任務(wù)之間不會(huì)存在多線(xiàn)程安全問(wèn)題蕴掏。
HandlerThread在實(shí)用性障般,靈活度调鲸,安全性上都有更好的表現(xiàn),但是需要寫(xiě)的代碼較多挽荡。
3. AsyncTask
public class MyAsyncTask extends AsyncTask {
@Override
protected Object doInBackground(Object[] params) {
return null;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
}
}
AsyncTask是android提供的輕量級(jí)的異步類(lèi),可以直接繼承AsyncTask藐石,在類(lèi)中實(shí)現(xiàn)異步操作,并提供接口反饋當(dāng)前異步執(zhí)行的程度(可以通過(guò)接口實(shí)現(xiàn)UI進(jìn)度更新)定拟,最后反饋執(zhí)行的結(jié)果給UI主線(xiàn)程贯钩。
AsyncTask的本質(zhì)是一個(gè)靜態(tài)的線(xiàn)程池,AsyncTask派生出的子類(lèi)可以實(shí)現(xiàn)不同的異步任務(wù)办素,這些任務(wù)都是提交到靜態(tài)的線(xiàn)程池中執(zhí)行的角雷。線(xiàn)程池中的工作線(xiàn)程執(zhí)行doInBackground(mParams)
方法執(zhí)行異步任務(wù)。當(dāng)任務(wù)狀態(tài)改變之后性穿,工作線(xiàn)程會(huì)向UI線(xiàn)程發(fā)送消息勺三,AsyncTask的內(nèi)部的IntentHandler響應(yīng)這些消息,并調(diào)用相關(guān)的回調(diào)函數(shù)需曾。
AsyncTask通過(guò)一個(gè)阻塞隊(duì)列BlockingQuery<Runnable>
存儲(chǔ)待執(zhí)行的任務(wù)吗坚,利用靜態(tài)線(xiàn)程池THREAD_POOL_EXECUTOR
提供一定數(shù)量的線(xiàn)程,默認(rèn)128個(gè)呆万。在Android 3.0以前商源,默認(rèn)采取的是并行任務(wù)執(zhí)行器,3.0以后改成了默認(rèn)采用串行任務(wù)執(zhí)行器谋减,通過(guò)靜態(tài)串行任務(wù)執(zhí)行器SERIAL_EXECUTOR
控制任務(wù)串行執(zhí)行牡彻,循環(huán)取出任務(wù)交給THREAD_POOL_EXECUTOR
中的線(xiàn)程執(zhí)行,執(zhí)行完一個(gè)出爹,再執(zhí)行下一個(gè)庄吼。
AsyncTask的幾處回調(diào)都給了我們機(jī)會(huì)去中斷任務(wù),在任務(wù)狀態(tài)的管理上較之Thread()
方式更為靈活严就。值得注意的是AsyncTask的cancel()
方法并不會(huì)終止任務(wù)的執(zhí)行总寻,開(kāi)發(fā)者需要自己去檢查cancel的狀態(tài)值來(lái)決定是否中止任務(wù)。
AsyncTask也有隱式的持有外部類(lèi)對(duì)象引用的問(wèn)題梢为,需要特別注意防止出現(xiàn)意外的內(nèi)存泄漏渐行。AsyncTask由于在不同的系統(tǒng)版本上串行與并行的執(zhí)行行為不一致,被不少開(kāi)發(fā)者所詬病铸董,這確實(shí)是硬傷祟印,絕大部分的多線(xiàn)程場(chǎng)景都需要明確任務(wù)是串行還是并行。
AsyncTask適合單個(gè)異步任務(wù)的處理袒炉。
4. ThreadPoolExecutor
線(xiàn)程池是指在初始化一個(gè)多線(xiàn)程應(yīng)用程序過(guò)程中創(chuàng)建的一個(gè)線(xiàn)程集合旁理。線(xiàn)程池在任務(wù)未到達(dá)前,會(huì)創(chuàng)建一定數(shù)量的線(xiàn)程放在空閑隊(duì)列中我磁。這些線(xiàn)程都是處于睡眠狀態(tài)的孽文,即均為啟動(dòng)但不消耗CPU,只占用較小的內(nèi)存空間夺艰。當(dāng)請(qǐng)求到來(lái)時(shí)芋哭,線(xiàn)程池給這次請(qǐng)求分配一個(gè)空閑線(xiàn)程,把請(qǐng)求傳入此線(xiàn)程中運(yùn)行郁副,進(jìn)行處理减牺。當(dāng)預(yù)制線(xiàn)程不夠時(shí),線(xiàn)程池可以自由創(chuàng)建一定數(shù)量的新線(xiàn)程用于處理更多的請(qǐng)求存谎。如果線(xiàn)程池中的最大線(xiàn)程數(shù)使用滿(mǎn)了拔疚,則會(huì)拋出異常,拒絕請(qǐng)求既荚。 當(dāng)系統(tǒng)較為空閑時(shí)稚失,也可以通過(guò)移除一部分一直處于停用狀態(tài)的線(xiàn)程。線(xiàn)程池中的每個(gè)線(xiàn)程都有可能被分配為多個(gè)任務(wù)恰聘,一旦任務(wù)完成句各,線(xiàn)程回到線(xiàn)程池等待下一次任務(wù)分配。
ThreadPoolExecutor提供了一組線(xiàn)程池晴叨,可以管理多個(gè)線(xiàn)程并行執(zhí)行凿宾。這樣一方面減少了每個(gè)并行任務(wù)獨(dú)自建立線(xiàn)程的開(kāi)銷(xiāo),另一方面可以管理多個(gè)并發(fā)線(xiàn)程的公共資源兼蕊,從而提高了多線(xiàn)程的效率初厚。所以ThreadPoolExecutor比較適合一組任務(wù)的執(zhí)行。Executors利用工廠(chǎng)模式對(duì)ThreadPoolExecutor進(jìn)行了封裝孙技,使用起來(lái)更加方便惧所。
1. Executors.newFixedThreadPool()
創(chuàng)建一個(gè)定長(zhǎng)的線(xiàn)程池,每提交一個(gè)任務(wù)就創(chuàng)建一個(gè)線(xiàn)程绪杏,直到達(dá)到池的最大長(zhǎng)度下愈,這時(shí)線(xiàn)程池會(huì)保持長(zhǎng)度不再變化
2. Executors.newCachedThreadPool()
創(chuàng)建一個(gè)可緩存的線(xiàn)程池,如果當(dāng)前線(xiàn)程池的長(zhǎng)度超過(guò)了處理的需要時(shí)蕾久,它可以靈活的回收空閑的線(xiàn)程势似,當(dāng)需要增加時(shí),它可以靈活的添加新的線(xiàn)程僧著,而不會(huì)對(duì)池的長(zhǎng)度作任何限制
3. Executors.newScheduledThreadPool()
創(chuàng)建一個(gè)定長(zhǎng)的線(xiàn)程池履因,而且支持定時(shí)的以及周期性的任務(wù)執(zhí)行,類(lèi)似于Timer
4. Executors.newSingleThreadExecutor()
創(chuàng)建一個(gè)單線(xiàn)程化的executor盹愚,它只創(chuàng)建唯一的worker線(xiàn)程來(lái)執(zhí)行任務(wù)
線(xiàn)程池可以避免線(xiàn)程的頻繁創(chuàng)建和銷(xiāo)毀栅迄,顯然性能更好,但線(xiàn)程池并發(fā)的特性往往也是疑難雜癥的源頭皆怕,是代碼降級(jí)和失控的開(kāi)始毅舆。多線(xiàn)程并行導(dǎo)致的bug往往是偶現(xiàn)的西篓,不方便調(diào)試,一旦出現(xiàn)就會(huì)耗掉大量的開(kāi)發(fā)精力憋活。
ThreadPool較之HandlerThread在處理多任務(wù)上有更高的靈活性岂津,但也帶來(lái)了更大的復(fù)雜度和不確定性。
5. IntentService
IntentService又是另一種開(kāi)工作線(xiàn)程的方式悦即,從名字就可以看出這個(gè)工作線(xiàn)程會(huì)帶有service的屬性吮成。IntentService是繼承并處理異步請(qǐng)求的一個(gè)類(lèi)。和AsyncTask不同辜梳,沒(méi)有和UI線(xiàn)程的交互粱甫,也不像HandlerThread的工作線(xiàn)程會(huì)一直存活。IntentService背后其實(shí)也有一個(gè)HandlerThread來(lái)串行的處理Message Queue作瞄。只不過(guò)在所有的Message處理完畢之后茶宵,工作線(xiàn)程會(huì)自動(dòng)結(jié)束。所以可以把IntentService看做是Service和HandlerThread的結(jié)合體粉洼,適合需要在工作線(xiàn)程處理UI無(wú)關(guān)任務(wù)的場(chǎng)景节预。
在IntentService內(nèi)有一個(gè)工作線(xiàn)程來(lái)處理耗時(shí)操作,啟動(dòng)IntentService的方式和啟動(dòng)傳統(tǒng)的Service一樣属韧,同時(shí)安拟,當(dāng)任務(wù)執(zhí)行完后,IntentService會(huì)自動(dòng)停止宵喂,而不需要我們手動(dòng)去控制糠赦。
另外,可以啟動(dòng)IntentService多次锅棕,而每一個(gè)耗時(shí)操作會(huì)以工作隊(duì)列的方式在IntentService的onHandlerIntent回調(diào)方法中執(zhí)行拙泽,并且,每次只會(huì)執(zhí)行一個(gè)工作線(xiàn)程裸燎,執(zhí)行完后第一個(gè)再執(zhí)行第二個(gè)顾瞻。
- 其本質(zhì)是一個(gè)特殊的Service,繼承自Service并且本身就是一個(gè)抽象類(lèi)德绿;
- 它內(nèi)部通過(guò)HandlerThread和Handlershi'xian