Looper,Handler,Message探究

本文是在閱讀張鴻陽(yáng)的深入理解 Looper、Handler、Message三者關(guān)系的一個(gè)自我學(xué)習(xí)總結(jié)

Looper

Looper主要作用:

  1. 綁定當(dāng)前線程,new一個(gè)MessageQueue并綁定 ,且保證一個(gè)線程只有一個(gè)Looper
  2. 不斷進(jìn)行消息輪詢,有則分發(fā),無(wú)則阻塞

Looper主要是prepare()和loop()兩個(gè)方法。

  1. prepare()方法
    ** 作用:初始化Looper,保證一個(gè)線程初始化一次**
    public static final void prepare() {
    if (sThreadLocal.get() != null) {
    throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(true));
    }
    sThreadLocal是一個(gè)ThreadLocal對(duì)象,可以在一個(gè)線程中存儲(chǔ)變量。將一個(gè)Looper的實(shí)例放入了ThreadLocal,判斷sThreadLocal是否為null,否則拋出異常。** 說(shuō)明了Looper.prepare()方法不能被調(diào)用兩次,同時(shí)也保證了一個(gè)線程中只有一個(gè)Looper實(shí)例.**

  2. 構(gòu)造方法Looper()
    ** 作用:綁定當(dāng)前線程,new一個(gè)MessageQueue并綁定**

    private Looper(boolean quitAllowed) {
     mQueue = new MessageQueue(quitAllowed);
     mRun = true;
     mThread = Thread.currentThread();
    }
    

new了一個(gè)MessageQueue(消息隊(duì)列)給全局變量mQueue,獲取當(dāng)前線程賦給全局變量mThread,在prepare()調(diào)用了構(gòu)建方法.

  1. loop()方法
    ** 不斷輪詢并有消息分發(fā)消息 **

    public static void loop() {
     final Looper me = myLooper();
     if (me == null) {
         throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
     }
     final 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();
    
     for (;;) {
         Message msg = queue.next(); // might block
         if (msg == null) {
             // No message indicates that the message queue is quitting.
             return;
         }
    
         // This must be in a local variable, in case a UI event sets the logger
         Printer logging = me.mLogging;
         if (logging != null) {
             logging.println(">>>>> Dispatching to " + msg.target + " " +
                     msg.callback + ": " + msg.what);
         }
    
         msg.target.dispatchMessage(msg);
    
         if (logging != null) {
             logging.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(TAG, "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 static @Nullable Looper myLooper() {    return sThreadLocal.get();}
    

a. 首先在sThreadLocal取一個(gè)Looper.
b. 然后不斷輪詢消息隊(duì)列,當(dāng)有消息取出的時(shí)候, msg.target.dispatchMessage(msg);msg.targer實(shí)際為Handle,那么這個(gè)** Handle怎樣與Message關(guān)聯(lián),Message怎么進(jìn)入消息隊(duì)列的呢? **
c. 釋放msg,msg.recycle();

Handle

  1. 構(gòu)造方法Handle()**
    作用:獲取當(dāng)前線程中Looper,獲取 Looper的Queue賦給全局變量 **
    public Handler(Callback callback, boolean async) {
    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 = callback;
     mAsynchronous = async;
    

    }

  2. 發(fā)送消息sendMesssage(Message msg)
    ** 作用:使用全局mQueue發(fā)送消息,并且將msg.targer設(shè)為this(Handle),解釋了Handle如何與MessageQueue,msg關(guān)聯(lián) **

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
     msg.target = this;
     if (mAsynchronous) {
         msg.setAsynchronous(true);
     }
     return queue.enqueueMessage(msg, uptimeMillis);
     }
    
  3. 接收處理消息

    public void dispatchMessage(Message msg) { 
    if (msg.callback != null) {
    handleCallback(msg);
    }  else {
     if (mCallback != null) { 
    if (mCallback.handleMessage(msg)) { return; } 
     } 
    handleMessage(msg); 
    }
    }
    

覆寫(xiě)Handle的handleMessage就能處理消息了.

Message,Messenger

Message作為傳遞中的數(shù)據(jù)載體.使用Message#obtian()進(jìn)行構(gòu)建,obj用Bundle傳遞序列化參數(shù).
Messenger相當(dāng)于Handle的封裝,用于多進(jìn)程間通信.

后記

面試上我有寫(xiě)這個(gè),面試的時(shí)候老喜歡讓我講講Handle,筆試碰到幾率也高。我一般三句話就完了:
1.Looper輪詢器,不斷輪詢,有消息取出,沒(méi)則阻塞。
2.每個(gè)線程只有一個(gè)輪詢器,消息隊(duì)列。
3.message是消息載體。
這答案我自己聽(tīng)了都想哭。
所以,我回來(lái)再看一般,然后整理一個(gè)答案:
1.初始化Handle實(shí)例的時(shí)候獲取當(dāng)前線程的Looper輪詢器。
2.將Looper中的消息隊(duì)列賦值給Handle中對(duì)象中全局變量。
3.Handle利用消息隊(duì)列發(fā)送消息,消息發(fā)的targer設(shè)置成自己。所以能夠跨線程通信。
4.Looper的parper方法保證每個(gè)線程只初始化一次,構(gòu)造方法綁定當(dāng)前線程,new出消息隊(duì)列。loop方法不斷輪詢消息,有讀取,無(wú)阻塞。
還有會(huì)讓你說(shuō)說(shuō)線程間通信,Handle是要講的,用在主線程與子線程通信,因?yàn)樽泳€程無(wú)法更新ui.
但是一般線程間的通信其實(shí)是共享同進(jìn)程內(nèi)存,讀取共享的內(nèi)存變量(大家都能訪問(wèn)到,比如全局變量,普通類(lèi)的static變量)就能實(shí)現(xiàn)簡(jiǎn)單的通信了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容