Handler機制源碼分析(異步一)

   一.場景

           Handler 機制是Android異步消息的核心(線程間通信), 其實Handler不侷限於子線程與主線程(UI線程) ,我們完全可以創建一個子線程,然後初始化Looper,Handler,我們可以通過Handler在其他線程(包括主線程)往該子線程發送消息。例如系統幫我們封裝好的--> HandlerThread (一個封裝好Looper,MessageQueueu的線程(不死線程)),發送消息給子線程有什麼好處呢, 這裏拿HandlerThread說明,現在有個場景, 我們都知道UI線程不能做耗時的操作, 會影響程序的性能,用戶體驗,所以耗時(IO,網絡,數據庫操作)操作全部往子線程堆。怎麼解決呢, 我們可以創建一個子線程HandlerThread, 拿到該線程的Looper--HandlerThread.getLooper(),然後可以new Handler(Looper).post(Runnable r)往該線程發送消息, 然後就會在該子線程執行消息;

  二. 重要的概念(Handler , Looper,MessageQueue,Message)

          Handler   看看官方是怎麼說的吧  A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue ,簡單說Handler主要的作用就是往MessageQueue插入一個消息

     Looper --Looper作用是從MessageQueue讀取Message,然後交給Handler處理消息,另外Looper還可以保證線程不死

    MessageQueue 消息隊列, 鏈表插入和刪除方便, 主要用於存放消息(Message) 

    Message 消息(消息會包裝發送Message的Handler,詳細見下面 一一源碼驗證)

 三.源碼解析(UI線程應用Handler機制)

<span style="color:#3333ff;">	</span>private static final TaskHandler TASK_HANDLER = new TaskHandler();
	public void doClick(View v) {
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				try {
					Thread.sleep(3000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				Message message = Message.obtain();
				message.arg1 = ++i;
				TASK_HANDLER.sendMessage(message);
			}
		}).start();
		
	}

	static final class TaskHandler extends Handler {
		@Override
		public void handleMessage(Message msg) {
			Log.i("Info", "message:"+msg.arg1);
		}
	}

     上面代碼很簡單, 估計大家都會, 在子線程中睡三秒, 然後往UI線程發送一個消息。很簡單就完成了一個子線程和UI線程的通信,並且沒有任務問題, 其實,Android在背後爲我們做了很多封裝和處理了,纔可以往UI線程發送消息, 不信見如下:

	new Thread(new Runnable() {


			@Override
			public void run() {
				handler = new Handler();
				handler.sendEmptyMessage(0x33);
			}
		}).start();

	}

   在子線程初始化一個Handler,然後發送一個消息。結果:

07-10 15:12:53.251: E/AndroidRuntime(5296): FATAL EXCEPTION: Thread-158
07-10 15:12:53.251: E/AndroidRuntime(5296): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
07-10 15:12:53.251: E/AndroidRuntime(5296): at android.os.Handler.<init>(Handler.java:121)
07-10 15:12:53.251: E/AndroidRuntime(5296): at com.example.just_intentservice.MainActivity$1.run(MainActivity.java:25)
07-10 15:12:53.251: E/AndroidRuntime(5296): at java.lang.Thread.run(Thread.java:856)

拋出異常了,大概意思是說沒有,不能在沒有初始化Looper的線程去創建Handler;

 點進Handler源碼查看下,

 public 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;
        mCallback = null;
    }
  先會到當前線程查找Looper ,如果沒有Looper就會拋出上面的異常。所以不能在沒有初始化Looper的線程初始化Handler,UI線程之所以可以初始化Handler,我們見以下,Android UI線程是如何初始化Looper吧...

    Java應用程序入口都是從mian函數, Android也不例外,只不過被SDK用hide標識着,我們無法查看,看下Android,UI線程是如何玩轉Handler機制的,在ActivityThread裏面main函數代碼如下

public static void main(String[] args) {
    //******省略若干代碼****//
        Looper.prepareMainLooper();//初始一個Looper
        Looper.loop();//開始無限循環讀取消息
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
   我們代碼追蹤看Android是如何初始換Looper,在Looper類裏面

 public static void prepareMainLooper() {//初始化MainLooper也是UI線程的Looper
        prepare();
        setMainLooper(myLooper());
    }
 public static void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }
