多線程中的問題和非同步消息處理機制
來自專欄 Android初學記錄
在子線程中更新UI
前面反覆提到過Android不允許在子線程中進行任何的UI操作,這是由於Android的UI是線程不安全的,必須在主線程中進行。不妨實驗一下,在一個AndroidThreadTest裡面:
我們希望在button按下之後能夠更換TextView的內容,點擊事件我們放在一個子線程中,看一下究竟會發生什麼:
結果如下:
雖然在程序崩潰前最後一秒看到了變成了777,但是終究還是崩潰了。logcat裡面也看到錯誤信息:Only the original thread!
確實沒法在子線程中進行UI操作,但是有時候在子線程中執行一些耗時任務比如網路請求,然後我們再根據結果要更新UI控制項,這種情況挺常見的。Android提供了非同步消息處理機制,可以解決這個問題。
先定義了一個整型常量UPDATE_TEXT,然後新增了一個Handler對象,重寫超類的handleMessage方法,檢查msg的what欄位,如果等於UPDATE_TEXT,那就更新TextView。然後點擊事件裡面我們新建了一個Message對象,what欄位設置成了UPDATE_TEXT,然後調用handler的sendMessage方法,並且把剛剛構建的Message對象傳遞進去,會被發送。Handler會接受到這個對象,然後在handleMessage裡面處理它。handleMessage在主線程裡面,可以進行任何UI操作。以上就是Handler的簡單的原理。運行之後:
解析Handler非同步消息處理機制
非同步消息處理機制由四個部分組成:
- Message:Message是在線程之間傳遞的消息,它的內部的欄位可以攜帶少量信息,我們這裡就用到了它的what,常用的有:
2.Handler:用於send和handle消息,分別在sendMessage和handleMessage方法中完成。
3.MessageQueue:消息隊列,Handler發送的消息會存在其中,等待被處理。每個線程中只有一個MessageQueue對象。
4.Looper:調用Looper中的loop()方法之後,會進入循環中,每當發現MessageQueue中存在一個Message,就取出傳遞給Handler的handleMessage中,每個線程中只有一個Looper對象。
我們就可以藉助非同步消息處理機制,在主線程的handleMessage中進行UI操作。之前用的runOnUiThread其實是一個非同步消息處理機制的介面封裝,背後的原理就是這裡介紹的。
AsyncTask
AsyncTask是Android提供的封裝好的基於非同步消息處理機制的進行UI操作的工具。它是一個抽象類,基本用法是創建一個子類繼承它,繼承時候需要指定3個泛型參數:
- Params。執行AsyncTask時需要傳入的參數,在後台任務中使用。
- Progress。後台執行任務需要在界面上顯示進度,使用這個參數指定的泛型作為單位。
- Result。任務完畢後,執行結果的返回類型。
class DownloadTask extends AsyncTask<Void, Integer, Boolean>{ ...}
這個例子就表示執行AsyncTask時候不需要傳入任何參數給後台任務。進度顯示的單位是整型,同時結果的返回類型為布爾型。具體的操作要涉及到一些方法的重寫,主要有四個,我們直接通過一個簡單的DownloadTask示例來了解一下(下面是比較不偽的偽代碼。。部分方法我們並沒有實現,只是為了了解這四個方法)
簡單來說就是onPreExecute()是準備工作進行一些初始化,doInBackground()是在後台執行具體的耗時任務,並且返回任務的執行結果(參數類型為泛型第一個,返回類型為泛型的第三個),而onProgressUpdate()會在publishProgress方法被調用之後調用,在其中進行UI操作(接收的參數類型為泛型第二個),而最後的onPostExecute()方法進行一些善後收尾工作。
啟動這個任務,使用:
new DownloadTask().execute();
和Handler相比,確實更為簡單了一些。
推薦閱讀:
※學習就有收穫
※無量壽經科註第四回學習班【第80集】
※葛劍雄:得天下與治天下———天益:學習型社會領航者
※學習哲學
※利為匯_SEO_培訓班_學習_百善孝為先★武夷山seo