Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 關於android開發 >> AsyncTask和Handler兩種異步方式的實現和區別比較

AsyncTask和Handler兩種異步方式的實現和區別比較

編輯:關於android開發

AsyncTask和Handler兩種異步方式的實現和區別比較


1 AsyncTask實現的原理,和適用的優缺點

AsyncTask,是android提供的輕量級的異步類,可以直接繼承AsyncTask,在類中實現異步操作,並提供接口反饋當前異步執行的程度(可以通過接口實現UI進度更新),最後反饋執行的結果給UI主線程.

使用的優點:

l 簡單,快捷

l 過程可控

使用的缺點:

l 在使用多個異步操作和並需要進行Ui變更時,就變得復雜起來.

2 Handler異步實現的原理和適用的優缺點

在Handler 異步實現時,涉及到 Handler, Looper, Message,Thread四個對象,實現異步的流程是主線程啟動Thread(子線程)àthread(子線程)運行並生成Message- àLooper獲取Message並傳遞給HandleràHandler逐個獲取Looper中的Message,並進行UI變更。

使用的優點:

l 結構清晰,功能定義明確

l 對於多個後台任務時,簡單,清晰

AsyncTask這個類感覺使用比較簡單,就是實現其中幾個方法,onPreExecute()方法是在任務剛開始運行時執行的一些初始化操作,比如初 始化一個進度條等等,然後就執行doInBackground()方法這裡面主要放業務操作,比如查詢數據庫等,在這個方法執行的時候會調用 onProgressUpdate(),可以在這個方法中更新UI界面,最後是調用onPostExecute()方法,當得到業務結果後就可以在這個方 法中返回給UI線程,也可以關閉一些執行這個業務時開的一些資源。大家可以看得出AsyncTask這個類是一個泛型類,這個類的三個參數以此對應 doInBackground(String... params),onProgressUpdate(String... values),onPostExecute(String result)的參數,很形象的···如果不需要傳參和返回值,可以用Void代替。而doInBackground(String... params)方法的返回值也就是onPostExecute(String result)方法的參數值,因為doInBackground方法執行後返回的值是在onPostExecute(String result)中處理的。

用handler方式處理需要知道與handler相關的幾個組件,Looper和Queue,其實Looper的作用就是把handler發送的消息放 到Queue中,並把消息廣播給所有與這個Queue相關的handler,而Queue一般是主線程開啟的時候就給這個線程分配了一個,所以你要與UI 主線程通信必須用於這個Queue相關聯的handler對象才行,一般handler對象在那個線程中創建的就與那個線程的queue關聯,所以在UI 線程中創建的handler對象就與UI線程通訊,這樣我們就可以在子線程中發送消息給主線程,實現更新UI的功能。那主線程又是怎麼處理子線程發送的消 息的呢?其實在生成handler對象的時候我們就要實現handler對象的handleMessage()方法這個方法就是主線程接受並處理子線程發 送過來的消息的方法,從而實現 更新UI線程的功能。

很多網友可能發現Android平台很多應用使用的都是AsyncTask,而並非Thread和Handler去更新UI,這裡給大家說下他們到底有什 麼區別,我們平時應該使用哪種解決方案。從Android 1.5開始系統將AsyncTask引入到android.os包中,過去在很早1.1和1.0 SDK時其實官方將其命名為UserTask,其內部是JDK 1.5開始新增的concurrent庫,做過J2EE的網友可能明白並發庫效率和強大性,比Java原始的Thread更靈活和強大,但對於輕量級的使 用更為占用系統資源。Thread是Java早期為實現多線程而設計的,比較簡單不支持concurrent中很多特性在同步和線程池類中需要自己去實現 很多的東西,對於分布式應用來說更需要自己寫調度代碼,而為了Android UI的刷新Google引入了Handler和Looper機制,它們均基於消息實現,有時可能消息隊列阻塞或其他原因無法准確的使用。

推薦大家使用AsyncTask代替Thread+Handler的方式,不僅調用上更為簡單,經過實測更可靠一些,Google在Browser中大量 使用了異步任務作為處理耗時的I/O操作,比如下載文件、讀寫數據庫等等,它們在本質上都離不開消息,但是AsyncTask相比Thread加 Handler更為可靠,更易於維護,但AsyncTask缺點也是有的比如一旦線程開啟即dobackground方法執行後無法給線程發送消息,僅能 通過預先設置好的標記來控制邏輯,當然可以通過線程的掛起等待標志位的改變來通訊,對於某些應用Thread和Handler以及Looper可能更靈 活。

