Handler機制解析

1.Handler是Android中用來在線程中傳遞消息的工具,它提供了一種異步的回調機制,使在完成了一個相對耗時的操作後作出相應和通知。

2.Handler的使用,爲了能讓handler在線程間傳遞消息,還需要使用到Looper,messageQueue,message。

   Looper是爲了指定的單一線程創建一個消息循環,與線程一一對應,在UI線程中會自動建立一個Looper,

   而在子線程中則需要手動創建或者使用主線程的Looper。

   在Looper與線程進行關聯時會同時產生一個messageQueue消息隊列,用來存放handler所發送的message,遵循先進先出原則。

   message包含必要的描述和屬性數據,並且此對象可以被髮送給Handler處理,屬性字段:arg1、arg2、what、obj、replyTo等。

   what是用來保存消息標示的;obj是Object類型的任意對象;replyTo是消息管理器,會關聯到一個handler,handler就是處理其中的消息。

   通常Message可以直接new出來的,但推薦調用handler中的obtainMessage方法來直接獲得Message對象。

   (從系統線程池中直接取出,可以避免message的創建和銷燬,從而節省資源。)

   在主線程(UI)中使用handler很簡單,只需要創建一個handler對象,並實現他的handleMessage方法,在該方法中對接收到的消息作出相應處理。

3.handler發送消息的機制。在使用handler.sendEmptyMessage(0);發送一個消息對象後,message對象會被放入一個messageQueue隊列中,

   而該隊列屬於某個Lopper對象,每個Looper對象通過ThreadLocal.set(new Looper())跟一個Thread進行綁定,Looper對象所屬的線程在Looper.Loop

   方法中循環執行從messageQueue消息隊列中讀取message對象並把message對象交由handler處理,調用handler的dispatchMessage方法。

4.子線程中的handler,當在新建的子線程中建立handler時,程序會報RuntimeException異常,產生這個異常的原因是因爲子線程沒有建立Looper,

   爲什麼主線程中不會報錯 呢?來看一下ActivityThread的源碼:

public static final void main(String[] args) {
        SamplingProfilerIntegration.start();

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        Looper.loop();

        if (Process.supportsProcesses()) {
            throw new RuntimeException("Main thread loop unexpectedly exited");
        }

        thread.detach();
        String name = (thread.mInitialApplication != null)
            ? thread.mInitialApplication.getPackageName()
            : "<unknown>";
        Slog.i(TAG, "Main thread of " + name + " is now exiting");
    }
可以看到,在main函數中它已經做了這個事情了,調用 Looper.prepareMainLooper(); Looper.loop();在prepareMainLooper方法中建立了一個looper對象,並且與當前的進程進行綁定,在Looper.loop方法中,線程建立消息循環機制,循環從MessageQueue獲取Message對象,調用  msg.target.dispatchMessage(msg);進行處理msg.target在myThreadHandler.sendEmptyMessage(0)設置進去的,因爲一個Thead中可以建立多個Hander,通過msg.target保證MessageQueue中的每個msg交由發送message的handler進行處理,然而Handler是怎樣與Looper建立聯繫呢,打開Handler構造函數中會發現這樣一段代碼:

mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
當新建Handler對象時需要設置mLooper成員,Looper.myLooper是從當前線程中獲取綁定的Looper對象:

public static final Looper myLooper() {
		return (Looper)sThreadLocal.get();
	}
若Looper對象沒有創建,就會拋出"Can't create handler inside thread that has not called Looper.prepare()"這樣一個異常。

所以想要在子線程中創建一個Handler對象就需要以下這種方式:

class MyThread extends Thread {
		public void run() {
			// 其它線程中新建一個handler
			Log.i(TAG, MessageFormat.format("Thread run...", Thread.currentThread().getName()));
			// 創建該線程的Looper對象,用於接收消息,在非主線程中是沒有looper的所以在創建handler前一定要使用prepare()創建一個Looper
			Looper.prepare();
			newThreadHandler = new Handler() {
				public void handleMessage(Message msg) {
					Log.d(Constant.TAG, MessageFormat.format(
							"newThreadHandler run...", Thread.currentThread().getName()));
				}
			};
			Looper.myLooper().loop();// 建立一個消息循環,該線程不會退出
		}
	}


在其它線程中Handler使用主線程的Looper,前面我說了在新線程中要新建一個Handler需要調用Looper.prepare();

也有另一種方法就是使用主線程中的Looper,那就不必新建Looper對象了:

getMainLoopHandler =new Handler(Looper.getMainLooper()){
        public void handleMessage(android.os.Message msg) {
                Log.i(TAG, MessageFormat.format("handleMessage run...", Thread
                                .currentThread().getName()));                                        
        }
        //該handleMessage方法將在UI線程中執行
};
這時候注意不要在handleMessage做太多的操作,因爲它在主線程中執行,會影響主線程執行ui更新操作。

使用Message.callback回調

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
從dispatchMessage定義可以看出,如果Message對象自帶callback對象,handler不會執行handleMessage方法而是執行message.callback中定義的run方法,當然callback還是在handler關聯的looper所綁定的線程中執行的。實際上Handler.post(Runnable r)方法就是把r添加到一個msg.callback的,也就是說,下面兩種寫法,沒有什麼區別:

1.使用Message.callback

Message msg = Message.obtain(newThreadHandler,new Runnable() {
	@Override
	public void run() {
		Log.i(TAG, MessageFormat.format("newThreadHandler.Message.callback.run",
				Thread.currentThread().getName()));	
	}
});
newThreadHandler.sendMessage(msg);

2.使用Handler.post

newThreadHandler.post(new Runnable() {				
	@Override
	public void run() {
        Log.i(TAG, MessageFormat.format("newThreadHandler.Message.callback.run",
			Thread.currentThread().getName()));	
	}
});






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章