handler的異步機制

http://mobile.51cto.com/abased-375243.htmjava

一.一個問題

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

二.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()函數,執行完畢,就進入死亡狀態。ide

舉個建立線程的例子:函數

  1. Runnable r=new Runnable(){ 
  2.  
  3. @Override 
  4. public void run() { 
  5. // TODO Auto-generated method stub 
  6. System.out.println("thread"); 
  7. handler.postDelayed(thread, 3000); 
  8. }; 

2.關於Runnable隊列工具

(1)原理oop

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

(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,即鍵值對的包。而它特殊在鍵和值都必需要是基本數據類型或是基本數據類型的數組(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)方法,以下:

  1. Handler handler=new Handler(){ 
  2.  
  3. @Override 
  4. public void handleMessage(Message msg) { 
  5. // TODO Auto-generated method stub 
  6. 。。。。。。 
  7.  
  8. 。。。。。。 
  9. }; 

五.Handler的兩個做用

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

代碼示例:

  1. public class HandlerTestActivity extends Activity { 
  2. private Button start; 
  3. @Override 
  4. protected void onCreate(Bundle savedInstanceState) { 
  5. // TODO Auto-generated method stub 
  6. super.onCreate(savedInstanceState); 
  7. setContentView(R.layout.handlertest); 
  8. start=(Button) findViewById(R.id.start); 
  9. start.setOnClickListener(new startListener()); 
  10.  
  11. System.out.println("Activity Thread:"+Thread.currentThread().getId()); 
  12. Handler handler=new Handler(); 
  13. Runnable thread=new Runnable(){ 
  14.  
  15. @Override 
  16. public void run() { 
  17. // TODO Auto-generated method stub 
  18. System.out.println("HandlerThread:"+Thread.currentThread().getId()); 
  19.  
  20. }; 
  21. class startListener implements OnClickListener{ 
  22.  
  23. @Override 
  24. public void onClick(View v) { 
  25. // TODO Auto-generated method stub 
  26. handler.post(thread); 
  27.  

這個小程序中,首先程序啓動,進入onCreate(),打印出當前線程(即主線程)的ID,以後點擊按鈕start,會將線程thread添加到線程隊 列,執行線程thread,thread的做用就是打印出當前線程的ID。在這個程序中,咱們能夠看到經過Handler咱們能夠實現安排 Runnable 在某個主線程中某個地方執行,即做用(1)。

不過這裏有個小小的陷阱,你發現了嗎?這個程序看上去彷佛實現了Handler的異步機制, handler.post(thread)彷佛實現了新啓線程的做用,不過經過執行咱們發現,兩個線程的ID相同!也就是說,實際上thread仍是原來 的主線程,因而可知,handler.post()方法並未真正新建線程,只是在原線程上執行而已,咱們並未實現異步機制。

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

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

第一步:

 

  1.  Runnable r=new Runnable(){ 
  2.  
  3. @Override 
  4. public void run() { 
  5. // TODO Auto-generated method stub 
  6. System.out.println("thread"); 
  7. handler.postDelayed(thread, 3000); 
  8. }; 

第二步:

  1. Thread t=new Thread (r); 

第三步:

  1. 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的線程,

代碼示例:

  1.  //建立一個名叫handler_hread的HandlerThread 對象 
  2. HandlerThread handlerThread=new HandlerThread("handler_hread"); 
  3.  
  4. //開啓handlerThread,在使用handlerThread.getLooper()以前必須先調用start方法,不然取出的是空 
  5. handlerThread.start(); 
  6.  
  7. //將handler綁定在handlerThread的Looper上,即這個handler是運行在handlerThread線程中的 
  8. myHandler handler=new myHandler(handlerThread.getLooper()); 
  9.  
  10. class myHandler extends Handler{ 
  11. public myHandler(){} 
  12. public myHandler(Looper looper){ 
  13. super(looper); 
  14. @Override 
  15. public void handleMessage(Message msg) { 
  16. // TODO Auto-generated method stub 
  17. System.out.println("Activity Thread:"+Thread.currentThread().getId()); 
  18. }

這樣,就實現了handler的異步處理機制,在調用handler.post()方法,經過打印線程ID能夠得知,子線程與主線程是分屬不一樣線程的。

相關文章
相關標籤/搜索