Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android源碼分析——Looper,Messagequeue,Message,handler初始化及handler機制簡介

Android源碼分析——Looper,Messagequeue,Message,handler初始化及handler機制簡介

編輯:關於Android編程

Handler機制算是我入門源碼的第一節。看得比較仔細。體會較多。mark一下。

順序:先科普一下Handler基本功,然後再細講下源碼

一、Handler目的:

目的:Handler機制來處理了子線程去更新UI線程控件問題。

二、handler,messagequeue,looper,message關系圖:

其實各種書籍上都有這麼一張圖。但是主要是學習源碼,所以還是自己手畫一張“流程圖”。

 

三、handler知識點總結:

(若以下總結都能理解,那麼可以不再看本文後續源碼分析;)

1)handler、Looper、Messagequeue初始化總結:在主UI實例化Looper或者子線程手工實例化looper。在Looper中實例化Messagequeue。在handler初始化在主線程或者子線程中,且handler初始化只獲取looper對象,獲取Messagequeue對象,因此必須先有Looper實例再有handler實例,否則handler無法獲取looper、Messagequeue。

2)hanlder中寫了操作主UI線程的handlemessage空方法、使用handler實例在子線程中發送消息Message

3)Looper初始化:一個線程只有一個Looper,Looper管理Messagequeue

4)主UI線程(ActivityThread)創建Looper對象,而Looper創建了Messagequeue對象(可通過其他方式創建looper)

5)在handler裡使用queue(Messagequeue對象),equeueMessage插入消息到Messagequeue,在Looper的loop()方法裡調用handler對象的dispatchMessage()分發消息。並通過handler的handlerMessage()方法使主UI改變。

6)Messagequeue對象和Message對象

四、逐步分析handler源碼

1)handler、Looper、Messagequeue初始化:

由於初始化順序必然是Looper ==> Messagequeue ==> handler。所以我也按照此順序分析源碼。

四、1)Looper獲取:

當我啟動一個程序。按照如下順序:

ActivityManager的startActivity()==>ActivityThread的main()==》Looper.prepareMainLooper()實例化主UI線程Looper

以上實例源碼,部分如下:

ActivityManager的startActivity()

 

/**
         * Start an activity in this task.  Brings the task to the foreground.  If this task
         * is not currently active (that is, its id < 0), then a new activity for the given
         * Intent will be launched as the root of the task and the task brought to the
         * foreground.  Otherwise, if this task is currently active and the Intent does not specify
         * an activity to launch in a new task, then a new activity for the given Intent will
         * be launched on top of the task and the task brought to the foreground.  If this
         * task is currently active and the Intent specifies {@link Intent#FLAG_ACTIVITY_NEW_TASK}
         * or would otherwise be launched in to a new task, then the activity not launched but
         * this task be brought to the foreground and a new intent delivered to the top
         * activity if appropriate.
         *
         * 

In other words, you generally want to use an Intent here that does not specify * {@link Intent#FLAG_ACTIVITY_NEW_TASK} or {@link Intent#FLAG_ACTIVITY_NEW_DOCUMENT}, * and let the system do the right thing.

* * @param intent The Intent describing the new activity to be launched on the task. * @param options Optional launch options. * * @see Activity#startActivity(android.content.Intent, android.os.Bundle) */ public void startActivity(Context context, Intent intent, Bundle options) { ActivityThread thread = ActivityThread.currentActivityThread(); thread.getInstrumentation().execStartActivityFromAppTask(context, thread.getApplicationThread(), mAppTaskImpl, intent, options); }此處ActivityThread.currentActivityThread();

 

 

public static ActivityThread currentActivityThread() {
        return sCurrentActivityThread;
    }
一個單例模式的ActivityThread,簡單看下定義。
/** Reference to singleton {@link ActivityThread} */
    private static ActivityThread sCurrentActivityThread;

ActivityThread的main()

 

 

 

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

        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");
    }
