Android:Handler實現非同步處理功能

來源:互聯網
上載者:User

標籤:android   style   blog   color   ar   os   使用   sp   java   

一.一個問題

有這樣一個問題值得我們思考,若把一些類似於下載的功能(既耗時且不一定有結果)寫在Activity(主線程)裡,會導致Activity阻塞,長時間無響應,直至頁面假死(如果5秒鐘還沒有完成的話,會收到Android系統的一個錯誤提示 "強制關閉")。因此,我們需要把這些耗時的操作放在單獨的子線程中操作。這就是Handler的使命。Handler提供非同步處理的功能,發送和接收不是同時的(Activity的主線程和線程隊列裡的線程是不同的線程,並行進行,互不影響)。

二.Handler簡介

Handler 為Android作業系統中的線程通訊工具,它主要由兩個作用:(1)安排訊息或Runnable 在某個主線程中某個地方執行(2)安排一個動作在另外的線程中執行。每個Handler對象維護兩個隊列(FIFO),訊息佇列和Runnable隊列, 都是有Android作業系統提供的。Handler可以通過這兩個隊列來分別:

  1. 發送、接受、處理訊息–訊息佇列;
  2. 啟動、結束、休眠線程–Runnable隊列;

Handler的使用方法大體分為3個步驟:1.建立Handler對象。2.建立Runnable和訊息。3.調用post以及sendMessage方法將Runnable和訊息添加到隊列。

三.Runnable隊列

1.java中的線程

在java中,線程的建立有兩種方法:繼承Thread類和實現Runnable介面。而這最重要的都是要複寫run方法來實現線程的功能。當線程的時間片到了,開始運行時,就執行run()函數,執行完畢,就進入死亡狀態。

舉個建立線程的例子:

Runnable r=new Runnable(){  @Override public void run() { // TODO Auto-generated method stub System.out.println("thread"); handler.postDelayed(thread, 3000); } };

  

2.關於Runnable隊列

(1)原理

Android的線程非同步處理機制:Handler對象維護一個線程隊列,有新的Runnable送來(post())的時候,把它放在隊尾,而處理 Runnable的時候,從隊頭取出Runnable執行。當向隊列發送一個Runnable後,立即就返回,並不理會Runnable是否被執行,執行 是否成功等。而具體的執行則是當排隊排到該Runnable後系統拿來執行的。這就好比郵局的例子。寄信者將信寫好後放入郵筒就回家了,他並不知道郵件何 時被郵局分發,何時寄到,對方怎樣讀取這些事。這樣,就實現了Android的非同步處理機制。

(2)具體操作

向隊列添加線程:

handler.post(Runnable );將Runnable直接添排入佇列

handler.postDelayed(Runnable, long)延遲一定時間後,將Runnable添排入佇列

handler.postAtTime(Runnable,long)定時將Runnable添排入佇列

終止線程:

handler.removeCallbacks(thread);將Runnable從Runnable隊列中取出

四.訊息佇列

1.訊息對象

(1)Message對象

Message對象攜帶資料,通常它用arg1,arg2來傳遞訊息,當然它還可以有obj參數,可以攜帶Bundle資料。它的特點是系統效能消耗非常少。

初始化: Message msg=handler.obtainMessage();

(2)Bundle對象

Bundle是Android提供的類,可以把它看做是特殊的Map,即索引值對的包。而它特殊在鍵和值都必須要是基礎資料型別 (Elementary Data Type)或是基礎資料型別 (Elementary Data Type)的數組(Map的索引值要求都是對象),特別的,鍵要求都是String類型。用Message來攜帶Bundle資料:

放入:msg.setData(Bundle bundle);

取出:msg.getData();

2.關於訊息佇列

(1)原理

Android的訊息非同步處理機制:Handler對象維護一個訊息佇列,有新的訊息送來(sendMessage())的時候,把它放在隊尾,之後排隊 到處理該訊息的時候,由主線程的Handler對象處理(handleMessage())。整個過程也是非同步,和Runnable隊列的原理相同。

(2)具體操作:

向隊列添加Runnable:handler.sendMessage(Message);

將訊息發送到訊息佇列msg.sendToTarget();

延遲一定時間後,將訊息發送到訊息佇列 handler.sendMessageDelayed(Message,long);

定時將訊息發送到訊息佇列 handler.sendMessageAtTime(Message,long)

處理訊息:

訊息的具體處理過程,需要在new Handler對象時使用匿名內部類重寫Handler的handleMessage(Message msg)方法,如下:

Handler handler=new Handler(){  @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub 。。。。。。  。。。。。。 } };

  

五.Handler的兩個作用

1.安排訊息或Runnable 在某個主線程中某個地方執行

程式碼範例:

public class HandlerTestActivity extends Activity { private Button start; @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); setContentView(R.layout.handlertest); start=(Button) findViewById(R.id.start); start.setOnClickListener(new startListener());  System.out.println("Activity Thread:"+Thread.currentThread().getId()); } Handler handler=new Handler(); Runnable thread=new Runnable(){  @Override public void run() { // TODO Auto-generated method stub System.out.println("HandlerThread:"+Thread.currentThread().getId());  } }; class startListener implements OnClickListener{  @Override public void onClick(View v) { // TODO Auto-generated method stub handler.post(thread); } }  } 

這個小程式中,首先程式啟動,進入onCreate(),列印出當前線程(即主線程)的ID,之後點擊按鈕start,會將線程thread添加到線程隊 列,執行線程thread,thread的作用就是列印出當前線程的ID。在這個程式中,我們可以看到通過Handler我們可以實現安排 Runnable 在某個主線程中某個地方執行,即作用(1)。

不過這裡有個小小的陷阱,你發現了嗎?這個程式看上去似乎實現了Handler的非同步機制, handler.post(thread)似乎實現了新啟線程的作用,不過通過執行我們發現,兩個線程的ID相同!也就是說,實際上thread還是原來 的主線程,由此可見,handler.post()方法並未真正建立線程,只是在原線程上執行而已,我們並未實現非同步機制。

2.安排一個動作在另外的線程中執行。

(1)java中標準的建立線程的方法

第一步:

