1. 程式人生 > >Android多執行緒中的Handler機制、Looper的介紹與整理

Android多執行緒中的Handler機制、Looper的介紹與整理

在多執行緒的開發中,Handler機制如同在主執行緒中執行一樣,只是需要注意在非主執行緒中Handler機制的使用限制,本文將對這些內容作出解釋。

如果在子執行緒中對上UI介面進行操作,將丟擲異常。為此,Android中引入了Handler訊息
 傳遞機制,來實現在子建立的執行緒中更新UI介面,下面將對Handler訊息傳遞機制進行介紹。


 一.Looper簡介
 1.首先需要知道一個概念,那就是MessageQueue,在Android中,一個執行緒對應一個Looper物件
  ,而一個Looper物件又對應一個MessageQueue(訊息佇列)。MessageQueue用於存放Message,
 在MessageQueue中,存放的訊息以佇列的模式執行。
  2.Looper物件用來為一個執行緒開啟一個訊息迴圈,用來操作MessageQueue。預設情況下,Android
 中新建立的執行緒是沒有開啟訊息迴圈的,但是主執行緒除外,系統自動為主執行緒建立Looper物件,開啟訊息循 環。所以,在主執行緒中,應用下面的程式碼建立Handler物件時,不會出錯。而如果在新建立的非主執行緒中,應用下面的程式碼建立Handler物件時,將產生異常資訊。
 如果想要在非主執行緒中,建立Handler物件,首先要使用Looper類的prepare()方法來初始化一個
 Looper物件,然後建立這個Handler物件,再使用Looper物件的loop()方法,啟動Looper,從訊息佇列裡
 獲取和處理訊息。

原始碼分析 :

    
     /** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    public static final void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }


    /**
     *  Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static final void loop() {
        Looper me = myLooper();
        MessageQueue queue = me.mQueue;
        
        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();
        
        while (true) {
            Message msg = queue.next(); // might block
            //if (!me.mRun) {
            //    break;
            //}
            if (msg != null) {
                if (msg.target == null) {
                    // No target is a magic identifier for the quit message.
                    return;
                }
                if (me.mLogging!= null) me.mLogging.println(
                        ">>>>> Dispatching to " + msg.target + " "
                        + msg.callback + ": " + msg.what
                        );
                msg.target.dispatchMessage(msg);
                if (me.mLogging!= null) me.mLogging.println(
                        "<<<<< Finished to    " + msg.target + " "
                        + msg.callback);
                
                // Make sure that during the course of dispatching the
                // identity of the thread wasn't corrupted.
                final long newIdent = Binder.clearCallingIdentity();
                if (ident != newIdent) {
                    Log.wtf("Looper", "Thread identity changed from 0x"
                            + Long.toHexString(ident) + " to 0x"
                            + Long.toHexString(newIdent) + " while dispatching to "
                            + msg.target.getClass().getName() + " "
                            + msg.callback + " what=" + msg.what);
                }
                
                msg.recycle();
            }
        }
    }

    public void quit() {
        Message msg = Message.obtain();
        // NOTE: By enqueueing directly into the message queue, the
        // message is left with a null target.  This is how we know it is
        // a quit message.
        mQueue.enqueueMessage(msg, 0);
    }

再看下Handler的建構函式,在子執行緒中如果沒有呼叫Looper.prepare()就new Handler()則會丟擲異常。程式碼如下 :
    /**
     * Default constructor associates this handler with the queue for the
     * current thread.
     *
     * If there isn't one, this handler won't be able to receive messages.
     */
    public Handler() {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = null;
    }


 3.Looper物件
 提供了幾個方法:
 prepare()----  用於初始化Looper
 loop()----   用於開啟訊息迴圈,當呼叫了loop()方法後,Looper執行緒就真正的開始工作了,它會從訊息佇列中
                      獲取訊息並處理訊息
 quit()----   用於結束Looper訊息迴圈
 
 注意:
 在loop()之後的程式碼不會被執行,這個函式內部是一個訊息迴圈,除非呼叫quit()方法,loop()才會終止,
 其後面的程式碼才能得以執行。

原始碼如下:

onCreate()方法:

  1. publicclass Thread_Handler_Activity 
    extends Activity {  
  2.     @Override
  3.     protectedvoid onCreate(Bundle savedInstanceState) {  
  4.         super.onCreate(savedInstanceState);  
  5.         setContentView(R.layout.activity_thread__handler_);  
  6.         LooperHandler thread=new LooperHandler();  
  7.         thread.start();  
  8.     }  
  9.     @Override
  10.     publicboolean onCreateOptionsMenu(Menu menu) {  
  11.         // Inflate the menu; this adds items to the action bar if it is present.
  12.         getMenuInflater().inflate(R.menu.activity_thread__handler_, menu);  
  13.         returntrue;  
  14.     }  
  15. }  

自定義執行緒類:

  1. publicclass LooperHandler extends Thread{  
  2.     public Handler handler;  
  3.     @Override
  4.     publicvoid run() {  
  5.         // TODO Auto-generated method stub
  6.         super.run();  
  7.         //初始化Looper物件
  8.         Looper.prepare();  
  9.         //例項化一個Handler物件
  10.         handler=new Handler(){  
  11.             @Override
  12.             publicvoid handleMessage(Message msg) {  
  13.                 // TODO Auto-generated method stub
  14.                 super.handleMessage(msg);  
  15.                 Log.d("BruceZhang""This is Test!!!");  
  16.             }  
  17.         };  
  18.         Message msg=handler.obtainMessage();  
  19.         msg.what=1;  
  20.         handler.sendMessage(msg);  
  21.         Looper.loop();  
  22.     }  
  23. }  

執行的結果是在日誌中顯示一條資訊,如圖所示執行結果:


但是,如果沒有對Looper的宣告,執行就會丟擲如下的異常:


所以,在實際的應用中,應考慮Handler在哪一個執行緒的中的實現。

相關推薦

no