Android中 handler , looper 以及 message 的關係

聲明:


    在我的工作中用到了handler,looper 以及 message的關係,所以我在這裏總結一下自己對他們關係的瞭解,同時也會在後面介紹如何使用他們來完成自己的任務。同時還是強調,這是我自己的一些工作總結,可能有不完善的地方, 請大家指正,希望對你有所幫忙。同時這裏面有很多的looper,handler,以及message等,其實他們的首字母是大寫的,只是我這裏爲了方便就使用小寫了,所以你看的時候需要自己理解下。同時我這裏有的時候會稱呼方法爲函數,這是我在c中習慣的稱呼,希望你們不要介意

一. handler,looper,MessageQueue等的關係介紹


    在開始講解之前,我希望大家可以對這幾個名詞有個全局的概念或者可以清楚他們的關係,所以使用下圖來表示:

    上面的圖片是我看文章的時候發現的一個很好的圖示,我也會在這篇文章的參考文章中列出這位大神的博客,講的非常詳細大家可以去看一下。這裏我就使用這個圖來解釋一下他們的關係,從上面的圖中我們可以看出:線程(thread),Looper以及MessageQueue是一一對應的。也就是說,我們創建一個線程:thread,那麼他有且只能有一個Looper與之對應,而一個looper有且只有一個MessageQueue與之對應。而在上面的圖中顯示handler與thread,looper等的關係是調用操作的關係,那麼我們就要問了,handler與thread,looper等也是一一對應的嗎? 這個就不是了,一個thread(looper)可以對應多個handler。具體爲什麼會是這樣,我會在後面的代碼分析中講到。

 

二. 如何使用

    上面簡單的講解了他們之間的關係,下面我們簡單講解一下如何使用他們。我們知道在Android中使用他們是用來實現信息在不同的線程間傳遞的,而他們的操作是如何實現的那? 我們也用一個圖來說明:

    從上圖看要想實現在線程1中將信息傳遞給線程2,需要下面的幾步:

1. 在線程2中創建一個handler,同時需要將這個handler與looper綁定(而每個looper都有對應的MessageQueue)

2. 在線程1中擁有線程而的handler(這個可以將線程2的handler作爲線程1中的一個成員)

3. 在線程1中將獲得的信息通過線程2的handler的sendMessage函數或者post等函數將message放入到handler對應的MessageQueue中

4. 在looper的loop函數中獲得MessageQueue中的message,並將這個信息發送到handler的回調函數handleMessage中處理

 

三. 工作中如何使用

    下面我以我工作中用到的上面的信息傳遞方式來講解。在我的工作中我們需要從一個信息處理線程中提取出部分需要特殊處理的事件,並對這部分事件做特殊的操作。具體方法爲:

1. 我們新建一個線程來對特殊的事件進行處理,而這個線程會綁定一個自己的looper以及會有自己的handler

2. 在信息處理的線程中使用新建線程的handler將特定的信息使用sendMessage函數發出

3. 在新建線程的handler的回調函數handleMessage中處理這些信息

 

具體的代碼實現:

1. 創建線程,獲得looper,以及handler與looper綁定

Looper looper; // Create our own looper here 
mThread = new HandlerThread("mThread",Process.THREAD_PRIORITY_FOREGROUND); //建立handler的線程 
mThread.start(); //運行線程 
looper = mThread.getLooper(); //將當前的線程與looper綁定 
mHandler = new EventHandler(looper); //創建一個handler,並將其與looper綁定

    詳細的代碼我們後面講。

2. 創建handler類

public class EventHandler extends Handler {
    public EventHandler(Looper looper) {
        super(looper);
    }
    @Override
    public void handleMessage(Message msg) {
        //處理信息
    }
}

3. 使用handler,從主線程獲得信息併發送信息到處理特定信息的 線程

if (mHandler != null) {
    Message mmsg = Message.obtain(msg);  //拷貝Message
    mHandler.sendMessage(mmsg);
}

    通過上面的操作就可以簡單的實現線程間傳遞信息了。

 

四. 參考文章:

Android 源碼分析 —— Handler、Looper 和 MessageQueue : 講解的非常好,以問答的方式將原理和代碼結合,同時有Android中對於handler,looper以及MessageQueue的介紹

Android中Thread、Handler、Looper、MessageQueue的原理分析 :講解很詳細,上面handler,looper與MessageQueue的關係圖就是來自這篇文章

 

