Handler源碼分析(超詳細的)

這篇博客是一種入門級的但講的很細,本人能力有限,希望看到大神,發現有不對的地方請聯繫我,也希望可以和大家在討論區互動。

本文不管是從哪得到得信息,本人都認真的研究過和測試。包括代碼所以說本人技術可能一般,但是都是總結別人和自己用心研究的成果。不喜勿噴..本文純屬拋磚引玉,先。謝謝大家的觀看!不說廢話了進入正題 


爲什麼使用Handler?

Android的UI要求更新只能在UI線程,因爲安卓是單線程模型。如果任意線程都可以更新UI的話,線程安全問題處理起來會相當麻煩複雜,就會出現頁面錯亂。所以就規定了Android的是單線程模型,只允許在UI線程更新UI操作。

也就是在Android中更新Ui必須在主線程,在子線程更新Ui會報子線程不能修改UI異常。

你想想安卓的頁面一會被這個線程修改,一會被別的線程更改。用戶體驗就會很差,給別人一種不可控制的感覺。

Handler是什麼?

來自百度百科-----

主要用於異步消息的處理:當發出一個消息之後,首先進入一個消息隊列,發送消息的函數即刻返回,而另外一個部分在消息隊列中逐一將消息取出,然後對消息進行處理,也就是發送消息和接收消息不是同步的處理。 這種機制通常用來處理相對耗時比較長的操作。

簡單說Handler就是谷歌的Android的爲了開發人員封裝的一套更新UI的機制,消息處理機制。就是爲了方便開發者人員的。

Handler怎麼使用?

1.創建Handler實例。

2.創建Message對象建議使用Message的靜態方法obtain()構建。

3.給Message對象賦值。

4.Handler.sendMessage()方法把Message發送出去。

5.在Handler裏重寫它的handlerMessage方法,用來處理接收到的Message。

Handler機制:

Handler,looper,Message,MessageQueue。

Mssage:儲存信息。

Handler:把Message添加到MssageQueue裏,處理Looper發送過來的Message。

Looper:取出MessageQueue的Message,將Message發送到Handler。

MessageQueue:儲存Handler發送過來的Message

簡單說:

當一個應用啓動時,會初始化一個UI線程,UI線程中又初始化了Looper,創建Looper的時候又創建了MessageQueue。當Handler把 Messgae發送到MessageQueue裏,然後Looper循環的取出發給Handler,由Handler處理這個信息。

 

Handler源碼分析:

這篇文章是先貼代碼再說意思,害怕你不看源碼,源碼很重要,源碼很重要,源碼很重要!!!

一.首先從new  Handler開始

  private Handler handler=new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };

這樣new會報警告的,查看警告是:

This Handler class should be static or leaks might occur (anonymous android.os.Handler) less... Since this Handler is declared as an inner class, it may prevent the outer class from being garbage collected. If the Handler is using a Looper or MessageQueue for a thread other than the main thread, then there is no issue. If the Handler is using the Looper or MessageQueue of the main thread, you need to fix your Handler declaration, as follows: Declare the Handler as a static class; In the outer class, instantiate a WeakReference to the outer class and pass this object to your Handler when you instantiate the Handler; Make all references to members of the outer class using the WeakReference object.

翻譯成中文是:

這個處理程序類應該是靜態的,否則可能會發生泄漏(匿名的android.os.Handler)。

由於這個處理程序被聲明爲一個內部類,它可以防止外部類被垃圾收集。如果處理程序使用Looper或MessageQueue來處理主線程以外的線程,那麼就沒有問題了。如果處理程序使用主線程的Looper或MessageQueue,則需要修復處理程序聲明,如下所示:將處理程序聲明爲靜態類;在外部類中,實例化對外部類的WeakReference,並在實例化處理程序時將該對象傳遞給處理程序;使用WeakReference對象對外部類的所有成員進行引用。

說的很明顯了,使用靜態內部類來解決。   ---------------------------點擊查看解決方案

我們接着第一步看繼續看,怎麼警告怎麼來的。 new Handler直接調用的構造方法

  public Handler() {
        this(null, false);
    }

而這個構造又直接調用了(點擊this查看),這裏先仔細看看。

     /**
     * @hide
     */
    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 " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

又調用了標有@hide的構造方法,爲了講的清楚,我附一個講@hide的鏈接,不懂的可以看看。


    private static final boolean FIND_POTENTIAL_LEAKS = false;
    private static final String TAG = "Handler";
    private static Handler MAIN_THREAD_HANDLER = null;

先說Handler類上的參數

 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());
            }
        }

