本文主要講解下AsyncTask的使用以及Handler的應用
首先,我們得明確下一個概念,什麼是UI線程。顧名思義,ui線程就是管理著使用者介面的那個線程!
android的ui線程操作並不是安全的,並且和使用者直接進行介面互動的操作都必須在ui線程中進行才可以。這種模式叫做單線程模式。
我們在單線程模式下編程一定要注意:不要阻塞ui線程、確保只在ui線程中訪問ui組件
當我們要執行一個複雜耗時的演算法並且最終要將計算結果反映到ui上時,我們會發現,我們根本沒辦法同時保證上面的兩點要求;我們肯定會想到開啟一個新的線程,讓這個複雜耗時的任務到後台去執行,但是執行完畢了呢?我們發現,我們無法再與ui進行互動了。
為瞭解決這種情況,android為我們提供了很多辦法。
1)、handler和message機制:通過顯示的拋出、捕獲訊息與ui進行互動;
2)、Activity.runOnUiThread(Runnable):如果當前線程為ui線程,則立即執行;否則,將參數中的線程操作放入到ui線程的事件隊列中,等待執行。
3)、View.post(Runnable):將操作放入到message隊列中,如果放入成功,該操作將會在ui線程中執行,並返回true,否則返回false
4)、View.postDelayed(Runnable, long)跟第三條基本一樣,只不過添加了一個延遲時間。
5)、android1.5以後為我們提供了一個工具類來搞定這個問題AsyncTask.
AsyncTask是抽象類別,定義了三種泛型型別 Params,Progress,Result。
Params 啟動任務執行的輸入參數,比如HTTP請求的URL
Progress 背景工作執行的百分比。
Result 後台執行任務最終返回的結果,比如String
用程式調用,開發人員需要做的就是實現這些方法。
1) 子類化AsyncTask
2) 實現AsyncTask中定義的下面一個或幾個方法
onPreExecute(),該方法將在執行實際的後台操作前被UI thread調用。可以在該方法中做一些準備工作,如在介面上顯示一個進度條。
doInBackground(Params…),將在onPreExecute 方法執行後馬上執行,該方法運行在後台線程中。這裡將主要負責執行那些很耗時的後台計算工作。可以調用 publishProgress方法來更新即時的任務進度。該方法是抽象方法,子類必須實現。
onProgressUpdate(Progress…),在publishProgress方法被調用後,UI thread將調用這個方法從而在介面上展示任務的進展情況,例如通過一個進度條進行展示。
onPostExecute(Result),在doInBackground 執行完成後,onPostExecute 方法將被UI thread調用,背景計算結果將通過該方法傳遞到UI thread.
為了正確的使用AsyncTask類,以下是幾條必須遵守的準則:
1) Task的執行個體必須在UI thread中建立
2) execute方法必須在UI thread中調用
3) 不要手動的調用onPreExecute(), onPostExecute(Result),doInBackground(Params…), onProgressUpdate(Progress…)這幾個方法
4) 該task只能被執行一次,否則多次調用時將會出現異常
package cn.com.chenzheng_java;</p><p>import android.os.AsyncTask;<br />/**<br /> *<br /> * @author chenzheng_java<br /> * @description 非同步任務AcyncTask樣本<br /> *<br /> */<br />public class MyAsyncTask extends AsyncTask<String, Integer, Object> {</p><p> /**<br /> * 該方法由ui線程進行調用,使用者可以在這裡盡情的訪問ui組件。<br /> * 很多時候,我們會在這裡顯示一個進度條啥的,以示後台正在<br /> * 執行某項功能。<br /> */<br /> @Override<br /> protected void onPreExecute() {<br /> super.onPreExecute();<br /> }</p><p> /**<br /> * 該方法由後台進程進行調用,進行主要的耗時的那些計算。<br /> * 該方法在onPreExecute方法之後進行調用。當然在執行過程中<br /> * 我們可以每隔多少秒就調用一次publishProgress方法,更新<br /> * 進度資訊<br /> */<br /> @Override<br /> protected Object doInBackground(String... params) {<br /> return null;<br /> }</p><p> /**<br /> * doInBackground中調用了publishProgress之後,ui線程就會<br /> * 調用該方法。你可以在這裡動態改變進度條的進度,讓使用者知道<br /> * 當前的進度。<br /> */<br /> @Override<br /> protected void onProgressUpdate(Integer... values) {<br /> super.onProgressUpdate(values);<br /> }</p><p> /**<br /> * 當doInBackground執行完畢之後,由ui線程調用。可以在這裡<br /> * 返回我們計算的最終結果給使用者。<br /> */<br /> @Override<br /> protected void onPostExecute(Object result) {<br /> super.onPostExecute(result);<br /> }<br />}
下面介紹最本質的多線程:hanlder和message機制:
為何需要多線程:
在日常應用中,我們通常需要處理一些“後台,使用者不可見”的操作,例如說,我們需要下載一個音樂,要是你的應用必須等使用者下載完成之後才可以進行別的操作,那肯定讓使用者非常的不爽。這時候,我們通常的做法是,讓這些操作去後台執行,然後等後台執行完畢之後,再給使用者彈出相應的提示資訊。這時候,我們就需要使用多線程機制,然後通過建立一個新的線程來執行這些操作。
明白了,實現需求,我們就準備著手實現了。但是,經過進一步的瞭解,我們悲劇的發現,android中的線程機制是,只能在UI線程中和使用者進行互動。當我們建立了一個新線程,執行了一些後台操作,執行完成之後,我們想要給使用者彈出對話方塊以確認,但是卻悲劇的發現,我們根本無法返回UI主線程了。
(說明:何為UI線程:UI線程就是你當前看到的這些互動介面所屬的線程)。
這時候,我們如果想要實現這些功能,我們就需要一個android為我們提供的handler和message機制。
先講解下編程機制:
我們通常在UI線程中建立一個handler,handler相當於一個處理器,它主要負責處理和綁定到該handler的線程中的message。每一個handler都必須關聯一個looper,並且兩者是一一對應的,注意,這點很重要哦!此外,looper負責從其內部的messageQueue中拿出一個個的message給handler進行處理。因為我們這裡handler是在UI線程中實現的,所以經過這麼一個handler、message機制,我們就可以回到UI線程中了。
何為handler:處理後台進程返回資料的工作人員。
何為message:後台進程返回的資料,裡面可以儲存bundle等資料格式
何為messageQueue:是線程對應looper的一部分,負責儲存從後台進程中拋回的和當前handler綁定的message,是一個隊列。
何為looper:looper相當於一個messageQueue的管理員,它會不停的迴圈的遍曆隊列,然後將合格message一個個的拿出來交給handler進行處理。
注意,handler是在UI線程中聲明的,如果我們直接用類似代碼執行一個線程的話,實際上並沒有建立一個新的線程,因為handler已經跟預設的UI線程中的looper綁定了。
如果有興趣的話,可以去看下Handler的預設空建構函式便知道原因了,裡面直接綁定了當前UI線程的looper。
下面給出一個比較簡單,並且實用的執行個體。
package cn.com.src;</p><p>import cn.com.chenzheng_java.utils.R;<br />import android.app.Activity;<br />import android.os.Bundle;<br />import android.os.Handler;<br />import android.os.HandlerThread;<br />import android.os.Looper;<br />import android.os.Message;<br />import android.util.Log;<br />import android.view.View;<br />import android.view.View.OnClickListener;<br />import android.widget.Button;</p><p>/**<br /> * @author chenzheng_java<br /> * handler和message測試案例<br /> */<br />public class HanlderMessageTest extends Activity implements OnClickListener{<br /> Button button ;<br /> MyHandler handler ;</p><p> @Override<br /> protected void onCreate(Bundle savedInstanceState) {<br /> super.onCreate(savedInstanceState);<br /> setContentView(R.layout.main);<br /> button = (Button) this.findViewById(R.id.button1);<br /> button.setOnClickListener(this);</p><p> }</p><p> // 聲明自己的handler<br /> private class MyHandler extends Handler{<br /> /**<br /> * 使用預設的建構函式,會將handler綁定當前UI線程的looper。<br /> * 如果想使用多線程這裡是不能使用預設的構造方法的。<br /> */<br /> public MyHandler() {<br /> super();<br /> }</p><p> public MyHandler(Looper looper){<br /> super(looper);<br /> }</p><p> // 處理具體的message,該方法由父類中進行繼承.<br /> @Override<br /> public void handleMessage(Message msg) {<br /> int whatNumber = msg.what;<br /> Bundle bundle = (Bundle)msg.obj;<br /> Log.i("what", whatNumber+"");<br /> Log.i("名稱", bundle.getString("name"));<br /> Log.i("性別", bundle.getString("sex"));<br /> Log.i("年齡", bundle.getString("age"));<br /> super.handleMessage(msg);<br /> }<br /> }</p><p> // 我自訂的任務,一般都會實現Runnable<br /> private class MyThread implements Runnable {<br /> /**<br /> * 該方法的內部進行具體的任務實現,比如 下載.<br /> * Message中包含著想和ui線程互動的資料,原則上,線上程內部是<br /> * 最好不要直接調用handler的。<br /> * */<br /> @Override<br /> public void run() {</p><p> try {<br /> Thread.sleep(6000);<br /> Message message = Message.obtain(handler);<br /> message.what = 10 ;<br /> Bundle bundle = new Bundle();<br /> bundle.putString("name", "chenzheng");<br /> bundle.putString("sex", "純爺們");<br /> bundle.putString("age", "生卒年不詳");<br /> message.obj = bundle ;<br /> Log.i("通知", "開始發message了哦");<br /> Log.i("通知thread_id:", ""+Thread.currentThread().getId());<br /> message.sendToTarget();<br /> } catch (Exception e) {<br /> Log.i("通知", "線程sleep時出錯了!");<br /> e.printStackTrace();<br /> }<br /> }<br /> }</p><p> @Override<br /> public void onClick(View v) {<br /> Log.i("通知thread_id:", ""+Thread.currentThread().getId());</p><p> // 建立一個包含Looper的線程,這裡如果沒有HandlerThread的調用,會直接將後邊的MyThread放到UI線程隊列<br /> HandlerThread myHandlerThread = new HandlerThread("chenzheng_java");<br /> // 啟動新線程<br /> myHandlerThread.start();<br /> // 將handler綁定到新線程<br /> handler = new MyHandler(myHandlerThread.getLooper());<br /> // 在新線程中執行任務<br /> handler.post(new MyThread());<br /> }<br />}
轉載聲明: 本文轉自
Android之多線程工作-AsyncTask與handler
Android自用-----AsyncTask實現非同步處理任務
android線程 Handler Message Queue AsyncTask