五. 代碼分析:

    代碼分析(在這裏方法和函數是一個意思,成員和變量是一個意思):

1. HandlerThread

public class HandlerThread extends Thread {
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    } 
    
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }
    
}

    我們代碼中用到的方法:

mThread = new HandlerThread("mThread",Process.THREAD_PRIORITY_FOREGROUND);   //建立handler的線程
mThread.start();      //運行線程
looper = mThread.getLooper();   //將當前的線程與looper綁定

     HandlerThread繼承自thread,所以他也是一個線程類,而new HandlerThread()其實就是創建了一個線程,而線程有不同的狀態,如下圖:

    而當執行了線程的start方法之後,如果獲得了CPU資源就會執行線程的run方法了,看run中的代碼爲:

    public void run() {
        mTid = Process.myTid();  //獲得該進程中當前線程的ID
        Looper.prepare();   //初始化當前線程作爲一個looper
        synchronized (this) {
            mLooper = Looper.myLooper();  // 返回當前線程的looper對象
            notifyAll();
        }
        Process.setThreadPriority(mPriority);  //設置線程的調度屬性
        onLooperPrepared();  //這是個回調函數,如果你有特殊的事情需要在loop方法調用前操作,可以在這裏設置
        Looper.loop();  //調用looper的loop函數來發送分發信息
        mTid = -1;
    }

    其實在上面的代碼中主要實現的是將thread與looper的綁定。

    而代碼:

looper = mThread.getLooper();   //將當前的線程與looper綁定
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

    從上面的代碼可以知道,其實就是返回當前線程的looper。這裏首先判斷當前的線程是否還活着,如果還在運行,同時還沒有創建好looper的時候我們就需要他一直等,直到創建好,之後我們將創建好的looper返回。其中mLooper就是threadlocal的一個成員變量,而當looper初始化好後就會賦值給這個變量。

 

2.  Looper :

    我想單單通過我上面說的threadlocal中run做的事是實現thread與looper的綁定。大家可能不太相信,下面我們分析一下他主要的代碼就知道了,主要的代碼如下:

Looper.prepare();   //初始化當前線程作爲一個looper
mLooper = Looper.myLooper();  // 返回當前線程的looper對象
Looper.loop();  //調用looper的loop函數來發送分發信息

    而looper相關的代碼爲:

public final class Looper {
    public static void prepare() {
        prepare(true);
    }
    
     private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }   
    
     public static Looper myLooper() {
        return sThreadLocal.get();
    }   
    
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }  
    
    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();
        }
    }    
        
}

    下面開始講解我的代碼,首先講解Looper.prepare(),而Looper.prepare(); 其實就是調用prepare(true); 這裏主要的工作爲:

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

    而通過上面的代碼我們可以知道thread和looper只能是一一對應的關係,因爲當你第二次通過prepare來綁定thread與looper時,sThreadLocal.get()可以得到當前進程的looper,從而會報錯。而當第一次調用prepare的時候就可以調用sThreadLocal.set方法將當前線程與looper綁定。而sThreadLocal.set的實現爲,獲得當前的線程,同時獲得線程的map,之後將獲得的當前線程與New出來的 looper 放入到map的鍵值對中,這樣就實現了對線程與looper的綁定。下面是sThreadLocal.set方法:

public void set(T value) { 
    Thread t = Thread.currentThread(); 
    ThreadLocalMap map = getMap(t); 
    if (map != null) 
        map.set(this, value); 
    else 
        createMap(t, value); 
}

     下面我們分析一下new Looper(quitAllowed)主要做了什麼:

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

    從上面代碼看主要做了兩件事情,第一是創建一個MessageQueue對象,第二是獲得當前的線程。從這裏我們知道thread和looper是互相持有的,我們既可以通過looper獲得當前的線程,同時也可以通過當前的線程獲得looper。

    由於我們只能調用一次looper的prepare,所以我們也就只能new 一次MessageQueue了,所以這裏的looper與MessageQueue也是一一對應的。

