Android Handler、LOOP和Message

一,相關概念

Android中如果通過用戶界面(如button)來來啓動線程,然後再線程中的執行代碼將狀態信息輸出到用戶界面(如文本框),這時候就會拋出以下的異常信息:

5-12 13:33:04.393: ERROR/JavaBinder(1029):android.view.ViewRoot$CalledFromWrongThreadException:Onlythe original thread that created a view hierarchy can touch its views.

該異常的意思是,只有最初創建視圖層次結構的線程才能接觸該結構中的視圖,也就是說,不是最初創建界面的線程是不能接觸界面元素的。那麼,在不是創建界面的線程中,如何把內容輸出到界面元素中呢?

對了,我們可以通過線程消息分發機制來實現。就應用程序而言,Android系統中Java的應用程序和其他系統上一樣,都是依賴於消息驅動完成那些比較有難度的工作,它們的工作原理大致是分別有一個消息隊列負責接收各種消息,一個消息循環可以不斷從中取出消息然後做處理。先了解一下幾個消息機制的常見名詞概念:

  • Handler

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

  • Message

消息對象,Message Queue中的存放的對象。一個Message Queue中包含多個Message。Message實例對象的取得,通常使用Message類裏的靜態方法obtain(),該方法有多個重載版本可供選擇;它的創建並不一定是直接創建一個新的實例,而是先從Message Pool(消息池)中看有沒有可用的Message實例,存在則直接取出返回這個實例。如果Message Pool中沒有可用的Message實例,則才用給定的參數創建一個Message對象。調用removeMessages()時,將Message從Message Queue中刪除,同時放入到Message Pool中。除了上面這種方式,也可以通過Handler對象的obtainMessage()獲取一個Message實例。

  • MessageQueue

是一種數據結構,見名知義,就是一個消息隊列,存放消息的地方。每一個線程最多只可以擁有一個MessageQueue數據結構。創建一個線程的時候,並不會自動創建其MessageQueue。通常使用一個Looper對象對該線程的MessageQueue進行管理。主線程創建時,會創建一個默認的Looper對象,而Looper對象的創建,將自動創建一個Message Queue。其他非主線程,不會自動創建Looper,要需要的時候,通過調用prepare函數來實現。

  • Looper

是MessageQueue的管理者。每一個MessageQueue都不能脫離Looper而存在,Looper對象的創建是通過prepare函數來實現的。同時每一個Looper對象和一個線程關聯。通過調用Looper.myLooper()可以獲得當前線程的Looper對象創建一個Looper對象時,會同時創建一個MessageQueue對象。除了主線程有默認的Looper,其他線程默認是沒有MessageQueue對象的,所以,不能接受Message。如需要接受,自己定義一個Looper對象(通過prepare函數),這樣該線程就有了自己的Looper對象和MessageQueue數據結構了。Looper從MessageQueue中取出Message然後,交由Handler的handleMessage進行處理。處理完成後,調用Message.recycle()將其放入Message Pool中。

二,Looper類分析

當需要向其他線程發消息時我們需要實例化Looper對象會有以下兩步:

1,Looper.prepare()

我們可以看下源碼:

[java] view plain copy
  1. // sThreadLocal.get() will return null unless you've called prepare().  
  2.     static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();   
  3. public static void prepare() {   
  4.        if (sThreadLocal.get() != null) {   
  5.            throw new RuntimeException("Only one Looper may be created per thread");   
  6.        }   
  7.  // 構造一個Looper對象,保存到調用線程的局部變量中。  
  8.         sThreadLocal.set(new Looper());   
  9.    }  

可以看出來,Looper.prepare()會在調用線程的局部變量中設置一個Looper對象。這個調用線程就是LooperThread的run線程。F3看一下Looper的構造方法:

[java] view plain copy
  1. private Looper() {  
  2.      //構造一個消息隊列  
  3.         mQueue = new MessageQueue();  
  4.         mRun = true;  
  5.      //獲取當前線程的Thread對象  
  6.         mThread = Thread.currentThread();  
  7.     }  

所以Looper.prepare()的作用是設置一個Looper對象保存在sThreadLocal裏,而Looper自己內部封裝了一個消息隊列。就這樣把Looper和調用線程關聯在一起了。到底要幹嘛,我們看Looper的第二步棋