private Looper() {
        mQueue = new MessageQueue();//初始化一個MessageQueue
        mThread = Thread.currentThread();
    }

  

  先判斷下當前線程是否存在Looper,存在拋出異常, 也就是說一個線程自能有一個Looper, 如果沒有,則new出一個Looer(在構造方法裏面初始化了一個對應的MessageQueue), 然後通過sThreadLocal保存起來, ThreadLocal用於線程保存對象 。然後來看下

  setMainLooper(myLooper()); 
public static Looper myLooper() {
        return sThreadLocal.get();
    }
 private synchronized static void setMainLooper(Looper looper) {
        mMainLooper = looper;
    }
可以看出myLooper就是從當前線程中拿出Looper,然後返回,setMainLooper(),就更簡單了,其實就是保存個全局looper。
  就這樣UI線程就吧Looper初始化完成了,所以Handler可以正常的在主線程初始化;

下面我們來走一把完整的Handler機制通信流程     

handler.sendEmptyMessage(0x33);      <span style="font-family: Consolas, 'Liberation Mono', Menlo, Monaco, Courier, monospace;">----子線程</span>
 public final boolean sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);// 包裝下時間調用兩個參數<span style="font-family: Consolas, 'Liberation Mono', Menlo, Monaco, Courier, monospace;">sendEmptyMessageDelayed方法</span>

    }
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();//獲取一個Message
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }
 public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;//如果時間小於0則賦值爲0
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
    {
        boolean sent = false;
        MessageQueue queue = mQueue;
        if (queue != null) {//對消息隊列判空
            msg.target = this;//this表示當前handler
            sent = queue.enqueueMessage(msg, uptimeMillis);//進入隊列
        }
        else {
            RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
        }
        return sent;
    }
     子線程的handler發送一個簡單的空消息到UI線程, 要經過不斷的包裝, 可以看出,最後都是包裝Message然後進入隊列(MessageQueue)其實post(Runnable r)最後也是經過包裝成Message對象,發送到隊列, 這裏就不做說明了, 詳細見源碼。 我們知道Looper在UI線程初始化完成就開始無限死循環讀取MessageQueue裏面消息,我們看Looper是如何讀取和處理消息的:

public static void loop() {
        Looper me = myLooper();//獲取當前線程的Looper
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        MessageQueue queue = me.mQueue;   //獲取Looper裏面的消息隊列     
        while (true) { //死循環讀取
            Message msg = queue.next(); // might block 從隊列裏面拿到消息
            if (msg != null) {
                if (msg.target == null) {
                    // No target is a magic identifier for the quit message.
                    return;
                }      
              //省略一些代碼
                msg.target.dispatchMessage(msg);//從message中拿到handler,然後調用<span style="font-family: Consolas, 'Liberation Mono', Menlo, Monaco, Courier, monospace;">dispatchMessage</span>
                msg.recycle();//最終銷燬消息
            }
        }
    }
  Handler類裏面的dispatchMessage如下:

 public void dispatchMessage(Message msg) {
        if (msg.callback != null) {//callback其實是一個Runnable對象, 一般post(Runnable r)是執行
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);//回調handlerMessage函數--一般該方法由我們繼承去實現的
        }
    }
private final void handleCallback(Message message) {
        message.callback.run();//執行run方法體
    }
   說明下, 子線程handler.sendEmptyMessage()當Message到達MessageQueue,這裏就是子線程切換到UI線程了, 因爲MessageQueue跑在UI線程,上面代碼可以看出,Looper會不斷的在MessageQueue裏面讀取消息,然後讀到的message爲空的化return掉, 其實裏面還有一些喚醒機制(保證UI線程的不阻塞,當消息過來了才喚醒,這裏不細說)如果message不爲空,則從message中取出handler,然後調用handler的dispathMessage(Message message)方法來方法消息, 如果消息爲post(Runnable r)則走handlerCallback,否則走handleMessage(msg),整個消息通信結束。


   通過源碼分析, 我們完全可以創建一個不死子線程,然後在主線程中往子線程發送消息:

private Handler handler;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		new Thread(new Runnable() {

			@Override
			public void run() {
				Looper.prepare();
				handler = new Handler(Looper.myLooper()) {
					@Override
					public void handleMessage(Message msg) {
						if (msg.what == 0x88) {
							Log.i("Info", "i am sub");
						}

					}
				};
				Looper.loop();
				
			}
		}).start();

	}
public void doClick(View v) {

		handler.sendEmptyMessage(0x88);

	}

最後總結:Handler往MessageQueue插入Message,Looper往messageQueue裏面讀取message,最後都是交給Handler分發處理。













      


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