Android多線程
一個(gè)Android的應(yīng)用程序運(yùn)行在一個(gè)獨(dú)立的進(jìn)程中碍讨,運(yùn)行在一個(gè)獨(dú)立的虛擬機(jī)(dvk)上。 (進(jìn)程名為包名)
Android應(yīng)用程序開啟后鸟顺,默認(rèn)開啟一個(gè)主線程(UI線程)
Activity次坡,Service疼约,BroadcastReceiver組件運(yùn)行在主線程中
Android應(yīng)用程序退出后溺拱,保留空UI線程逃贝,可以加快應(yīng)用程序啟動(dòng)速度用戶不能在UI主線程中做耗時(shí)的操作,一旦該操作超過5s迫摔,應(yīng)用程序拋一個(gè)ANR異常(Application not respond)沐扳。
如何避免ANR錯(cuò)誤?
將耗時(shí)的操作放入子線程中句占。(耗時(shí)的操作包括:長(zhǎng)時(shí)間的休眠沪摄,計(jì)數(shù),聯(lián)網(wǎng)辖众,復(fù)雜的運(yùn)算卓起。)只有主線程才能操作Widget控件。
如果在子線程中操作Widget控件凹炸,系統(tǒng)拋出CalledFromWrongThreadException異常。系統(tǒng)為什么要這么做昼弟?
避免出現(xiàn)同步問題啤它。
Handler機(jī)制。
Goolge為什么設(shè)計(jì)這套機(jī)制舱痘?
主要是為了解決非UI線程中不能更新Widget控件的問題Handler機(jī)制剖析
子線程發(fā)送消息給底層的消息隊(duì)列变骡。
handler.sendMessage(msg)
主線程查詢消息隊(duì)列,處理消息對(duì)象芭逝。
handlerMessage(msg)
MessageQueue 消息隊(duì)列
負(fù)責(zé)存儲(chǔ)消息對(duì)象Looper
給UI線程安排代碼塌碌,一個(gè)UI線程只能有一個(gè)Looper對(duì)象,否則多個(gè)Looper對(duì)象都在UI線程上安排代碼旬盯,解決沖突就是個(gè)大問題台妆。 Looper對(duì)象會(huì)線性安排在UI線程上執(zhí)行的代碼,它通過一個(gè)隊(duì)列管理各個(gè)Handler對(duì)象提交的代碼胖翰。Message消息對(duì)象
//從消息池中獲取消息對(duì)象
Message msg = handler.obtainMessage();
//在消息對(duì)象上綁定int類型數(shù)據(jù)
msg.arg1 = count;
//在消息對(duì)象上綁定其它類型數(shù)據(jù)
msg.setData(Bundle); //Bundler為數(shù)據(jù)集(類似于HashMap容器)
- 向消息隊(duì)列發(fā)送消息接剩,1000毫秒后執(zhí)行Runnable對(duì)象中的代碼。
myHandler.postDelayed(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
Log.e("Test", "thread name = "+Thread.currentThread().getName());
}
}萨咳, 1000);
異步任務(wù)(AsyncTask)
概念
封裝多線程和Handler機(jī)制懊缺。給用戶提供重寫接口的方式,不需要用戶手動(dòng)創(chuàng)建子線程和Handler對(duì)象培他。異步任務(wù)的優(yōu)點(diǎn)
Handler模式需要為每一個(gè)任務(wù)創(chuàng)建一個(gè)新的線程鹃两,任務(wù)完成后通過Handler實(shí)例向UI主線程發(fā)送消息遗座,完成界面的更新,這種方式對(duì)于整個(gè)過程的控制比較精細(xì)俊扳,但是也有缺點(diǎn)途蒋,代碼臃腫,在多個(gè)任務(wù)同時(shí)執(zhí)行時(shí)拣度,不易對(duì)線程進(jìn)行精確的控制碎绎。為了簡(jiǎn)化操作,Android1.5提供了一個(gè)工具類AsyncTask抗果,它是創(chuàng)建異步任務(wù)變的更加簡(jiǎn)單筋帖,不再需要編寫任務(wù)線程和Handler實(shí)例就可完成任務(wù)。異步任務(wù)的局限性
多個(gè)異步任務(wù)不能同時(shí)執(zhí)行冤馏,在某個(gè)時(shí)間內(nèi)日麸,只能執(zhí)行一個(gè)異步任務(wù)。執(zhí)行異步任務(wù)的步驟:
- execute(Params... params)逮光,執(zhí)行一個(gè)異步任務(wù)代箭,需要我們?cè)诖a中調(diào)用此方法,觸發(fā)異步任務(wù)的執(zhí)行涕刚。
- onPreExecute()嗡综,在execute(Params... params)被調(diào)用后立即執(zhí)行,一般用來在執(zhí)行后臺(tái)任務(wù)前對(duì)UI做一些標(biāo)記杜漠。
- doInBackground(Params... params)极景,在onPreExecute()完成后立即執(zhí)行,用于執(zhí)行較為費(fèi)時(shí)的操作驾茴,此方法將接收輸入?yún)?shù)和返回計(jì)算結(jié)果盼樟。在執(zhí)行過程中可以調(diào)publishProgress(Progress... values)來更新進(jìn)度信息。
- onProgressUpdate(Progress... values)锈至,在調(diào)用publishProgress(Progress... values)時(shí)晨缴,此方法被執(zhí)行,直接將進(jìn)度信息更新到UI組件上峡捡。
- onPostExecute(Result result)击碗,當(dāng)后臺(tái)操作結(jié)束時(shí),此方法將會(huì)被調(diào)用棋返,計(jì)算結(jié)果將做為參數(shù)傳遞到此方法中延都,直接將結(jié)果顯示到UI組件上。
- 取消異步任務(wù)
/**
*cancel(true) 取消當(dāng)前的異步任務(wù)睛竣,傳入的true,表示當(dāng)中斷異步任務(wù)時(shí)繼續(xù)已經(jīng)運(yùn)行的線程的操作晰房,
*但是為了線程的安全一般為讓它繼續(xù)設(shè)為true
**/
mTask.cancel(true);
/**
* 但是重新運(yùn)行后會(huì)發(fā)現(xiàn)還是不能起到效果,
* 注意:這是因?yàn)閏ancel方法只是發(fā)出一個(gè)請(qǐng)求取消異步任務(wù)的信號(hào),
* 將對(duì)應(yīng)當(dāng)前的異步任務(wù)標(biāo)記為CANCEL狀態(tài),而并不是真正取消線程的執(zhí)行殊者,
* 而此時(shí)異步任務(wù)中的線程仍然在執(zhí)行并沒有結(jié)束
* 所以效果依然是這樣的与境,并且在java中我們是無法直接暴力將一個(gè)線程給停止掉
* 既然我們知道無法去取消一個(gè)已經(jīng)正在運(yùn)行的線程,但是我們?nèi)绾稳ソ鉀Q這個(gè)BUG呢猖吴?
* 在異步任務(wù)中還給我們提供一個(gè)isCanceled的回調(diào)方法摔刁,也就是當(dāng)我已經(jīng)給當(dāng)前的異步任務(wù)
* 調(diào)用了cancel(true)方法,發(fā)出一個(gè)請(qǐng)求取消異步任務(wù)的信號(hào)海蔽,那么此時(shí)的isCanceled的回調(diào)方法
* 會(huì)直接返回一個(gè)true共屈,那么我們就可以通過判斷當(dāng)前異步任務(wù)isCanceled是否為true,來終止
* 線程中的操作而不是去終止線程党窜,從而達(dá)到了界面顯示好像線程中的操作被終止了拗引,而實(shí)際上
* 該線程依然在運(yùn)行
* */
- 注意
- 異步任務(wù)對(duì)象只能執(zhí)行一次。
- 異步任務(wù)對(duì)象必須在UI主線程中創(chuàng)建幌衣。execute(Params... params)方法必須在UI線程中調(diào)用矾削。
- 不要手動(dòng)調(diào)用onPreExecute(),doInBackground(Params... params)豁护,onProgressUpdate(Progress... values)哼凯,onPostExecute(Result result)這幾個(gè)方法。
- 不能在doInBackground(Params... params)中更改UI組件的信息楚里。