天天看點

5分鐘完全了解android handler

Handler機制簡介

Handler是android中最重要組成部分之一,Handler機制可以看做是一個消息阻塞隊列,APP啟動後很快就進入死循環(while循環),不斷的讀取消息隊列中的消息,每個線程最多隻有一個消息隊列,沒有消息時就阻塞,有就立馬執行。所有消息排隊執行,因為是一個線程,是以同時隻能執行一個消息。android的view繪制,事件響應(點選,觸摸螢幕等)都是把消息發送到了主線程的消息隊列,等待android APP的執行(這點可以通過手動抛出異常檢視錯誤堆棧來驗證)。包括自己在主線程new 的handler最終也是把消息插入到了主線程消息隊列中。從以上來看android主線程大部分時間是空閑的。當點選螢幕後手機能立馬響應也可以看出android主線程大部分時間是空閑的。雖然主線程要處理的事情狠多,很雜,很瑣碎(view布局、繪制,事件分發等等),但處理時間都很短暫,可以保證很快處理完畢,然後等待下一個消息的到來。android handler機制簡可以實作所有view相關的操作都在主線程進行,進而避免了使用 鎖 。具體實作代碼 如下。

java工程實作Handler機制代碼

下面的代碼跟android的handler機制主要原理完全一緻,但不依賴android系統。

1
     
     
      2
     
     
      3
     
     
      4
     
     
      5
     
     
      6
     
     
      7
     
     
      8
     
     
      9
     
     
      10
     
     
      11
     
     
      12
     
     
      13
     
     
      14
     
     
      15
     
     
      16
     
     
      17
     
     
      18
     
     
      19
     
     
      20
     
     
      21
     
     
      22
     
     
      23
     
     
      24
     
     
      25
     
     
      26
     
     
      27
     
     
      28
     
     
      29
     
     
      30
     
     
      31
     
     
      32
     
     
      33
     
     
      34
     
     
      35
     
     
      36
     
     
      37
     
     
      38
     
     
      39
     
     
      40
     
     
      41
     
     
      42
     
     
      43
     
     
      44
     
     
      45
     
     
      46
     
     
      47
     
     
      48
     
     
      49
     
     
      50
     
     
      51
     
     
      52
     
     
      53
     
     
      54
     
     
      55
     
     
      56
     
     
      57
     
     
      58
     
     
      59
     
     
      60
     
     
      61
     
     
      62
     
     
      63
     
     
      64
     
     
      65
     
     
      66
     
     
      67
     
     
      68
     
     
      69
     
     
      70
     
     
      71
     
     
      72
     
     
      73
     
     
      74
     
     
      75
     
     
      76
     
     
      77
     
     
      78
     
     
      79
     
     
      80
     
     
      81
     
     
      82
     
     
      83
     
     
      84
     
     
      85
     
     
      86
     
     
      87
     
     
      88
     
     
      89
     
     
      90
     
     
      91
     
     
      92
     
     
      93
     
     
      94
     
     
      95
     
     
      96
     
     
      97
     
     
      98
     
     
      99
     
     
      100
     
     
      101
     
     
      102
     
     
      103
     
     
      104
     
     
      105
     
     
      106
     
     
      107
     
     
      108
     
     
      109
     
     
      110
     
     
      111
     
     
      112
     
     
      113
     
     
      114
     
     
      115
     
     
      116
     
     
      117
     
     
      118
     
     
      119
     
     
      120
     
     
      121
     
     
      122
     
     
      123
     
     
      124
     
     
      125
           
