為什么我們需要使用多線程
提高用戶體驗
避免ANR(Application is not responding)
上面這兩個原因其實也是因果關系,因為會出現ANR,所以會導致用戶體驗很差
詳解ANR
安卓的main線程負責UI的繪制,為了防止應用程序反應較慢導致系統無法正常用運行做如下處理:
- 當用戶輸入時間在5秒內無法得到響應,那么系統會彈出ANR對話框
- BreadcastReciever 超過10秒沒執行完也會彈出ANR對話
所以事件處理的原則:
所有可能耗時的操作都放到其他線程去處理
為什么要在android中使用多線程
異步操作
應用中有些情況下并不一定需要同步阻塞去等待返回結果,可以通過多線程來實現異步
舉例說明:假如某個Activity需要從云端獲取一些圖片,加載圖片比較耗時,是一個耗時的操作,這時需要使用異步加載,加載完成一個圖片刷新一個
主線程Activity與子線程
- 默認啟動的第一個Activity成為主線程
- 由此Activity創建的線程(子線程)無法對主線程控制的內容進行修改,如果強行修改,會報以下錯誤:
Only the original thread that created a view hierarchy can touch its views. - 所以只有UI線程才能更新UI
所以怎么解決跨線程更新UI呢?
- 方式一:其他線程委托UI線程更新UI
- 方式二:通過Hnadler發送Message給UI線程,令UI線程根據Message消息更新UI
- 方式三:使用Android提供的AsyncTask
通常情況下,我們會使用方式二和方式三來解決更新UI的問題
但是Thread + Handler 是有一定的缺陷的
- 線程的開銷較大,如果每個任務都要創建一個線程,那么程序的效率要低很多。
- 線程無法管理,匿名線程創建并啟動后就不受程序的控制了,如果有很多個請求發送,那么就會啟動非常多的線程,系統將不堪重負。
- 另外,在新線程中更新UI還必須要引入handler,這讓代碼看上去非常臃腫。
所以這里就引入了AsyncTask
AsyncTask的特點:
在任務在主UI線程之外運行,而回調方法是在主UI線程中,這就有 效地避免了使用Handler帶來的麻煩。
AsyncTask定義了三種泛型類型:
- Params : 啟動任務執行的輸入參數。
- Progress : 后臺任務執行的百分比。
- Result : 后臺執行任務返回的結果。
使用AsyncTask簡化多線程開發:
AsyncTask專門用于完成非UI線程更新UI的任務
本質上也是開啟新線程執行耗時操作,然后將結果發送給UI線程 優點:簡化代碼,減少編寫線程間通信代碼這一繁瑣且易出錯的過程
操作要點:
AsyncTask為抽象類,必須先子類化
- onPreExecute():開始執行前的準備工作
- doInBackground(Params ...):開始執行后臺處理,并調用publishProgress(Progress )方法來更新實時的任務進度
- onProgressUpdate(Progress ...):在publishProgress()方法被調用
后,UI線程將調 用這個方法從而在界面上展示任務的進展情況 - onPostExecute(Result...):執行完成后的操作,傳送結果給UI線程
幾條必須遵守和記住的原則:
- Task的實例必須在UI 線程中創建
- execute方法必須在UI 線程中調用
- 不要手動的調用onPreExecute(),
onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...) 這幾個方法,它們需要在UI線程中實例化這個task來調用。 - 該task只能被執行一次,否則多次調用時將會出現異常
doInBackground方法和onPostExecute的參數必須對應,這兩個參數在AsyncTask聲 明的泛型參數列表中指定,第一個為doInBackground接受的參數,第二個為顯示進度 的參數,第三個為doInBackground返回和onPostExecute傳入的參數。
構造函數解讀
private class task extends AsyncTask<String, String, String>
AsyncTask<>的參數類型由用戶設定,這里設為三個String
- 第一個String代表輸入到任務的參數類型,也即是doInBackground()的參數類型
- 第二個String代表處理過程中的參數類型,也就是doInBackground()執行過程中的產出 參數類型,通過publishProgress()發消息,傳遞給onProgressUpdate()一般用來更新界 面
- 第三個String代表任務結束的產出類型,也就是doInBackground()的返回值類型,和 onPostExecute()的參數類型