第一行的if判斷了是有沒有泄露(FIND_POTENTIAL_LEAKS=找到潛在的泄漏)默認是false的所以if裏面不用看。接下

 mLooper = Looper.myLooper();

Looper調用了自己的靜態方法,看myLooper方法是幹嘛的

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

myLooper它返回與當前線程關聯的Looper對象。如果調用線程與一個循環程序沒有關聯就會返回爲null。

怎麼返回呢,點get()進去看看。

 public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

這個方法是,返回當前線程的此線程局部變量副本中的值。如果變量沒有當前線程的值,則首先將其初始化爲調用initialValue()方法返回的值。

第一行的  Thread t = Thread.currentThread();會調用c++的方法,如果你想簡單瞭解一下。(在往下看就到ThreadLocal類了,我本來打算繼續講的,但是初心是爲了大家都能看懂,就刪了)

@FastNative
public static native Thread currentThread();

返回到Handler的構造方法

 if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }

這個簡單如果Looper不存在的話拋異常。繼續

  mQueue = mLooper.mQueue;
  mCallback = callback;
  mAsynchronous = async;

獲取一些值,比如 mLooper.mQueue就是關聯MessageQueue,實現了MessageQueue自動綁定。到此Handler的構造方法就結束。

自己總結一下Handler的構造幹了什麼。

  1. new Handler的時候判斷了被new的時候有沒有泄漏。
  2. myLooper它返回與當前線程關聯的Looper對象接着判斷looper是否爲null。

休息一下吧!

 

 @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }

看完構造我們看看handleMessage方法。這個方法在Handler裏,你只要實現它就可以拿到Message了。這個你先知道這些就好,下面會講的。

二、獲取Message

接下發送消息開始,發信息就需要Message,怎麼獲得Message呢,那先看看之前我是怎麼寫的

      Message message = new Message();
      message.arg1=1;
      Handler handler = new Handler();
      handler.sendMessage(message);

再看現在怎麼寫的代碼

 Message message = Message.obtain();
 message.arg1=1;
 Handler handler = new Handler();
 handler.sendMessage(message);

爲什麼這樣寫,看源碼。

  /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

判斷sPoolSync有沒有被創建,如果被創建了證明Message已經被實例過了,沒有的話就new 一個Message。

就是從全局池返回一個新的Message實例,節約內存。

三、Handler發送Message

handler.sendMessage(message);

從Handler發送信息開始:

   public final boolean sendMessage(Message msg){
        return sendMessageDelayed(msg, 0);
    }

我們繼續分析的sendMessage這個方法,點進去你會看到調用了sendMessageDelayed()方法

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

sendMessageDelayed判斷了設置的時間是不是爲小於0,小於0就有問題了,所以小於0就直接賦值爲0.

然後一點注意,它調用sendMessageAtTime的時候,傳入的值!!!

msg=看上面可以知道是Message對象

SystemClock.uptimeMillis+delayMillis是幹嘛的呢?

 SystemClock.uptimeMillis是獲取從開機到現在的毫秒數+延遲的時間,也就是說這個方法發送出去的參數。(重要)

有同學說了,我用的不是這個方法,慢慢看,別急.sendMessageDelayed也只是調用了方法sendMessageAtTime。

  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);
    }

sendMessageAtTime把的MessageQueue對象判斷一下爲不爲空,爲空就是一個異常。那麼MessageQueue在哪來的呢。我們找找它在哪?

把mQueue賦值隊列,那麼mQueue哪裏來的呢,知道了嗎?就是之前構造方法裏創建的。

 public Handler(Callback callback, boolean async) {
  
           ...這裏是刪了一些代碼的地方
       

        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;
    }

看到這個方法了嗎mQueue = mLooper.mQueue;也就是說MessageQueue是在Looper類裏拿的。這裏就不說了,我們返回來看。

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);
    }

又回到了sendMssagerAtTime方法,講過的就不說了。它調用了enqueueMessage方法

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

enqueueMessage這方法中msg.target = this;this就是Handler啦.target是標記的意思吧。翻譯目標(目標),也就是說它把Handler關聯起來了。

四、QueueMessage儲存Message

接着就要調用這個enqueueMessage方法啦。而這個方法是queue調的,queue是參數傳過來的就是說是的MessageQueue類的方法。我,們繼續看啊。接下來代碼多,但是一定要看一下,不用記住就好。

 boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

分開講

 if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