上面是整個main方法

 

細心可以發現這麼一條主UI線程Looper初始化實例的語句:(這個代碼待會兒講Looper時一起講,此處知道在這初始化即可)

Looper.prepareMainLooper()

 

Looper.prepareMainLooper();
當然有實例,還得開啟主UI線程Looper的輪詢方法。看這句就知道。(這個也一會兒講Looper講)

 

 

Looper.loop();
講到這兒我們主線程的Looper已經初始化完事了。接下來講講重中之重Looper。

 

 

四、2)Messagequeue實例化即Looper源碼分析

先不要著急Messagequeue實例化,由於這個Looper是重點,我決定按照傳統的辦法來看源碼。按照如下順序讀源碼: 構造方法Looper()==》looper().loop()輪詢方法

構造方法Looper()

 

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
直接上源碼可見是個private的構造方法。可見單例模式痕跡明顯。肯定有對外的實例方法。接著看:
private static Looper sMainLooper;  // guarded by Looper.class
實例在類初始化的時候初始化了。只需要獲取即可。看看有哪些能獲取實例的方法:

 

 

/**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. The main looper for your application
     * is created by the Android environment, so you should never need
     * to call this function yourself.  See also: {@link #prepare()}
     *///主線程用來實例化Looper的,你應該永遠也用不到它。
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
prepareMainLooper很熟悉吧。對,就是它。主方法裡面用的就是它。當然注釋裡面說了,主線程用來實例化Looper的,你應該永遠也用不到它。然後提示你看prepare()

 

這裡第一句代碼看看。

 

prepare(false)
這菊花雖然短,但是千萬不要忽略了。它就是那個萬惡的深藏著的Looper實例所在。

 

 

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));
    }
如果ThreadLocal.get()不為空,可能你會有疑問sThreadLocal究竟是什麼。後面我會認真考慮寫關於ThreadLocal的技術博客的。現在只需要立即為一個存儲數據的對象,對外公開了set,get方法即可。

 

 

sThreadLocal.set(new Looper(quitAllowed));
這裡的quitallowed參數明顯一直沒變,是false會到最開始的私有的初始化函數private looper(boolean quitallowed)。發現其實到這兒我們的Looper實例化已經完事了。quitallowed是給Messagequeue對象實例化傳遞的一個參數罷了。既然這樣子,那就先放放,交給後面的Messagequeue去講它的實例化方法再提。

 

繞了一圈,再來談談輪詢loop()方法。

looper().loop()輪詢方法

先把loop()源碼奉上。在哪用到就再提一句吧,每次Looper實例化完,必須緊跟著它。因為首先要有這個輪詢器(實例化),然後再將輪訓器運轉起來loop()。

 