 Runnable r=new Runnable(){  @Override public void run() { // TODO Auto-generated method stub System.out.println("thread"); handler.postDelayed(thread, 3000); } }; 

第二步:

Thread t=new Thread (r);

第三步:

t.start();

若把上面樣本程式中的handler.post(thread);語句改成以上形式,通過列印我們可以看到,兩個ID是不同的,新的線程啟動了!

(2)關於Looper

Looper類用來為線程開啟一個訊息迴圈,作用是可以迴圈的從訊息佇列讀取訊息,所以Looper實際上就是訊息佇列+訊息迴圈的封裝。每個線程只能對應一個Looper,除主線程外,Android中的線程預設是沒有開啟Looper的。

通過Handler與Looper互動,Handler可以看做是Looper的介面,用來向指定的Looper發送訊息以及定義處理方法。預設情況下Handler會與其所線上程的Looper綁定,即:

Handler handler=new Handler();等價於Handler handler=new Handler(Looper.myLooper());

Looper有兩個主要方法:

Looper.prepare();啟用Looper
Looper.loop(); 讓Looper開始工作,從訊息佇列裡取訊息,處理訊息。

注意:寫在Looper.loop()之後的代碼不會被執行,這個函數內部應該是一個迴圈,當調用mHandler.getLooper().quit()後,loop才會中止,其後的代碼才能得以運行。

(3)Handler非同步機制的實現

Handler是通過HandlerThread 使得子線程與主線程分屬不同線程的。實際上,HandlerThread 是一個特殊的線程,它是一個封裝好Looper的線程,

程式碼範例:

//建立一個名叫handler_hread的HandlerThread 對象 HandlerThread handlerThread=new HandlerThread("handler_hread");  //開啟handlerThread,在使用handlerThread.getLooper()之前必須先調用start方法,否則取出的是空 handlerThread.start();  //將handler綁定在handlerThread的Looper上,即這個handler是運行在handlerThread線程中的 myHandler handler=new myHandler(handlerThread.getLooper());  class myHandler extends Handler{ public myHandler(){} public myHandler(Looper looper){ super(looper); } @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub System.out.println("Activity Thread:"+Thread.currentThread().getId()); } } 

這樣,就實現了handler的非同步處理機制,在調用handler.post()方法,通過列印線程ID可以得知,子線程與主線程是分屬不同線程的。

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.