2,Looper.loop()

[java] view plain copy
  1. public static void loop() {  
  2.         //myLooper()將會返回保存在sThreadLocal中的Looper對象。  
  3.         Looper me = myLooper();  
  4.         if (me == null) {  
  5.             throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");  
  6.         }  
  7.         //取出該Looper中的消息隊列  
  8.         MessageQueue queue = me.mQueue;    
  9.         while (true) {  
  10.             //處理消息,Message對象中有一個target,它是Handle類型  
  11.             Message msg = queue.next(); // might block  
  12.             if (msg != null) {  
  13.                 if (msg.target == null) {  
  14.                     // No target is a magic identifier for the quit message.  
  15.                     return;  
  16.                 }  
  17.                 msg.target.dispatchMessage(msg);  
  18.                 msg.recycle();  
  19.             }  
  20.         }  
  21.     }  
  22.   
  23.     /** 
  24.      * Return the Looper object associated with the current thread.  Returns 
  25.      * null if the calling thread is not associated with a Looper. 
  26.      */  
  27.     public static Looper myLooper() {  
  28.         return sThreadLocal.get();  
  29.     }  

綜上可以知道Looper類的作用是:

封裝了一個消息隊列;

Looper的prepare方法把這個Looper和調用prepare的線程綁定在一起;

處理線程調用loop方法,處理來自該消息隊列的消息。

三,Handler類分析

1,好多構造函數

一樣看去,我們可以看到幾個眼熟的傢伙:MessageQueue,Looper,Callback,一個不少,他們是幹嘛的?往下看發現有4個形態不一的構造函數:

[java] view plain copy
  1. /** 
  2.      * 構造函數一,鐵板一塊,不過也完成了調用線程的Looper,並獲得它的消息隊列。 
  3.      */  
  4.     public Handler() {  
  5.         mLooper = Looper.myLooper();  
  6.         if (mLooper == null) {  
  7.             throw new RuntimeException(  
  8.                 "Can't create handler inside thread that has not called Looper.prepare()");  
  9.         }  
  10.         mQueue = mLooper.mQueue;  
  11.         mCallback = null;  
  12.     }  
  13.   
  14.     /** 
  15.      * 構造函數二,多了一個callback 
  16.      */  
  17.     public Handler(Callback callback) {  
  18.         mLooper = Looper.myLooper();  
  19.         if (mLooper == null) {  
  20.             throw new RuntimeException(  
  21.                 "Can't create handler inside thread that has not called Looper.prepare()");  
  22.         }  
  23.         mQueue = mLooper.mQueue;  
  24.         mCallback = callback;  
  25.     }  
  26.   
  27.     /** 
  28.      * 構造函數三,Looper由外部傳入,定製。 
  29.      */  
  30.     public Handler(Looper looper) {  
  31.         mLooper = looper;  
  32.         mQueue = looper.mQueue;  
  33.         mCallback = null;  
  34.     }  
  35.   
  36.     /** 
  37.      * 構造函數四,它是2和3的結合體。 
  38.      */  
  39.     public Handler(Looper looper, Callback callback) {  
  40.         mLooper = looper;  
  41.         mQueue = looper.mQueue;  
  42.         mCallback = callback;  
  43.     }  

在上面的構造函數中,Handler中的消息隊列變量最終都會指向Looper的消息隊列,Handler爲啥要這麼做,我們繼續挖。

2,Handler的真正使命

通過分析Handler源代碼,可以發現它的作用就是提供一系列函數,方便我們完成消息的創建封裝和插入到消息隊列:

[java] view plain copy
  1. //查看消息隊列中是否有存貨  
  2.   public final boolean hasMessages(int what) {  
  3.         return mQueue.removeMessages(this, what, nullfalse);  
  4.     }  
  5. //創建封裝一個消息  
  6.   public final Message obtainMessage(int what)  
  7.     {  
  8.         return Message.obtain(this, what);  
  9.     }  
  10. //發送一個消息,並添加到隊列尾  
  11.   public final boolean sendMessage(Message msg)  
  12.     {  
  13.         return sendMessageDelayed(msg, 0);  
  14.     }  
  15. //延時發送一個消息,並添加到隊列尾  
  16.   public final boolean sendMessageDelayed(Message msg, long delayMillis)  
  17.     {  
  18.         if (delayMillis < 0) {  
  19.             delayMillis = 0;  
  20.         }  
  21.         return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);  
  22.     }  