首先判斷了這個handler存在不,在繼續,不在拋異常,判斷消息有沒有在使用在使用拋異常,沒有繼續。

 synchronized (this) {
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

這個方法上鎖了,判斷這個有沒有向死線程上的處理程序發送消息,有拋異常,沒有繼續。

 public void recycle() {
        if (isInUse()) {
                if (gCheckRecycle) {
                    throw new IllegalStateException("This message cannot be recycled because it "
                            + "is still in use.");
                }
                return;
        }
        recycleUnchecked();
    }

就是說先檢查在不在使用,在判斷有沒有回收,然後調用recycleUnchecked()方法

 void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

註釋看到了嗎?翻譯一下

當消息保留在回收對象池中時,將其標記爲正在使用。清除所有其他細節。懂了吧,我們返回繼續看。

msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    

這個方法很多內容吧,分開講,建議你搞清楚。


              if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;

判斷消息隊列裏有無消息, 若無,則將當前插入的消息 作爲隊頭 & 若此時消息隊列處於等待狀態,則喚醒線程。


                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;

這個死循環就是根據 消息(Message)創建的時間 插入到隊列中放到的MessageQueue裏。

MessageQueue也就先到這了。

填坑

之前有同學說他一直用的是下面的方法

  handler.post(new Runnable() {
                    @Override
                    public void run() {
                        
                    }
                });

這個方法是吧,我這個小白要放大招了

                Message message = Message.obtain();
                message.arg1 = 1;
                Handler handler = new Handler();
                handler.sendMessage(message);
                handler.sendEmptyMessage(0);
                handler.sendEmptyMessageDelayed(1, 1000);
                handler.sendMessageAtTime(message, 1000);
                handler.sendMessageAtFrontOfQueue(message);
                handler.sendMessageDelayed(message, 1000);
                handler.post(new Runnable() {
                    @Override
                    public void run() {

                    }
                });
                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {

                    }
                }, 1000);
                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {

                    }
                },1000);
                handler.postAtTime(new Runnable() {
                    @Override
                    public void run() {

                    }
                },1000);

哈哈,是不是很簡單了。不管怎麼調它都會走到下面這個方法。

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

不相信你試試。我承認簡單。別笑我這小白。

休息一下,我們要繼續啦。我們回顧一下,從Handler發到MessageQueue裏。

Handler調用sendMessage或者Post等以上發送Message的方法,都會發送到MessageQueue的enqueueMessage裏。

四、Looper類

Looper類怎麼來的呢?

看一下ActivityThread類

public static void main(String[] args) {
     

        //省略代碼1
        Looper.prepareMainLooper();

        //省略代碼2
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

        //省略代碼3
        Looper.loop();

       
    }

繼續看Looper.prepareMainLooper();方法

  public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

裏面有個 prepare(false);看這個:

 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));
    }

它new Looper跟進去

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

創建主線程時,會自動調用ActivityThread的1個靜態的main();而main()內則會調用Looper.prepareMainLooper()爲主線程生成1個Looper對象,同時也會生成其對應的MessageQueue對象。

看ActivityThread的main方法中省略代碼3下面:

 Looper.loop();

Looper調用了靜態loop方法

  final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }

首先判斷了looper是否存在,不存在就會拋異常說沒有looper,讓你調用prepare方法創建Looper

 public static void loop() {
         //省略

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

這個死循環會取出Message有就取。沒有就結束

   msg.target.dispatchMessage(msg);

然後會調用這個方法,這個方法是Handler的,去Handler裏看。msg.target證明了是那個Handler,證明了是那個Handler方法Message就是那個Handler處理Message。

 /**
     * 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);
        }
    }

這個方法,把Message發個Handler了。

handler.Callback的消息處理可以“覆蓋”處理器自己的消息處理所以可以設置一個默認的消息處理注意:。返回值爲true才“覆蓋”默認的消息處理,如果都沒有覆蓋就是調用的handleMessage(MSG) ;方法啦這個方法就是你在處理程序重寫的方法,消息方法一樣。

    private Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            Toast.makeText(HomeActivity.this, "msg", Toast.LENGTH_SHORT).show();
        }
    };

這裏的Message 就是你拿出來的,記住處理器只有一個。並且Handler在那個線程新就在那個線程

到此Handler的簡單分析結束了。本來想寫一篇詳細的源碼分析,由於能力和時間不夠各位我盡力了。

謝了各位。如果喜歡請點個贊就好。如果看不懂,多看兩遍,肯定沒有問題。

大神有問題聯繫QQ在這:1441289824

參考文檔:

http://www.cnblogs.com/dolphin0520/p/3920407.html

https://www.jianshu.com/p/b4d745c7ff7a

附一張我以前喜歡的三葉。。。

 

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