import com.handler.Handler;
     
     
      import com.handler.Looper;
     
     
      import com.handler.Message;
     
     
     
      public 
      class Main {
     
     
     	
      public static void main(String[] args) {
     
     
     		
      new Main().start();
     
     		
     
     
      	}
     
     	
     
     	
      private void start(){
     
     		
      //建立該線程唯一的消息隊列,線程安全的阻塞隊列
     
     
      		Looper.prepare();
     
     
     
      		onCreate();
     
     		
     
     		
      //死循環,阻塞式,執行下面代碼後主線程就會去擷取消息隊列裡的消息,沒有消息時就阻塞,有就執行。執行Looper.loop前即使消息隊列裡有消息,消息也不會執行,因為主線程還沒有去檢查消息隊列。
     
     
      		Looper.loop();
     
     		
     
     		
      //下面 的代碼通常不會執行,除非手動讓主線程消息隊列退出。退出主線程消息隊列後android的view布局、繪制,事件分發就不執行了,是以android APP也沒必要繼續執行了,是以android采用了抛出異常的方式結束APP。
     
     
      		System.out.println(
      "exit........");
     
     		
      throw 
      new RuntimeException(
      "Main thread loop unexpectedly exited");
     
     
     
      	}
     
     	
      private void onCreate() {
     
     		
      //
     
     		
      // 下面的操作相當于運作在android的UI線程中 
     
     		
      //
     
     
     		
      final Thread thread = Thread.currentThread();
     
     
      		System.out.println(
      "main thread=" + thread);
     
     
     
      		Handler handler = 
      new Handler() {
     
     			
      @Override
     
     			
      public void handleMessage(Message msg) {
     
     				
      //若thread == Thread.currentThread(),則證明已經運作在主線程中了
     
     
      				System.out.println(
      "current thread is main thread? " + (thread == Thread.currentThread()));
     
     
      				System.out.println(msg);
     
     
      				System.out.println();
     
     
      			}
     
     
      		};
     
     		
      // 測試1       主線程建立handler,子線程使用該handler發送消息 
     
     		
      new Thread() {
     
     			
      public void run() {
     
     				
      try {
      //模拟耗時操作
     
     
      					Thread.sleep(
      1000 * 
      2);
     
     
      				} 
      catch (InterruptedException e) {
     
     
      				}
     
     
      				Message message = 
      new Message();
     
     
      				message.obj = 
      "new Thread" + Thread.currentThread();
     
     
      				message.what = (
      int) System.currentTimeMillis();
     
     				
      //在子線程中發送消息 
     
     
      				handler.sendMessage(message);
     
     				
     
     				
      try {
     
     
      					Thread.sleep(
      1000 * 
      2);
     
     
      				} 
      catch (InterruptedException e) {
     
     
      				}
     
     				
     
     
      				message = 
      new Message();
     
     
      				message.obj = 
      "hanler...waht==1" ;
     
     
      				message.what = 
      1;
     
     				
      //在子線程中發送消息 
     
     
      				handler.sendMessage(message);
     
     				
     
     
     
      				message = 
      new Message();
     
     
      				message.obj = 
      "hanler...waht==2" ;
     
     
      				message.what = 
      2;
     
     				
      //在子線程中發送消息 
     
     
      				handler.sendMessage(message);
     
     				
     
     
      				message = 
      new Message();
     
     
      				message.obj = 
      "hanler...waht==3" ;
     
     
      				message.what = 
      3;
     
     				
      //在子線程中發送消息 
     
     
      				handler.sendMessage(message);
     
     				
     
     
      			};
     
     
      		}.start();
     
     
     		
      // 測試2 在thread内部建立handler,結果會抛出異常
     
     		
      new Thread() {
     
     			
      public void run() {
     
     				
      try {
     
     
      					sleep(
      1000 * 
      3);
     
     
      				} 
      catch (InterruptedException e) {
     
     
      				}
     
     				
      /*
     
     
      				 * 線上程内部使用預設構造函數建立handler會抛出異常。
     
     
      				 * android中也可以在子線程中建立Handler,但要在初始化時傳入Looper,
     
     
      				 * Looper.getMainLooper()擷取到的就是主線程的Looper,是以可以這樣建立
     
     
      				 * 
     
     
      				 * new Handler(Looper.getMainLooper()){
     
     
      						@Override
     
     
      						public void handleMessage(Message msg) {
     
     
      							//運作在主線程中
     
     
      						}
     
     
      					};
     
     
      				 */
     
     
      				Handler h = 
      new Handler() {
     
     					
      public void handleMessage(Message msg) {
     
     
     
      						System.out.println(
      "haneler msg...." + msg);
     
     
      					};
     
     
      				};
     
     
     
      				Message message = 
      new Message();
     
     
      				message.obj = 
      "handler in new Thread";
     
     
      				message.what = (
      int) System.currentTimeMillis();
     
     				
      //在子線程中發送消息 
     
     
      				h.sendMessage(message);
     
     
     
      			};
     
     
      		}.start();
     
     
     		
      //
     
     		
      // 上面的操作相當于運作在android的UI線程中 
     
     		
      //
     
     
     
      	}
     
     
      }
           

運作結果

1
     
     
      2
     
     
      3
     
     
      4
     
     
      5
     
     
      6
     
     
      7
     
     
      8
     
     
      9
     
     
      10
     
     
      11
     
     
      12
     
     
      13
     
     
      14
     
     
      15
     
     
      16
     
     
      17
           
main thread=Thread[main,5,main]  
     
     
      current thread is main thread? true  
     
     
      what=18175614 obj=new ThreadThread[Thread-0,5,main]  
     
       
     
     
      Exception in thread "Thread-1" java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()  
     
     
          at com.handler.Handler.<init>(Handler.java:14)  
     
     
          at Main$3$1.<init>(Main.java:103)  
     
     
          at Main$3.run(Main.java:103)  
     
     
      current thread is main thread? true  
     
     
      what=1 obj=hanler...waht==1  
     
       
     
     
      current thread is main thread? true  
     
     
      what=2 obj=hanler...waht==2  
     
       
     
     
      current thread is main thread? true  
     
     
      what=3 obj=hanler...waht==3
           

Handler代碼

1
     
     
      2
     
     
      3
     
     
      4
     
     
      5
     
     
      6
     
     
      7
     
     
      8
     
     
      9
     
     
      10
     
     
      11
     
     
      12
     
     
      13
     
     
      14
     
     
      15
     
     
      16
     
     
      17
     
     
      18
     
     
      19
     
     
      20
     
     
      21
     
     
      22
     
     
      23
     
     
      24
     
     
      25
     
     
      26
     
     
      27
     
     
      28
     
     
      29
     
     
      30
     
     
      31
     
     
      32
     
     
      33
     
     
      34
     
     
      35
           
package com.handler;  
     
       
     
       
     
     
      public 
      class Handler {  
     
       
     
       
     
         
      private MessageQueue messageQueue;  
     
           
     
         
      public Handler() {  
     
       
     
     
              Looper looper=Looper.myLooper();  
     
               
     
             
      if (looper==
      null) {  
     
                  
      throw 
      new RuntimeException(  
     
                             
      "Can't create handler inside thread that has not called Looper.prepare()");  
     
                      
     
     
              }  
     
               
     
             
      this.messageQueue=looper.messageQueue;  
     
     
          }  
     
       
     
         
      public void sendMessage(Message msg) {  
     
               
     
             
      //Looper循環中發現message後,調用message.targer就得到了目前handler,使用taget.handleMessage  
     
             
      //就把消息轉發給了發送message時的handler的handleMessage函數  
     
     
              msg.target=
      this;  
     
               
     
     
              messageQueue.enqueueMessage(msg);  
     
               
     
     
          }  
     
           
     
         
      public void handleMessage(Message msg) {  
     
     
          }  
     
     
      }
           

Looper代碼

1
     
     
      2
     
     
      3
     
     
      4
     
     
      5
     
     
      6
     
     
      7
     
     
      8
     
     
      9
     
     
      10
     
     
      11
     
     
      12
     
     
      13
     
     
      14
     
     
      15
     
     
      16
     
     
      17
     
     
      18
     
     
      19
     
     
      20
     
     
      21
     
     
      22
     
     
      23
     
     
      24
     
     
      25
     
     
      26
     
     
      27
     
     
      28
     
     
      29
     
     
      30
     
     
      31
     
     
      32
     
     
      33
     
     
      34
     
     
      35
     
     
      36
     
     
      37
     
     
      38
     
     
      39
     
     
      40
     
     
      41
     
     
      42
     
     
      43
     
     
      44
     
     
      45
     
     
      46
     
     
      47
     
     
      48
           
