天天看點

Android Handler消息處理機制詳解

原文連接配接,感謝~

前言

從我們學習android開始,幾乎每天都在和handler打交道.有了它,我們在子線程中處理好了耗時的操作,可以利用它來更新UI.它為我們線上程間的通信提供了很大的友善,而今天部落格就來詳細的介紹一下Handler的消息循環機制,一步一步的了解其中的奧妙,本文不介紹Handler的詳細使用,探究的是内部的原理.是以看這篇部落格的童鞋需要有handler的基本使用能力

本部落格基于Android 6.0源碼講解。

先抛出一個簡單的使用例子

public class DemoAct extends AppCompatActivity {  

        private Handler h = new Handler() {  
            @Override  
            public void handleMessage(Message msg) {  
                Toast.makeText(DemoAct.this, "收到啦", Toast.LENGTH_LONG).show();  
            }  
        };  

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

        /** 
         * 按鈕的點選事件 
         * 
         * @param view 
         */  
        public void clickView(View view) {  
            new Thread(){  
                @Override  
                public void run() {  
                    h.sendEmptyMessage();  
                }  
            }.start();  
        }  

    }  
           

效果圖:

Android Handler消息處理機制詳解

從圖中我們可以看到一個簡單的使用,那麼裡面的實作原理到底是怎麼樣的呢?

下面我們一起來探究!

Android Handler消息處理機制詳解

上圖以在主線程中做了一個圖解,畫出了大緻的流程

圖檔畫的是不是很棒?以後請叫我神筆馬良~~~(臉皮厚模式)

MessageQueue和Looper的介紹

在Android中,一個線程可以對應一個Looper對象,不能有多個,為什麼說可以呢,Looper作為一個消息的循環器,在一個線程中可以使用它也可以不使用它,是以一個線程中可以有一個Looper對象不能有多個.

說到了消息的循環器,就必須掰扯掰扯所謂的消息隊列MessageQueue.每一個Looper對象裡面都會維護一個消息隊列MessageQueue,它用來存放消息(Message),在MessageQueue中,存放的消息按照FIFO(先進先出)原則執行。

Handler的介紹

Handler如何發送消息

Handler是我們發送消息和處理消息的一個類,那麼在上述的圖解中,到底是如何實作發送消息的呢?

public final boolean sendEmptyMessage(int what)  
    {  
        return sendEmptyMessageDelayed(what, );  
    }  
           

上面是發送一空消息的源碼,可以看到調用了另一個方法,那麼點進去~~~

public final boolean sendEmptyMessageDelayed(int what,long delayMillis) {  
        Message msg = Message.obtain();  
        msg.what = what;  
        return sendMessageDelayed(msg, delayMillis);  
    }  
           

在這裡我們可以明确的看到,在我們發送消息的時候,如何發送的不是一個Message對象,而是一個空消息,那麼它也會自動為我們建立一個Message對象,說到底其實最後發送出去的肯定是一個Message對象

繼續點進去

public final boolean sendMessageDelayed(Message msg, long delayMillis)  
    {  
        if (delayMillis < ) {  
            delayMillis = ;  
        }  
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);  
    }  
           

這個方法中對延時的時間毫秒值進行了判斷,因為時間永遠不會倒流的嘛~~~

繼續點

“`

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {

MessageQueue queue = mQueue;

if (queue == null) {

RuntimeException e = new RuntimeException(

this + ” sendMessageAtTime() called with no mQueue”);

Log.w(“Looper”, e.getMessage(), e);

return false;

}

return enqueueMessage(queue, msg, uptimeMillis);

}

在這個方法中對一個成員變量進行了為空的判斷,但是并不抛出,而是将異常列印一下,那麼這個成員變量又是什麼呢?
其實他就是這個handler所線上程的Looper中的消息隊列!咦,你之前不是說消息隊列在Looper裡面麼?怎麼在Handler裡面也有一份啊?
瞧瞧handler的構造函數便可知道!


           

public Handler(Callback callback, boolean async) {

if (FIND_POTENTIAL_LEAKS) {

final Class

從構造函數中我們可以看到,從Looper裡面拿到目前線程的Looper對象,然後從裡面拿出來消息隊列,是以這裡就解釋了上面那個成員變量的問題
那麼繼續點之前的方法
           

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {

msg.target = this;

if (mAsynchronous) {

msg.setAsynchronous(true);

}

return queue.enqueueMessage(msg, uptimeMillis);

}

到這裡位置,消息總算被送進了消息隊列中,上面講述了Handler是如何發送消息的。
####Handler是如何處理消息的
我們在上面的流程圖中可以看出,取出消息是誰幹的?是Looper,沒錯就是它,是以我們就去看Looper的代碼
Looper.loop方法是重點方法:

           

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.recycleUnchecked();  
       }  
   }  
           
從這個方法的注釋來看,我們就可以知道這個方法的功能:
也就是此方法開啟了一個死循環來拿出每一個消息,拿出來之後,通過消息對象中的target對象(其實就是之前發送消息的Handler的一個引用)的dispatchMessage(Message msg)方法來分發(處理)消息,貼出分發(處理)的代碼:
 ```
  /** 
     * Handle system messages here. 
     */  
    public void dispatchMessage(Message msg) {  
        if (msg.callback != null) {  
            handleCallback(msg);  
        } else {  
            if (mCallback != null) {  
                if (mCallback.handleMessage(msg)) {  
                    return;  
                }  
            }  
            handleMessage(msg);  
        }  
    }  
           