/**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the 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();
        }
    }
現在可以一行行看代碼了,先判斷看看有沒有輪訓器?有則繼續,沒有就拋異常。所以,當你看到下面這個異常的時候,你就知道,你肯定沒寫輪訓器。

 

 

final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
不要問我me是什麼,me就是Looper實例。 見myLooper()源碼:一目了然。
public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

獲取Looper實例全部靠它,然後再看看這一行:簡單明了,之前Looper已經實例化了Messagequeue,那麼現在拿來用。

 

final MessageQueue queue = me.mQueue;

 

看看下面這幾行。翻譯很明確,確保當前binder調用是否為遠程調用。調用這個clear方法,在當前進程中。將會清除之前進程的PID和UID,

重置成當前進程UID,PID。好吧,以後寫個IPC文章。

 

// 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 (;;) {

}
裡面的內容雖然涉及到Messagequeue這個對象和Message對象。但是其實從源碼很好看出來。首先我們簡化掉這些打日志的代碼,

 

再看如下:

 

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

            msg.target.dispatchMessage(msg);
           
            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();          

            msg.recycleUnchecked();
        }

天吶,我對它究竟做了什麼!居然只剩下四條了。所以不要害怕,一切源碼都是紙老虎。而且其中還有一條是剛剛講過的Binder重設。

 

這麼一看。居然只剩下三條。但是,我很無恥,我只打算在這個篇章裡面講上面兩條。其它都留給messagequeue和message。

 

 Message msg = queue.next(); // might block
一條是從“隊列”中取出一條消息。

 

 

msg.target.dispatchMessage(msg);
另一條是,是講這個消息分發出去。其中msg.target就是指向我們主線程中的handler對象。問我為什麼知道的。一會兒我打算講Message就知道了。

 

到這,很明顯的每次從“隊列”中取出一條,然後分發出去。我們輪詢就干了這麼間完全無技術含量的事。

Messagequeue和Message對象:

先講Messagequeue吧:我估計我寫的挺累,大家看的也挺累。再忍一忍。成功一大半了!最難的都看完了。就剩下一個操作類handler,

兩個對象Messagequeue、Message對象。

Messagequeue對象:

先把遺留問題講講:

mQuitAllowed:這個參數傳遞過來做什麼?看源碼吧。

PS:以下都是Messagequeue的部分源碼:

 

// True if the message queue can be quit.
    private final boolean mQuitAllowed;
源碼注釋寫的太明白了:就是判斷Messagequeue能不能退出的。再深入一點。

 

 

void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        synchronized (this) {
            if (mQuitting) {
                return;
            }
            mQuitting = true;

            if (safe) {
                removeAllFutureMessagesLocked();
            } else {
                removeAllMessagesLocked();
            }

            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
    }
Messagequeue自帶退出方法。為什麼不能退出?因為我們剛剛傳遞參數一直都是從主UI線程的Looper給的。主線程不允許Messagequeue退出,就是這麼任性!既然都看到了,就簡單講講吧。mQuitting這個標志:
If the loop is quitting then it must not be idling.
簡單來說就是一個loop空轉的標志。這個參數從哪來的?從Looper類的方法過來的: PS:如果Looper是自己實例化的,必須跟這Looper.loop(),之前我們已經說過,而且使用完Looper對象後通過Looper.quit()方法退出。 這個可不是我總結的:SDK說的,原文在Looper源碼的prepare方法中:
/** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    public static void prepare() {
        prepare(true);
    }
其實也就是說我們自己新建的Looper對象都是可以退出,且需要退出的。而主線程是不允許的。
public void quit() {
        mQueue.quit(false);
    }
還有一個quitSafely()
public void quitSafely() {
        mQueue.quit(true);
    }
涉及到一個是否安全退出的問題。Looper安全退出究竟是什麼?
回到Messagequeue源碼中來:繼續看removeAllFutureMessagesLocked及removeAllMessagesLocked方法。
private void removeAllFutureMessagesLocked() {
        final long now = SystemClock.uptimeMillis();
        Message p = mMessages;
        if (p != null) {
            if (p.when > now) {
                removeAllMessagesLocked();
            } else {
                Message n;
                for (;;) {
                    n = p.next;
                    if (n == null) {
                        return;
                    }
                    if (n.when > now) {
                        break;
                    }
                    p = n;
                }
                p.next = null;
                do {
                    p = n;
                    n = p.next;
                    p.recycleUnchecked();
                } while (n != null);
            }
        }
    }
大概看一眼則知道:
那麼我就安全退出 方法,具體分析如:
如果p.when>now,即隊首的消息執行時間大於當前時間,則表明隊首的消息還沒分發。等同於刪除所有的消息隊列的消息:那麼就調用removeAllMessageLocked。刪除所有。
否則刪除消息執行時間大於當前時間的消息。
private void removeAllMessagesLocked() {
        Message p = mMessages;
        while (p != null) {
            Message n = p.next;
            p.recycleUnchecked();
            p = n;
        }
        mMessages = null;
    }
雖然比較麻煩,可還是簡單要講下MessageQueue的next()方法:主要是一些算法用來取得下一條要執行的Message對象。不打算在此處花費大量篇幅寫。下一篇,會優先考慮寫下這個算法。有興趣的可以看看。 之前留下的還剩下一個dispatchMessage方法。這個方法在下面的Message中將提到。

Message對象:

從類的定義來看知道是一個序列化的對象,很明顯是用來傳輸數據的。
public final class Message implements Parcelable 
關鍵字what用得最多的:描述很清楚的寫著每個handler都有自己的獨立命名空間。不用擔心命名沖突問題。
/**
     * User-defined message code so that the recipient can identify 
     * what this message is about. Each {@link Handler} has its own name-space
     * for message codes, so you do not need to worry about yours conflicting
     * with other handlers.
     */
    public int what;