選其一進行分析,就sendMessageDelayed()了,F3進去:

[java] view plain copy
  1. public boolean sendMessageAtTime(Message msg, long uptimeMillis)  
  2.     {  
  3.         boolean sent = false;  
  4.         MessageQueue queue = mQueue;  
  5.         if (queue != null) {  
  6. //把Message的target設置爲自己,然後加入消息隊列中  
  7.             msg.target = this;  
  8.             sent = queue.enqueueMessage(msg, uptimeMillis);  
  9.         }  
  10.         else {  
  11.             RuntimeException e = new RuntimeException(  
  12.                 this + " sendMessageAtTime() called with no mQueue");  
  13.             Log.w("Looper", e.getMessage(), e);  
  14.         }  
  15.         return sent;  
  16.     }  

以上工作是往Looper的消息隊列中加入了一個消息,按照Looper類分析可知,它在獲取消息後會調用target的dispatchMessage函數,再把這個消息分發給Handle處理,接着繼續看Handler.java:

[java] view plain copy
  1. public void dispatchMessage(Message msg) {  
  2. // 1,如果Message本身有callback,則直接交給Message的callback處理  
  3.         if (msg.callback != null) {  
  4.             handleCallback(msg);  
  5.         } else {  
  6. // 2,如果本Handle設置了mCallback,則交給它處理  
  7.             if (mCallback != null) {  
  8.                 if (mCallback.handleMessage(msg)) {  
  9.                     return;  
  10.                 }  
  11.             }  
  12. // 3,啥也沒有,就交給子類來處理  
  13.             handleMessage(msg);  
  14.         }  
  15.     }  

在絕大多數情況下,我們都是走的第三部流程,用一個從Handler派生的子類並重載handleMessage()來處理各種封裝的消息。

小結:1,Looper中有一個Message隊列,裏面儲存的是一個個待處理的Message。

             2,Message中有一個Handler,這個Handler是用來處理Message的。

四,實戰篇

1,主線程給自己發消息

[java] view plain copy
  1. <span style="font-family:'Microsoft YaHei';font-size:12px;">public class handler1 extends Activity {  
  2.         private Button btnTest;  
  3.         private EditText textView;  
  4.         private final String tag="hyj_handler1";  
  5.         private Handler handler;  
  6.     /** Called when the activity is first created. */  
  7.     @Override  
  8.     public void onCreate(Bundle savedInstanceState) {  
  9.         super.onCreate(savedInstanceState);  
  10.         setContentView(R.layout.main);  
  11.         btnTest = (Button)this.findViewById(R.id.bt1);  
  12.         textView = (EditText)this.findViewById(R.id.ed1);  
  13.           
  14.         btnTest.setOnClickListener(new View.OnClickListener() {  
  15.               
  16.             @Override  
  17.             public void onClick(View arg0) {  
  18.                   
  19.                 Looper looper = Looper.getMainLooper(); //主線程的Looper對象  
  20.                 //這裏以主線程的Looper對象創建了handler,  
  21.                 //所以,這個handler發送的Message會被傳遞給主線程的MessageQueue。  
  22.                 handler = new MyHandler(looper);  
  23.                 handler.removeMessages(0);  
  24.                 //構建Message對象  
  25.                 //第一個參數:是自己指定的message代號,方便在handler選擇性地接收  
  26.                 //第二三個參數沒有什麼意義  
  27.                 //第四個參數需要封裝的對象  
  28.                 Message msg = handler.obtainMessage(1,1,1,"主線程發消息了");  
  29.                   
  30.                 handler.sendMessage(msg); //發送消息  
  31.                 Log.i(tag, looper.toString());  
  32.                 Log.i(tag, msg.toString());  
  33.                 Log.i(tag, handler.toString());  
  34.             }  
  35.         });  
  36.     }  
  37.       
  38.     class MyHandler extends Handler{  
  39.           
  40.         public MyHandler(Looper looper){  
  41.             super(looper);  
  42.         }  
  43.           
  44.         public void handleMessage(Message msg){  
  45.             super.handleMessage(msg);  
  46.             textView.setText("我是主線程的Handler,收到了消息:"+(String)msg.obj);  
  47.         }  
  48.     }  
  49. }</span>  

2,其他線程給主線程發消息

[java] view plain copy
  1. <span style="font-family:'Microsoft YaHei';font-size:12px;">public class handler2 extends Activity{  
  2.   
  3.         private Button btnTest;  
  4.         private EditText textView;  
  5. //      private final String tag="hyj_handler2";  
  6.         private Handler handler;  
  7.  /** Called when the activity is first created. */  
  8.  @Override  
  9.  public void onCreate(Bundle savedInstanceState) {  
  10.      super.onCreate(savedInstanceState);  
  11.      setContentView(R.layout.layout2);  
  12.      btnTest = (Button)this.findViewById(R.id.bt2);  
  13.      textView = (EditText)this.findViewById(R.id.ed2);  
  14.        
  15.      btnTest.setOnClickListener(new View.OnClickListener() {  
  16.            
  17.          @Override  
  18.          public void onClick(View arg0) {  
  19.                
  20.              //可以看出這裏啓動了一個線程來操作消息的封裝和發送的工作  
  21.              //這樣原來主線程的發送就變成了其他線程的發送,簡單吧?呵呵  
  22.              new MyThread().start();    
  23.          }  
  24.      });  
  25.  }  
  26.    
  27.  class MyHandler extends Handler{  
  28.        
  29.      public MyHandler(Looper looper){  
  30.          super(looper);  
  31.      }  
  32.        
  33.      public void handleMessage(Message msg){  
  34.          super.handleMessage(msg);  
  35.          textView.setText("我是主線程的Handler,收到了消息:"+(String)msg.obj);  
  36.      }  
  37.  }  
  38.  class MyThread extends Thread{  
  39.        
  40.      public void run(){  
  41.          Looper looper = Looper.getMainLooper(); //主線程的Looper對象  
  42.          //這裏以主線程的Looper對象創建了handler,  
  43.          //所以,這個handler發送的Message會被傳遞給主線程的MessageQueue。  
  44.          handler = new MyHandler(looper);  
  45.          Message msg = handler.obtainMessage(1,1,1,"其他線程發消息了");        
  46.          handler.sendMessage(msg); //發送消息              
  47.      }  
  48.  }  
  49. }</span>  

3,主線程給其他線程發消息