代碼很簡單,判斷了兩個callback(後面解釋),都為null的話就調用handleMessage(msg),此方法就是我們使用handler的時候最常用的方法了,上面的示例代碼中就是重寫了這個方法,可以回頭看看~~~

handler中的callback是什麼?

在Handler源碼中有如下一個接口:

/** 
     * Callback interface you can use when instantiating a Handler to avoid 
     * having to implement your own subclass of Handler. 
     * 
     * @param msg A {@link android.os.Message Message} object 
     * @return True if no further handling is desired 
     */  
    public interface Callback {  
        public boolean handleMessage(Message msg);  
    }  

           

在Message對象中有這麼一段:

/*package*/ Bundle data;

    /*package*/ Handler target;

    /*package*/ Runnable callback;
           

是以上面提到的在分發(處理)消息的時候的mCallback其實就是一個接口,接口的方法也是handleMessage.

那還有一個是Message對象中的callback,這個是一個Runnable接口

請注意:這個雖然是Runnable接口,但是别看這個是線程中經常用到的接口,你就認為這裡面可以處理耗時的操作,這裡是不允許的,否則會阻塞主線程

是以你建立Handler的時候就可以為所欲為了:

1.使用Handler中的Callback接口
private Handler h = new Handler(new Handler.Callback() {  
        @Override  
        public boolean handleMessage(Message msg) {  

            return false;  
        }  
    });  
           

這裡的傳回值是控制消息是否繼續傳遞給handler中的handleMessage方法執行

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

從消息的分發(處理)這裡可以看到,如果傳回了true,那麼handler中的handleMessge方法是不會被執行的

2.使用Message對象中的callback
Message m = Message.obtain(h, new Runnable() {  
            @Override  
            public void run() {  
                    //做一些事情,這個run方法是主線程調用的  
            }  
    });  
    h.sendMessage(m);  
           

這就是Handler處理消息的過程,你是否對Handler的發送和處理消息有一定的了解了呢?

一些大家應該比較想問的問題

消息循環器,我并沒有開啟,也就是并沒有在主線程中調用Looper.loop,為什麼主線程中的消息循環器就有作用呢?

答案在這:

public static void main(String[] args) {  
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");  
        SamplingProfilerIntegration.start();  

        // CloseGuard defaults to true and can be quite spammy.  We  
        // disable it here, but selectively enable it later (via  
        // StrictMode) on debug builds, but using DropBox, not logs.  
        CloseGuard.setEnabled(false);  

        Environment.initForCurrentUser();  

        // Set the reporter for event logging in libcore  
        EventLogger.setReporter(new EventLoggingReporter());  

        AndroidKeyStoreProvider.install();  

        // Make sure TrustedCertificateStore looks in the right place for CA certificates  
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());  
        TrustedCertificateStore.setDefaultUserDirectory(configDir);  

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

        Looper.prepareMainLooper();  

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

        if (sMainThreadHandler == null) {  
            sMainThreadHandler = thread.getHandler();  
        }  

        if (false) {  
            Looper.myLooper().setMessageLogging(new  
                    LogPrinter(Log.DEBUG, "ActivityThread"));  
        }  

        // End of event ActivityThreadMain.  
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);  
        Looper.loop();  

        throw new RuntimeException("Main thread loop unexpectedly exited");  
    }  
           

這是ActivityThread類中的main方法,可以看到裡面幫我們初始化了Looper

對應的代碼是:Looper.prepareMainLooper()

開啟了消息循環器

對應的代碼是:Looper.loop();

建立多個Handler來處理消息,為什麼可以區分開,而不幹擾

在發送消息的時候,有這麼一段:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {  
        msg.target = this;  
        if (mAsynchronous) {  
            msg.setAsynchronous(true);  
        }  
        return queue.enqueueMessage(msg, uptimeMillis);  
    }  
           

可以很清楚的看到,消息對象Message對象的target就是這個發送消息的handler,是以處理消息的時候就是根據這個target找到原來的那個handler,然後交由它來處理

Looper中的loop()方法是死循環,為什麼沒有卡死

Android Handler消息處理機制詳解

我用紅框框框住的就是從消息隊列中擷取下一個消息,後面有一句注釋,說這個方法可能會阻塞,是以在沒有消息的時候是會阻塞的

有的人可能還會有疑問:

那我沒有發送消息,一個app中都沒有發送任何handler消息,那為啥app還是正常走,也沒有卡死

答:你沒有發送消息,不代表系統中沒有發送消息,在Android中使用了大量的Handler消息機制。

以上。

筆記:

1.handler處理消息的時候

/**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
           

這2個分别是什麼時候處理?

第一個callback是msg的calback,是一個Runable,第二個callbak是Handler 中的一個interface。

Handler handler  = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                return false;
            }
        });
           
handler.post(new Runnable() {
            @Override
            public void run() {

            }
        });
           

點開post()

public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), );
    }
           

再點開getPostMessage(r):

private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
           

這個時候我們初始化Runnable。

2. 建立Handler對象必須先初始化一個Looper

否則會出現如下錯誤:

Activity在被建立的時候,ActivityThread.java(package android.app;)會幫我們初始化一個Looper對象,是以在主線程中,我們不必去調用Looper.prepare()去初始化Looper對象。

這裡順便了解到 Toast為何必須在主線程或者初始化了Looper的線程中執行show操作 ,也是一個意思。具體的可以看看該文。