看看構造方法:官方不推薦直接調用,推薦使用obtain獲取handler 對象。性能問題。具體原因:也就是一個新建對象的開銷問題。參考這篇文章:http://blog.csdn.net/h3c4lenovo/article/details/7914902
/** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
    */
    public Message() {
    }
其實講到這,Message日常用到可能也就是what,這兒補充一點內容。關於message對象的target。
/*package*/ Handler target;
這個target是個handler對象,主要目的是用來指向當前使用的handler。從obtain方法可見:
/**
     * Same as {@link #obtain()}, but copies the values of an existing
     * message (including its target) into the new one.
     * @param orig Original message to copy.
     * @return A Message object from the global pool.
     */
    public static Message obtain(Message orig) {
        Message m = obtain();
        m.what = orig.what;
        m.arg1 = orig.arg1;
        m.arg2 = orig.arg2;
        m.obj = orig.obj;
        m.replyTo = orig.replyTo;
        m.sendingUid = orig.sendingUid;
        if (orig.data != null) {
            m.data = new Bundle(orig.data);
        }
        m.target = orig.target;
        m.callback = orig.callback;

        return m;
    }
可能看到這兒大家都看到了m.target = orig.trget。   突然間可能有種懵的感覺?忘了orig這個參數?沒關系,溫習一下。handler實例的時候我們如何用target就知道了。 ----------------此處為分割線------------------------------ 講了這麼多總算到了handler機制,前面都是鋪墊,是不是簡直令人發指。 ---此處是基礎:------- Handler有個Callback接口,i m p liments接口,實現方法handleMessage,用來處理子線程發的消息的響應。
/**
     * 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);
    }
    
    /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }
new一個handler實例。直接上源碼:
public Handler() {
        this(null, false);
    }
指向多參數的handler構造方法
public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class 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;
    }
通過Looper.myLooper()獲取實例。myLooper()方法我前面講沒講?自己翻一翻。 通過前面去獲得Looper實例獲取queue實例。 拿到所有可以用的東西了。至於Message,那是通過handler的方法obtainMessage方法獲得的。 准備工作做好。發送消息====》handler.sendMessage()
public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
可見雖然我沒有傳入參數最終都是要進入===》sendMessageDelayed(Message msg, long delayMillis)
 public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
進入===》sendMessageAtTime(Message msg, long uptimeMillis)下面方法為發送消息的唯一方法。所有方法均調用它。
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自己的插入消息方法。其實這是一個偽方法。真正調用的Messagequeue的該同名方法。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
說到這,消息也就發送出去了。 當然光發出去還不行,還得由主UI線程或者當前handler接收回來。 當輪詢器Looper實例的Looper.loop方法調用msg.target.dispatchMessage(之前我已經講過,msg.target指向就是當前handler)來,看下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);
        }
    }
好了,總算到了handleMessage(msg)方法了。此方法,我們實現接口的時候已然重寫。那麼只需調用該方法即可。 主UI響應,over。   PS:寫到這,感謝大家看完。如有錯誤,歡迎指正。
 
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved