Android多線程
一個Android的應用程序運行在一個獨立的進程中,運行在一個獨立的虛擬機(dvk)上。 (進程名為包名)
Android應用程序開啟后,默認開啟一個主線程(UI線程)
Activity,Service,BroadcastReceiver組件運行在主線程中
Android應用程序退出后,保留空UI線程,可以加快應用程序啟動速度用戶不能在UI主線程中做耗時的操作,一旦該操作超過5s,應用程序拋一個ANR異常(Application not respond)。
如何避免ANR錯誤?
將耗時的操作放入子線程中。(耗時的操作包括:長時間的休眠,計數(shù),聯(lián)網(wǎng),復雜的運算。)只有主線程才能操作Widget控件。
如果在子線程中操作Widget控件,系統(tǒng)拋出CalledFromWrongThreadException異常。系統(tǒng)為什么要這么做?
避免出現(xiàn)同步問題。
Handler機制。
Goolge為什么設計這套機制?
主要是為了解決非UI線程中不能更新Widget控件的問題Handler機制剖析
子線程發(fā)送消息給底層的消息隊列。
handler.sendMessage(msg)
主線程查詢消息隊列,處理消息對象。
handlerMessage(msg)
MessageQueue 消息隊列
負責存儲消息對象Looper
給UI線程安排代碼,一個UI線程只能有一個Looper對象,否則多個Looper對象都在UI線程上安排代碼,解決沖突就是個大問題。 Looper對象會線性安排在UI線程上執(zhí)行的代碼,它通過一個隊列管理各個Handler對象提交的代碼。Message消息對象
//從消息池中獲取消息對象
Message msg = handler.obtainMessage();
//在消息對象上綁定int類型數(shù)據(jù)
msg.arg1 = count;
//在消息對象上綁定其它類型數(shù)據(jù)
msg.setData(Bundle); //Bundler為數(shù)據(jù)集(類似于HashMap容器)

- 向消息隊列發(fā)送消息,1000毫秒后執(zhí)行Runnable對象中的代碼。
myHandler.postDelayed(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
Log.e("Test", "thread name = "+Thread.currentThread().getName());
}
}, 1000);
異步任務(AsyncTask)
概念
封裝多線程和Handler機制。給用戶提供重寫接口的方式,不需要用戶手動創(chuàng)建子線程和Handler對象。異步任務的優(yōu)點
Handler模式需要為每一個任務創(chuàng)建一個新的線程,任務完成后通過Handler實例向UI主線程發(fā)送消息,完成界面的更新,這種方式對于整個過程的控制比較精細,但是也有缺點,代碼臃腫,在多個任務同時執(zhí)行時,不易對線程進行精確的控制。為了簡化操作,Android1.5提供了一個工具類AsyncTask,它是創(chuàng)建異步任務變的更加簡單,不再需要編寫任務線程和Handler實例就可完成任務。異步任務的局限性
多個異步任務不能同時執(zhí)行,在某個時間內,只能執(zhí)行一個異步任務。執(zhí)行異步任務的步驟:
- execute(Params... params),執(zhí)行一個異步任務,需要我們在代碼中調用此方法,觸發(fā)異步任務的執(zhí)行。
- onPreExecute(),在execute(Params... params)被調用后立即執(zhí)行,一般用來在執(zhí)行后臺任務前對UI做一些標記。
- doInBackground(Params... params),在onPreExecute()完成后立即執(zhí)行,用于執(zhí)行較為費時的操作,此方法將接收輸入?yún)?shù)和返回計算結果。在執(zhí)行過程中可以調publishProgress(Progress... values)來更新進度信息。
- onProgressUpdate(Progress... values),在調用publishProgress(Progress... values)時,此方法被執(zhí)行,直接將進度信息更新到UI組件上。
- onPostExecute(Result result),當后臺操作結束時,此方法將會被調用,計算結果將做為參數(shù)傳遞到此方法中,直接將結果顯示到UI組件上。
- 取消異步任務
/**
*cancel(true) 取消當前的異步任務,傳入的true,表示當中斷異步任務時繼續(xù)已經(jīng)運行的線程的操作,
*但是為了線程的安全一般為讓它繼續(xù)設為true
**/
mTask.cancel(true);
/**
* 但是重新運行后會發(fā)現(xiàn)還是不能起到效果,
* 注意:這是因為cancel方法只是發(fā)出一個請求取消異步任務的信號,
* 將對應當前的異步任務標記為CANCEL狀態(tài),而并不是真正取消線程的執(zhí)行,
* 而此時異步任務中的線程仍然在執(zhí)行并沒有結束
* 所以效果依然是這樣的,并且在java中我們是無法直接暴力將一個線程給停止掉
* 既然我們知道無法去取消一個已經(jīng)正在運行的線程,但是我們如何去解決這個BUG呢?
* 在異步任務中還給我們提供一個isCanceled的回調方法,也就是當我已經(jīng)給當前的異步任務
* 調用了cancel(true)方法,發(fā)出一個請求取消異步任務的信號,那么此時的isCanceled的回調方法
* 會直接返回一個true,那么我們就可以通過判斷當前異步任務isCanceled是否為true,來終止
* 線程中的操作而不是去終止線程,從而達到了界面顯示好像線程中的操作被終止了,而實際上
* 該線程依然在運行
* */
- 注意
- 異步任務對象只能執(zhí)行一次。
- 異步任務對象必須在UI主線程中創(chuàng)建。execute(Params... params)方法必須在UI線程中調用。
- 不要手動調用onPreExecute(),doInBackground(Params... params),onProgressUpdate(Progress... values),onPostExecute(Result result)這幾個方法。
- 不能在doInBackground(Params... params)中更改UI組件的信息。