[java] view plain copy
  1. <span style="font-family:'Microsoft YaHei';font-size:12px;">public class handler3 extends Activity{  
  2.     private Button btnTest;  
  3.     private EditText textView;  
  4.       
  5.     private Handler handler;  
  6.       
  7.     @Override  
  8.     public void onCreate(Bundle savedInstanceState) {  
  9.         super.onCreate(savedInstanceState);  
  10.         setContentView(R.layout.main);  
  11.           
  12.         btnTest = (Button)this.findViewById(R.id.bt1);  
  13.         textView = (EditText)this.findViewById(R.id.ed1);  
  14.           
  15.           
  16.         //啓動線程  
  17.         new MyThread().start();      
  18.           
  19.         btnTest.setOnClickListener(new View.OnClickListener() {  
  20.               
  21.             @Override  
  22.             public void onClick(View arg0) {  
  23.                 //這裏handler的實例化在線程中  
  24.                 //線程啓動的時候就已經實例化了  
  25.                 Message msg = handler.obtainMessage(1,1,1,"主線程發送的消息");  
  26.                 handler.sendMessage(msg);  
  27.             }  
  28.         });  
  29.     }  
  30.       
  31.     class MyHandler extends Handler{  
  32.           
  33.         public MyHandler(Looper looper){  
  34.             super(looper);  
  35.         }  
  36.           
  37.         public void handleMessage(Message msg){  
  38.             super.handleMessage(msg);  
  39.             textView.setText("我是主線程的Handler,收到了消息:"+(String)msg.obj);  
  40.         }  
  41.     }  
  42.       
  43.     class MyThread extends Thread{  
  44.           
  45.         public void run(){  
  46.             Looper.prepare(); //創建該線程的Looper對象,用於接收消息  
  47.               
  48.             //注意了:這裏的handler是定義在主線程中的哦,呵呵,  
  49.             //前面看到直接使用了handler對象,是不是在找,在什麼地方實例化的呢?  
  50.             //現在看到了吧???呵呵,開始的時候實例化不了,因爲該線程的Looper對象  
  51.             //還不存在呢。現在可以實例化了  
  52.             //這裏Looper.myLooper()獲得的就是該線程的Looper對象了  
  53.             handler = new ThreadHandler(Looper.myLooper());  
  54.               
  55.             //這個方法,有疑惑嗎?  
  56.             //其實就是一個循環,循環從MessageQueue中取消息。  
  57.             //不經常去看看,你怎麼知道你有新消息呢???  
  58.             Looper.loop();   
  59.   
  60.         }  
  61.           
  62.         //定義線程類中的消息處理類  
  63.         class ThreadHandler extends Handler{  
  64.               
  65.             public ThreadHandler(Looper looper){  
  66.                 super(looper);  
  67.             }  
  68.               
  69.             public void handleMessage(Message msg){  
  70.                 //這裏對該線程中的MessageQueue中的Message進行處理  
  71.                 //這裏我們再返回給主線程一個消息  
  72.                 handler = new MyHandler(Looper.getMainLooper());  
  73.                   
  74.                 Message msg2 = handler.obtainMessage(1,1,1,"子線程收到:"+(String)msg.obj);  
  75.                   
  76.                 handler.sendMessage(msg2);  
  77.             }  
  78.         }  
  79.     }</span>  

4,其他線程給其自己發消息

[java] view plain copy
  1. <span style="font-family:'Microsoft YaHei';font-size:12px;">public class handler4 extends Activity{  
  2.     Button bt1 ;  
  3.     EditText et1;  
  4.     Handler handler;  
  5.     public void onCreate(Bundle savedInstanceState){  
  6.         super.onCreate(savedInstanceState);  
  7.         setContentView(R.layout.main);  
  8.         bt1 = (Button)findViewById(R.id.bt1);  
  9.         et1 = (EditText)findViewById(R.id.ed1);  
  10.           
  11.         bt1.setOnClickListener(new View.OnClickListener() {  
  12.               
  13.             @Override  
  14.             public void onClick(View v) {  
  15.                 new ThreadHandler().start();  
  16.             }  
  17.         });  
  18.           
  19.           
  20.     }  
  21.       
  22. class MyHandler extends Handler{  
  23.           
  24.         public MyHandler(Looper looper){  
  25.             super(looper);  
  26.         }  
  27.           
  28.         public void handleMessage(Message msg){  
  29.             super.handleMessage(msg);  
  30.             et1.setText("我是主線程的Handler,收到了消息:"+(String)msg.obj);  
  31.         }  
  32.     }  
  33.       
  34.  public class ThreadHandler extends Thread{  
  35.         public void run(){  
  36.             Looper.prepare();  
  37.             handler = new Handlerthread(Looper.myLooper());  
  38.             Message msg = handler.obtainMessage(1,1,1,"我自己");  
  39.             handler.sendMessage(msg);  
  40.             Looper.loop();  
  41.         }  
  42.           
  43.         //定義線程類中的消息處理類  
  44.        public class Handlerthread extends Handler{     
  45.             public Handlerthread(Looper looper){  
  46.                 super(looper);  
  47.             }  
  48.               
  49.             public void handleMessage(Message msg){  
  50.                 //這裏對該線程中的MessageQueue中的Message進行處理  
  51.                 //這裏我們再返回給主線程一個消息  
  52.                 //加入判斷看看是不是該線程自己發的信息  
  53.                 if(msg.what == 1 && msg.obj.equals("我自己")){  
  54.                     handler = new MyHandler(Looper.getMainLooper());    
  55.                     Message msg2 = handler.obtainMessage(1,1,1,"稟告主線程:我收到了自己發給自己的Message");          
  56.                     handler.sendMessage(msg2);             
  57.                 //et1.setText("++++++threadhandler"+(String)msg.obj);  
  58.               
  59.         }}  
  60.        }  
  61.           
  62.     }  
  63. </span>  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章