mLooper = Looper.myLooper();  // 返回當前線程的looper對象

    我們通過上面的代碼知道通過sThreadLocal.set將looper與當前的線程放入了一個線程的map中,而想要獲得當前進程的looper就可以通過sThreadLocal.get來獲得,具體代碼爲:

    public static Looper myLooper() {
        return sThreadLocal.get();
    }

     我們知道了looper與線程綁定,那麼looper的作用是什麼那?他綁定線程的作用是什麼那?

     其實looper的作用就是將handler通過sandMessage中發送的Message獲得,之後發送給handler對應的handleMessage函數。而他之所以需要綁定一個線程是因爲loop函數需要在一個循環中不斷的運行,而如果沒有一個線程的話,他會一直佔用主線程,這是不可以的。而要更加詳細的瞭解looper就要看loop函數了:

Looper.loop();  //調用looper的loop函數來發送分發信息
  public static void loop() {
        final Looper me = myLooper();  //獲得looper
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;  //獲得MessageQueue
        ..................................
        for (;;) {
            Message msg = queue.next(); // might block   //獲得MessageQueue中的message
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
        ..................................
            msg.target.dispatchMessage(msg);       //將message發出
        ..................................
            msg.recycle();            //清除這個message
        }
    }

    通過上面的代碼我們可以看出loop主要做的就是通過Message msg = queue.next();獲得信息,同時通過msg.target.dispatchMessage(msg); 將信息發送出去。而信息是如何發送出去的那? 這就要看dispatchMessage函數了。

 

3. handler:

    首先我們回答一下上面的問題,如何發送信息:

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

    這裏處理message有三種情況:

第一種是有msg.callback就調用他的message的callback函數

    private static void handleCallback(Message message) {
        message.callback.run();
    }

第二種是有handler自己的callback函數,就調用handler的callback的handleMessage函數:mCallback.handleMessage(msg)

final Callback mCallback;

第三就是調用handler自己的handleMessage函數。

handleMessage(msg);

    這裏我們可能就要問了,爲什麼會有第一種情況? 這就要說明一下postXXX等函數與sendXXX等函數的區別了,我們知道handler中有sendMessage等的sendXXXX以及post等的postXXX,他們有什麼區別那?

1. sendXXX用來傳遞的是Message對象,而postXX用來傳遞的是Runnable對象。

2. postXXX之後也會調用sendXXX,而不同的就是爲其傳入了message.callback

     代碼爲:

    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

    從上面的代碼中我們知道postXX與sendXXX最後是一樣傳遞的message,而不同的是postXXX的message會走他自己的回調函數,也就是Runnable。

    而第二種處理message的情況是我們在定義一個handler的時候自己實現的,如果我們希望自己的message處理函數由自己編寫可以使用這個方式。

    這第三種方式就是handler類默認的了。

    下面我們就要講解重點了,也就是handler:

public class Handler {
    public interface Callback {
        public boolean handleMessage(Message msg);
    }
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }    
 
    public Handler(Looper looper) {
        this(looper, null, false);
    }  
         
    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }   
     
    public final Message obtainMessage()
    {
        return Message.obtain(this);
    } 
    
    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }    
    
    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }  
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }    
            
}

    handler最主要的工作就是獲得message,以及發送message,他們對應的函數分別爲:obtainMessage和sendMessage

    先說obtainMessage,其實handler的obtainMessage函數最主要的還是調用Message類的obtain函數。

    而sendMessage函數就是將Message加入到MessageQueue中,

    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
    
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;  //獲得MessageQueue
        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);
    }
    
     private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;  //這個handler
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis); //將message放入到MessageQueue中
    }   

  上面我們說過一個looper可以對應多個handler,那麼looper是如何判斷MessageQueue中的message發送給那個handler的信息處理函數那? 這裏就要看上面的enqueueMessage函數中的 msg.target = this; (這個this就是對應的handler)也就是說在將message放入到MessageQueue的時候就註定了這個message會發送給那個handler。而在looper的loop函數中就是調用msg.target.dispatchMessage(msg);  來分發message,也就是handler調用自己的dispatchMessage函數來分發message。

  public static void loop() {
        final Looper me = myLooper();  //獲得looper
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;  //獲得MessageQueue
        ..................................
        for (;;) {
            Message msg = queue.next(); // might block   //獲得MessageQueue中的message
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
        ..................................
            msg.target.dispatchMessage(msg);       //將message發出
        ..................................
            msg.recycle();            //清除這個message
        }
    }

    好了,講到這裏就對上面的代碼進行了一些講解,希望這部分可以加深你對handler,looper以及message的瞭解。

 

六. 參考文章 : 

啃碎併發(二):Java線程的生命週期

Java多線程學習(三)---線程的生命週期

Java 之 ThreadLocal 詳解

 

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