本文主要講解下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只能被執行一次,否則多次調用時將會出現異常

Java語言:

packagecn.com.chenzheng_java;

importandroid.os.AsyncTask;
/**
*
* @author chenzheng_java
* @description 異步任務AcyncTask示例
*
*/
publicclassMyAsyncTaskextendsAsyncTask{

/**
* 該方法由ui線程進行調用,用戶可以在這裡盡情的訪問ui組件。
* 很多時候,我們會在這裡顯示一個進度條啥的,以示後台正在
* 執行某項功能。
*/
@Override
protectedvoidonPreExecute(){
super.onPreExecute();
}

/**
* 該方法由後台進程進行調用,進行主要的耗時的那些計算。
* 該方法在onPreExecute方法之後進行調用。當然在執行過程中
* 我們可以每隔多少秒就調用一次publishProgress方法,更新
* 進度信息
*/
@Override
protectedObjectdoInBackground(String...params){
returnnull;
}

/**
* doInBackground中調用了publishProgress之後,ui線程就會
* 調用該方法。你可以在這裡動態的改變進度條的進度,讓用戶知道
* 當前的進度。
*/
@Override
protectedvoidonProgressUpdate(Integer...values){
super.onProgressUpdate(values);
}

/**
* 當doInBackground執行完畢之後,由ui線程調用。可以在這裡
* 返回我們計算的最終結果給用戶。
*/
@Override
protectedvoidonPostExecute(Objectresult){
super.onPostExecute(result);
}
}

下面介紹最本質的多線程: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。

下面給出一個比較簡單,並且實用的實例。

Java語言: packagecn.com.src;

importcn.com.chenzheng_java.utils.R;
importandroid.app.Activity;
importandroid.os.Bundle;
importandroid.os.Handler;
importandroid.os.HandlerThread;
importandroid.os.Looper;
importandroid.os.Message;
importandroid.util.Log;
importandroid.view.View;
importandroid.view.View.OnClickListener;
importandroid.widget.Button;

/**
* @author chenzheng_java
* handler和message測試用例
*/
publicclassHanlderMessageTestextendsActivityimplementsOnClickListener{
Buttonbutton;
MyHandlerhandler;

@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
button=(Button)this.findViewById(R.id.button1);
button.setOnClickListener(this);

}

// 聲明自己的handler
privateclassMyHandlerextendsHandler{
/**
* 使用默認的構造函數,會將handler綁定當前UI線程的looper。
* 如果想使用多線程這裡是不能使用默認的構造方法的。
*/
publicMyHandler(){
super();
}

publicMyHandler(Looperlooper){
super(looper);
}

// 處理具體的message,該方法由父類中進行繼承.
@Override
publicvoidhandleMessage(Messagemsg){
intwhatNumber=msg.what;
Bundlebundle=(Bundle)msg.obj;
Log.i("what",whatNumber+"");
Log.i("名稱",bundle.getString("name"));
Log.i("性別",bundle.getString("sex"));
Log.i("年齡",bundle.getString("age"));
super.handleMessage(msg);
}
}

// 我自定義的任務,一般都會實現Runnable
privateclassMyThreadimplementsRunnable{
/**
* 該方法的內部進行具體的任務實現,比如 下載.
* Message中包含著想和ui線程交互的數據,原則上,在線程內部是
* 最好不要直接調用handler的。
* */
@Override
publicvoidrun(){

try{
Thread.sleep(6000);
Messagemessage=Message.obtain(handler);
message.what=10;
Bundlebundle=newBundle();
bundle.putString("name","chenzheng");
bundle.putString("sex","純爺們");
bundle.putString("age","生卒年不詳");
message.obj=bundle;
Log.i("通知","開始發message了哦");
Log.i("通知thread_id:",""+Thread.currentThread().getId());
message.sendToTarget();
}catch(Exceptione){
Log.i("通知","線程sleep時出錯了!");
e.printStackTrace();
}
}
}

@Override
publicvoidonClick(Viewv){
Log.i("通知thread_id:",""+Thread.currentThread().getId());

// 創建一個包含Looper的線程,這裡如果沒有HandlerThread的調用,會直接將後邊的MyThread放到UI線程隊列
HandlerThreadmyHandlerThread=newHandlerThread("chenzheng_java");
// 啟動新線程
myHandlerThread.start();
// 將handler綁定到新線程
handler=newMyHandler(myHandlerThread.getLooper());
// 在新線程中執行任務
handler.post(newMyThread());
}
}

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved