Android 基礎總結:( 十四)Handler詳解(上)

來源:互聯網
上載者:User

標籤:

Handler的定義:

主要接受子線程發送的資料, 並用此資料配合主線程更新UI。

解釋: 當應用程式啟動時,Android首先會開啟一個主線程 (也就是UI線程) , 主線程為管理介面中的UI控制項,進行事件分發,比如說,你要是點擊一個 Button ,Android會分發事件到Button上,來響應你的操作。

如果此時需要一個耗時的操作,例如:連網讀取資料,或者讀取本地較大的一個檔案的時候,你不能把這些操作放在主線程中,如果你放在主線程中的話,介面會出現假死現象,如果5秒鐘還沒有完成的話,會收到Android系統的一個錯誤提示 "強制關閉"。 這個時候我們需要把這些耗時的操作,放在一個子線程中,因為子線程涉及到UI更新,Android主線程是線程不安全的,也就是說,更新UI只能在主線程中更新,子線程中操作是危險的。

這個時候,Handler就出現了。來解決這個複雜的問題,由於Handler運行在主線程中(UI線程中),它與子線程可以通過Message對象來傳遞資料,這個時候,Handler就承擔著接受子線程傳過來的(子線程用sedMessage()方法傳弟)Message對象,(裡麵包含資料),把這些訊息放入主線程隊列中,配合主線程進行更新UI。

Handler 概念解釋:

handler類允許你發送訊息和處理線程訊息佇列中的訊息及runnable對象。handler執行個體都是與一個線程和該線程的訊息佇列一起使用,一旦建立了一個新的handler執行個體,系統就把該執行個體與一個線程和該線程的訊息佇列捆綁起來,這將可以發送訊息和runnable對象給該訊息佇列,並在訊息佇列出口處處理它們。

handler類有兩種主要用途:1。按照時間計劃,在未來某時刻,對處理一個訊息或執行某個runnable執行個體。2。把一個對另外線程對象的操作請求放入訊息佇列中,從而避免線程間衝突。

時間類訊息通過如下方法使用: post(Runnable), postAtTime(Runnable, long), postDelayed(Runnable, long), sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long), and sendMessageDelayed(Message, long)

methods.post之類函數可以傳輸一個runnable對象給訊息佇列,並在到達訊息佇列後被調用。sendmessage之類函數可以傳送一個包含資料的message對象,該message對象可以被Handler類的handleMessage(Message) 方法所處理。

post之類函數和sendmessage之類的函數都可以指定訊息的執行時機,是立即執行、稍後一段時間執行,還是在某個確定時刻執行。這可以用來實現逾時、訊息或其他時間相關的操作。

當一個進程啟動時,主線程獨立執行一個訊息佇列,該隊列管理著應用頂層的對象(如:activities、broadcast receivers等等)和所有建立的視窗。你可以建立自己的一個線程,並通過handler來與主線程進行通訊。這可以通過在新的線程中調用主線程的handler的post和sendmessage操作來實現。

HandlerThread/Looper & MessageQueue & Message的關係:

handler負責將需要傳遞的資訊封裝成Message,通過handler對象的sendMessage()來將訊息傳遞給Looper,由Looper將Message放入MessageQueue中。當Looper對象看到MessageQueue中含有Message,就將其廣播出去。該handler 對象收到該訊息後,調用相應的handler對象的handleMessage()方法對其進行處理。

Handler一些特點

handler可以分發Message對象和Runnable對象到主線程中,每個Handler執行個體,都會綁定到建立他的線程中(一般是位於主線程),它有兩個作用:

(1): 安排訊息或Runnable 在某個主線程中某個地方執行。

(2)安排一個動作在不同的線程中執行。

Handler中分發訊息的一些方法

post(Runnable)

postAtTime(Runnable,long)

postDelayed(Runnable long)

sendEmptyMessage(int)

sendMessage(Message)

sendMessageAtTime(Message,long)

sendMessageDelayed(Message,long)

以上post類方法允許你排列一個Runnable對象到主線程隊列中,

sendMessage類方法, 允許你安排一個帶資料的Message對象到隊列中,等待更新.

Handler的使用
下面用兩個執行個體講解:
樣本1:
public class HandlerTest extends Activity {/** Called when the activity is first created. */private Button startButton;private Button endButton;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);// 根據id獲得控制項對象startButton = (Button) findViewById(R.id.startButton);endButton = (Button) findViewById(R.id.endButton);// 為控制項設定監聽器startButton.setOnClickListener(new StartButtonListener());endButton.setOnClickListener(new EndButtonListener());}class StartButtonListener implements OnClickListener {public void onClick(View v) {// 調用Handler的post()方法,將要執行的線程對象放到隊列當中handler.post(updateThread);}}class EndButtonListener implements OnClickListener {public void onClick(View v) {// 調用Handler的removeCallbacks()方法,刪除隊列當中未執行的線程對象handler.removeCallbacks(updateThread);}}// 建立Handler對象Handler handler = new Handler();// 建立一個線程對象Runnable updateThread = new Runnable() {// 將要執行的操作寫線上程對象的run方法當中public void run() {System.out.println("updateThread");// 調用Handler的postDelayed()方法// 這個方法的作用是:將要執行的線程對象放入到隊列當中,待時間結束後,運行制定的線程對象// 第一個參數是Runnable類型:將要執行的線程對象// 第二個參數是long類型:延遲的時間,以毫秒為單位handler.postDelayed(updateThread, 3000);}};}


