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()方法:
-
publicclass Thread_Handler_Activity
- @Override
- protectedvoid onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_thread__handler_);
- LooperHandler thread=new LooperHandler();
- thread.start();
- }
- @Override
- publicboolean onCreateOptionsMenu(Menu menu) {
- // Inflate the menu; this adds items to the action bar if it is present.
- getMenuInflater().inflate(R.menu.activity_thread__handler_, menu);
- returntrue;
- }
- }
自定義執行緒類:
- publicclass LooperHandler extends Thread{
- public Handler handler;
- @Override
- publicvoid run() {
- // TODO Auto-generated method stub
- super.run();
- //初始化Looper物件
- Looper.prepare();
- //例項化一個Handler物件
- handler=new Handler(){
- @Override
- publicvoid handleMessage(Message msg) {
- // TODO Auto-generated method stub
- super.handleMessage(msg);
- Log.d("BruceZhang", "This is Test!!!");
- }
- };
- Message msg=handler.obtainMessage();
- msg.what=1;
- handler.sendMessage(msg);
- Looper.loop();
- }
- }
執行的結果是在日誌中顯示一條資訊,如圖所示執行結果:
但是,如果沒有對Looper的宣告,執行就會丟擲如下的異常:
所以,在實際的應用中,應考慮Handler在哪一個執行緒的中的實現。