package com.handler;
     
     
     
      public 
      class Looper {
     
     
     
     	
      private 
      static 
      final ThreadLocal<Looper> threadLocal=
      new ThreadLocal<>();
     
     	
      /**
     
     
      	 * 存儲Message的隊列,阻塞式,沒有消息則一直等待
     
     
      	 */
     
     	
      final MessageQueue messageQueue;
     
     	
     
     	
     
     	
      private Looper() {
     
     
      		messageQueue=
      new MessageQueue();
     
     
      	}
     
     
     	
      /**為該線程建立Looper,
     
     
      	 * 若該線程已經有Looper了則不需要再次調用prepare
     
     
      	 */
     
     	
      public  static void prepare() {
     
     		
      if (threadLocal.get() != 
      null) {
     
                 
      throw 
      new RuntimeException(
      "Only one Looper may be created per thread");
     
     
              }
     
     
      		threadLocal.set(
      new Looper() );
     
     
      	}
     
     	
     
     	
      public static void loop() {
     
     
      		Looper looper=myLooper();
     
     		
      if (looper == 
      null) {
     
                 
      throw 
      new RuntimeException(
      "No Looper; Looper.prepare() wasn't called on this thread.");
     
     
              }
     
     
      		MessageQueue messageQueue=looper.messageQueue;
     
     		
     
     		
      for(;;){
     
     
      			Message message=messageQueue.next();
     
     
      			message.target.handleMessage(message);
     
     
      		}
     
     
      	}
     
     	
     
     	
      /**
     
     
      	 * 擷取當先線程的Looper
     
     
      	 * 
      @return
     
     
      	 */
     
     	
      public static Looper myLooper() {
     
     		
      return threadLocal.get();
     
     
      	}
     
     
      }
           

MessageQueued代碼

1
     
     
      2
     
     
      3
     
     
      4
     
     
      5
     
     
      6
     
     
      7
     
     
      8
     
     
      9
     
     
      10
     
     
      11
     
     
      12
     
     
      13
     
     
      14
     
     
      15
     
     
      16
     
     
      17
     
     
      18
     
     
      19
     
     
      20
     
     
      21
     
     
      22
     
     
      23
     
     
      24
     
     
      25
     
     
      26
     
     
      27
     
     
      28
     
     
      29
     
     
      30
     
     
      31
     
     
      32
     
     
      33
     
     
      34
     
     
      35
     
     
      36
           
package com.handler;  
     
       
     
     
      import java.util.concurrent.BlockingQueue;  
     
     
      import java.util.concurrent.LinkedBlockingQueue;  
     
       
     
     
      public 
      class MessageQueue {  
     
       
     
       
     
         
      private BlockingQueue<Message>blockingQueue=
      new LinkedBlockingQueue<>();  
     
           
     
         
      /** 
     
     
           * 阻塞式,沒有消息則一直等待 
     
     
           * 
      @return 
     
     
           */  
     
         
      public Message next() {  
     
             
      try {  
     
                 
      return blockingQueue.take();  
     
     
              } 
      catch (InterruptedException e) {  
     
                 
      throw 
      new RuntimeException();  
     
     
              }  
     
     
          }  
     
           
     
         
      /** 
     
     
           * 插入到消息隊列尾部 
     
     
           * 
      @param message 
     
     
           */  
     
         
      void enqueueMessage(Message message) {  
     
             
      try {  
     
     
                  blockingQueue.put(message);  
     
     
              } 
      catch (InterruptedException e) {  
     
                 
      // TODO Auto-generated catch block  
     
     
                  e.printStackTrace();  
     
     
              }  
     
     
          }  
     
     
      }
           

ThreadLocal簡單實作

ThreadLocal内部原理和下面實作方式不同,但達到的效果是相同的,本篇主要介紹Handler機制,簡化了ThreadLocal