樣本2:

一個應用程式中有一個進度條和一個按鈕,當點擊按鈕後,每隔一秒鐘進度條前進一部分。

為應用程式的運行:


public class ProgressBarHandlerTest extends Activity {/** Called when the activity is first created. */private ProgressBar progressBar;private Button startButton;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);progressBar = (ProgressBar) findViewById(R.id.progressbar);startButton = (Button) findViewById(R.id.startButton);startButton.setOnClickListener(new ProgressBarOnClickListener());}class ProgressBarOnClickListener implements OnClickListener {public void onClick(View v) {// 設定進度條為可見狀態progressBar.setVisibility(View.VISIBLE);updateBarHandler.post(updateThread);}}// 使用匿名內部類來複寫Handler當中的handlerMessage()方法Handler updateBarHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {progressBar.setProgress(msg.arg1);updateBarHandler.post(updateThread); // 將要執行的線程放入到隊列當中}};// 線程類,該類使用匿名內部類的方式進行聲明Runnable updateThread = new Runnable() {int i = 0;public void run() {// TODO Auto-generated method stubSystem.out.println("Begin Thread");i += 10;// 得到一個訊息對象,Message類是android系統提供的Message msg = updateBarHandler.obtainMessage();// 將Message對象的arg1參數的值設定為imsg.arg1 = i; // 用arg1、arg2這兩個成員變數傳遞訊息,優點是系統效能消耗較少try {Thread.sleep(1000); // 讓當前線程休眠1000毫秒} catch (InterruptedException ex) {ex.printStackTrace();}// 將Message對象加入到訊息佇列當中updateBarHandler.sendMessage(msg);// 如果i的值等於100if (i == 100) {// 將線程對象從隊列中移除updateBarHandler.removeCallbacks(updateThread);}}};}

Handler與線程的關係

Handler在預設情況下,實際上它和調用它的Activity是處於同一個線程的。

例如在Handler的使用(一)的樣本1中,雖然聲明了線程對象,但是在實際調用當中它並沒有調用線程的start()方法,而是直接調用當前線程的run()方法。

通過一個例子來證實一下

一個Android應用程式,在Activity中建立Handler和線程對象,並且在Activity的onCreate()方法中輸出當前線程的id和名字,然後線上程對象的run方法中也列印輸出下當前線程的id和名字。如果說,Activity輸出的結果與線程對象輸出的結果是一樣的,那麼就表示它們使用的是同一個線程。

下面是Activity代碼:

public class HandlerTwo extends Activity {/** Called when the activity is first created. */Handler handler = new Handler();@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// 在設定布局檔案之前先調用post方法,// 表示在執行完線程之後才會顯示布局檔案中的內容,而線程中又設定了休眠10秒鐘,// 所以最終效果為,先顯示應用程式主介面,等待10秒鐘之後才顯示布局檔案中的內容handler.post(r);setContentView(R.layout.main);System.out.println("activity id--->" + Thread.currentThread().getId());System.out.println("activity name--->+ Thread.currentThread().getName());}Runnable r = new Runnable() {public void run() {// 輸出當前線程的id和name// 如果這裡輸出的線程id、name與上面onCreate()方法中輸出的線程id、name相同的話,// 那麼則表示,他們使用的是同一個線程System.out.println("runnable_id--->+ Thread.currentThread().getId());System.out.println("runnable_name--->+ Thread.currentThread().getName());try {Thread.sleep(10000); // 讓線程休眠10秒} catch (InterruptedException e) {e.printStackTrace();}}};}

根據結果可以看出,兩個輸出的id和name都相同,它們使用的是同一個線程。

現在將Activity中的代碼修改一下,建立一個線程Thread,然後調用線程的start()方法,再觀察一下控制台的輸出結果。

這裡只要將上面的代碼稍微修改一下就可以了。

1、先將handler.post(r)注釋掉。

2、再添加下面兩句代碼就OK了。

//handler.post(r); Thread t = new Thread(r); t.start();

從這個輸出結果中可以看出,這次線程對象的id、name與activity裡的線程id、name完全不一樣了,由此可見,它們現在使用的不是同一個線程。

這個例子中還掩飾了一個效果,就是平時我們是將Handler的post()方法放在setContentView(R.layout.main)這個方法之後調用,將設定完布局之後再執行其他的操作,而在這個例子中,是將Handler的post()方法放在setContent()方法之前調用,而post裡傳遞的線程對象的run()方法呢,又執行了休眠線程10秒鐘,所以運行實現的效果會是,當程式運行後,首先Activity上沒有任何內容,過來10秒之後,才會顯示Activity裡的內容。

歡迎閱讀下篇:

Android 基礎總結:( 十五)Handler詳解(下)




Android 基礎總結:( 十四)Handler詳解(上)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.