1
     
     
      2
     
     
      3
     
     
      4
     
     
      5
     
     
      6
     
     
      7
     
     
      8
     
     
      9
     
     
      10
     
     
      11
     
     
      12
     
     
      13
     
     
      14
     
     
      15
     
     
      16
     
     
      17
     
     
      18
     
     
      19
     
     
      20
     
     
      21
     
     
      22
     
     
      23
     
     
      24
     
     
      25
     
     
      26
     
     
      27
     
     
      28
     
     
      29
           
package com.handler;  
     
       
     
     
      import java.util.HashMap;  
     
     
      import java.util.Map;  
     
     
      /** 
     
     
       * ThreadLocal簡單實作 
     
     
       * 
      @author Young 
     
     
       * 
     
     
       * 
      @param <T> 
     
     
       */  
     
     
      public 
      class ThreadLocal<T> {  
     
       
     
       
     
         
      private Map<Thread,T>map;  
     
       
     
         
      public ThreadLocal() {  
     
     
              map=
      new HashMap<>();  
     
     
          }  
     
           
     
         
      public void set(T obj) {  
     
     
              map.put(Thread.currentThread(),obj);  
     
     
          }  
     
           
     
         
      public T get() {  
     
             
      return map.get(Thread.currentThread());  
     
     
          }  
     
           
     
     
      }
           

Message代碼

1
     
     
      2
     
     
      3
     
     
      4
     
     
      5
     
     
      6
     
     
      7
     
     
      8
     
     
      9
     
     
      10
     
     
      11
     
     
      12
     
     
      13
     
     
      14
     
     
      15
           
package com.handler;  
     
       
     
     
      public 
      class Message {  
     
       
     
     
          Handler target;  
     
         
      public Object obj;  
     
         
      public 
      int what;  
     
       
     
         
      @Override  
     
         
      public String toString() {  
     
             
      return   
      "what="+what+
      " obj="+obj.toString();  
     
     
          }  
     
           
     
     
      }
           

以上就是android Handler機制原理代碼了。

android還提供了HandlerThread,其實是對Handler和Thread的封裝。

先看一下HandlerThread使用方式

1
     
     
      2
     
     
      3
     
     
      4
     
     
      5
     
     
      6
     
     
      7
     
     
      8
     
     
      9
     
     
      10
     
     
      11
     
     
      12
     
     
      13
     
     
      14
     
     
      15
     
     
      16
     
     
      17
     
     
      18
           
Handler myHandler;
     
      
      new HandlerThread(
      "Compress-Thread") {  
     
                 
      @Override  
     
                 
      protected void onLooperPrepared() {  
     
                     
      super.onLooperPrepared();  
     
     
                      myHandler = 
      new Handler();  
     
     
                      myHandler.post(
      new Runnable(){
     
                        
      @Override  
     
             			
      public void run() {  
     
                  					
      //在HandlerThread線程執行
     
     
             				} 
     
                     	
     
     
                      });
     
     
                  }  
     
     
              }.start();
     
     
     
      //不要在這使用myHandler發送消息,因為myHandler是在onLooperPrepared中建立的,onLooperPrepared又是運作在HandlerThread線程的,是以剛執行到這時HandlerThread線程可能還沒有建立完,onLooperPrepared也就不會執行,myHandler自然是null
           

每次new HandlerThread都會建立一個新線程,當我們使用myHandler發送消息時消息就會在HandlerThread線程執行。HandlerThread線程内部也是一個死循環,通常不會退出,當通過myHandler為其發送消息時就會從阻塞中醒來執行消息,執行完消息隊列裡的消息後就又阻塞。HandlerThread可以排隊執行消息,保證能按加入消息的先後順序執行。比如我們需要壓縮很多圖檔時,就可以使用HandlerThread,主線程直接把圖檔通過myHandler發送到HandlerThread,HandlerThread就可以執行圖檔壓縮,所有壓縮任務都按添加順序依次執行。

來自我的部落格

http://blog.csdn.net/qingchunweiliang/article/details/50448365

繼續閱讀