日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
AndroidHandler消息機(jī)制原理解析

歡迎訪問我的個人博客 傳送門

成都網(wǎng)站建設(shè)哪家好,找成都創(chuàng)新互聯(lián)公司!專注于網(wǎng)頁設(shè)計、成都網(wǎng)站建設(shè)、微信開發(fā)、微信小程序開發(fā)、集團(tuán)成都企業(yè)網(wǎng)站定制等服務(wù)項目。核心團(tuán)隊均擁有互聯(lián)網(wǎng)行業(yè)多年經(jīng)驗,服務(wù)眾多知名企業(yè)客戶;涵蓋的客戶類型包括:成都橡塑保溫等眾多領(lǐng)域,積累了大量豐富的經(jīng)驗,同時也獲得了客戶的一致稱譽(yù)!

前言

做過 Android 開發(fā)的童鞋都知道,不能在非主線程修改 UI 控件,因為 Android 規(guī)定只能在主線程中訪問 UI ,如果在子線程中訪問 UI ,那么程序就會拋出異常

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy .

并且,Android 也不建議在 UI 線程既主線程中做一些耗時操作,否則會導(dǎo)致程序 ANR 。如果我們需要做一些耗時的操作并且操作結(jié)束后要修改 UI ,那么就需要用到 Android 提供的 Handler 切換到主線程來訪問 UI 。因此,系統(tǒng)之所以提供 Handler,主要原因就是為了解決在子線程中無法訪問 UI 的問題。

概述

要理解 Handler 消息機(jī)制原理 還需要了解幾個概念:

  1. UI 線程

    主線程 ActivityThread

  2. Message

    Handler 發(fā)送和處理的消息,由 MessageQueue 管理。

  3. MessageQueue

    消息隊列,用來存放通過 Handler 發(fā)送的消息,按照先進(jìn)先出執(zhí)行,內(nèi)部使用的是單鏈表的結(jié)構(gòu)。

  4. Handler

    負(fù)責(zé)發(fā)送消息和處理消息。

  5. Looper

    負(fù)責(zé)消息循環(huán),循環(huán)取出 MessageQueue 里面的 Message,并交給相應(yīng)的 Handler 進(jìn)行處理。

在應(yīng)用啟動時,會開啟一個 UI 線程,并且啟動消息循環(huán),應(yīng)用不停地從該消息列表中取出、處理消息達(dá)到程序運(yùn)行的效果。
Looper 負(fù)責(zé)的就是創(chuàng)建一個 MessageQueue,然后進(jìn)入一個無限循環(huán)體不斷從該 MessageQueue 中讀取消息,而消息的創(chuàng)建者就是一個或多個 Handler 。
流程圖如下:

Android Handler 消息機(jī)制原理解析

下面結(jié)合源碼來具體分析

Looper

Looper 比較重要的兩個方法是 prepare( ) 和 loop( )

先看下構(gòu)造方法

final MessageQueue mQueue;

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

Looper 在創(chuàng)建時會新建一個 MessageQueue

通過 prepare 方法可以為 Handler 創(chuàng)建一個 Lopper,源碼如下:

 static final ThreadLocal sThreadLocal = new ThreadLocal();
 private static Looper sMainLooper;  // guarded by Looper.class

 public static void prepare() {
        prepare(true);
 }

 private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            //一個線程只能有一個looper
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
 }

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

可以看到這里創(chuàng)建的 Looper 對象使用 ThreadLocal 保存,這里簡單介紹下 ThreadLocal。

ThreadLocal 是一個線程內(nèi)部的數(shù)據(jù)存儲類,通過它可以在指定的線程中存儲數(shù)據(jù),數(shù)據(jù)存儲以后,只有在指定線程中可以獲取到存儲的數(shù)據(jù),對于其他線程來說則無法獲取到數(shù)據(jù),這樣就保證了一個線程對應(yīng)了一個 Looper,從源碼中也可以看出一個線程也只能有一個 Looper,否則就會拋出異常。

prepareMainLooper() 方法是 系統(tǒng)在 ActivityThread 中調(diào)用的。

ActivityThread.java

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

       //...省略代碼

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

由此可以看出,系統(tǒng)在創(chuàng)建時,會自動創(chuàng)建 Looper 來處理消息,所以我們一般在主線程中使用 Handler 時,是不需要手動調(diào)用 Looper.prepare() 的。這樣 Handler 就默認(rèn)和主線程的 Looper 綁定。
當(dāng) Handler 綁定的 Looper 是主線程的 Looper 時,則該 Handler 可以在其 handleMessage 中更新UI,否則更新 UI 則會拋出異常。
在開發(fā)中,我們可能在多個地方使用 Handler,所以又可以得出一個結(jié)論:一個 Looper 可以和多個 Handler 綁定,那么 Looper 是怎么區(qū)分 Message 由哪個 Handler 處理呢?
繼續(xù)看源碼 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
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            final long end;
            try {
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (slowDispatchThresholdMs > 0) {
                final long time = end - start;
                if (time > slowDispatchThresholdMs) {
                    Slog.w(TAG, "Dispatch took " + time + "ms on "
                            + Thread.currentThread().getName() + ", h=" +
                            msg.target + " cb=" + msg.callback + " msg=" + msg.what);
                }
            }

            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();// 回收消息 
        }
    }

代碼比較多,我們撿重點看。
2~6 行 獲取當(dāng)前 Looper 如果沒有則拋異常,有則獲取消息隊列 MessageQueue
所以如果我們在子線程中使用 Handler 則必須手動調(diào)用 Looper.prepare() 和 Looper.loop()
系統(tǒng)在代碼中提供了示例代碼

class LooperThread extends Thread {
            public Handler mHandler;

            public void run() {
                Looper.prepare();

                mHandler = new Handler() {
                    public void handleMessage(Message msg) {
                        // process incoming messages here
                    }
                };

                Looper.loop();
            }
        }

獲取到 MessageQueue 后開啟消息循環(huán),不斷從 MessageQueue 中取消息,無則阻塞,等待消息。有則調(diào)用 msg.target.dispatchMessage(msg) 處理消息。

msg.target 就是 Message 所屬的 Handler,這個會再后面具體介紹 Handler 中會說明

所以上面的問題就可以回答了,Looper 不需要考慮怎么區(qū)分 Message 由哪個 Handler 處理,只負(fù)責(zé)開啟消息循環(huán)接收消息并處理消息即可。處理完消息后會調(diào)用 msg.recycleUnchecked() 來回收消息。

那么開啟消息循環(huán)后,可以停止嗎?
答案是肯定的,Looper 提供了 quit() 和 quitSafely() 來退出。

  public void quit() {
     mQueue.quit(false);
  }

  public void quitSafely() {
     mQueue.quit(true);
  }

可以看到實際上調(diào)用的是 MessageQueue 中的退出方法,具體會在 MessageQueue 中介紹。
調(diào)用 quit() 會直接退出 Looper,而 quitSafely() 只是設(shè)定一個退出標(biāo)記,然后把消息隊列中的已有消息處理完畢后才安全地退出。在 Loooper 退出后,通過 Handler 發(fā)送消息會失敗。如果在子線程中手動創(chuàng)建了 Looper ,則應(yīng)在處理完操作后退出 Looper 終止消息循環(huán)。

到此 Looper 的源碼分析就完了,我們來總結(jié)下 Looper 所做的工作:

  1. 被創(chuàng)建時與線程綁定,保證一個線程只會有一個 Looper 實例 ,并且一個 Looper 實例只有一個 MessageQueue
  2. 創(chuàng)建后,調(diào)用 loop( ) 開啟消息循環(huán),不斷從 MessageQueue 中取 Message ,然后交給 Message 所屬的 Handler 去處理,也就是 msg.target 屬性。
  3. 處理完消息后,調(diào)用 msg.recycleUnchecked 來回收消息

Message 和 MessageQueue

Message 是線程通信中傳遞的消息,它有幾個關(guān)鍵點

  1. 使用 what 來區(qū)分消息
  2. 使用 arg1、arg2、obj、data 來傳遞數(shù)據(jù)
  3. 參數(shù) target,它決定了 Message 所關(guān)聯(lián)的 Handler,這個在后面看 Handler 源碼時會一目了然。

MessageQueue

MessageQueue 負(fù)責(zé)管理消息隊列,通過一個單鏈表的數(shù)據(jù)結(jié)構(gòu)來維護(hù)。
源碼中有三個主要方法:

  1. enqueueMessage 方法往消息列表中插入一條數(shù)據(jù),
  2. next 方法從消息隊列中取出一條消息并將其從消息隊列中移除
  3. quit 方法退出消息列表,通過參數(shù) safe 決定是否直接退出

next 方法

 Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }

               //...省略代碼
        }
    }

可以發(fā)現(xiàn) next 方法是一個無限循環(huán)的方法,如果消息隊列中沒有消息,那么 next 方法會一直阻塞在這里。當(dāng)有新消息到來時,next 方法會從中獲取消息出來返回給 Looper 去處理,并將其從消息列表中移除。

quit方法

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

private void removeAllMessagesLocked() {
    Message p = mMessages;
    while (p != null) {
        Message n = p.next;
        p.recycleUnchecked();
        p = n;
    }
    mMessages = null;
}

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

從 上述代碼中可以看出,當(dāng) safe 為 true 時,只移除尚未觸發(fā)的所有消息,對于正在處理的消息不做處理,當(dāng) safe 為 false 時,移除所有消息。

Handler

Handler 是我們使用最多的類,主要用來發(fā)送消息和處理消息。

先來看構(gòu)造方法

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());
            }
        }
        //獲取當(dāng)前線程的 Looper 實例,如果不存在則拋出異常
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        //關(guān)聯(lián) Looper 中的 MessageQueue 消息隊列
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
}

public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
}

構(gòu)造方法主要有三個參數(shù)

  1. looper 不傳值則調(diào)用 Looper.myLooper( ) 獲取當(dāng)前線程的 Looper 實例;傳值則使用,一般是在子線程中使用 Handler 時才傳參數(shù)。
  2. callback Handler 處理消息時的回調(diào)
  3. async 是否是異步消息。這里和 Android 中的 Barrier 概念有關(guān),當(dāng) View 在繪制和布局時會向 Looper 中添加了 Barrier(監(jiān)控器),這樣后續(xù)的消息隊列中的同步的消息將不會被執(zhí)行,以免會影響到 UI繪制,但是只有異步消息才能被執(zhí)行。所謂的異步消息也只是體現(xiàn)在這,async 為 true 時,消息還可以繼續(xù)被執(zhí)行,不會被推遲運(yùn)行。

從源碼中可看出,因為 UI 線程在啟動時會自動創(chuàng)建 Looper 實例,所以一般我們在 UI 線程中使用 Handler 時不需要傳遞 Looper 對象。而在子線程中則必須手動調(diào)用 Looper.prepare 和 Looper.loop 方法,并傳遞給 Handler ,否則無法使用,這一點肯定有不少童鞋都遇到過。
在拿到 Looper 對象后,Handler 會獲取 Looper 中的 MessageQueue 消息隊列,這樣就和 MessageQueue 關(guān)聯(lián)上了。

關(guān)聯(lián)上 MessageQueue ,接下來那我們就看下 Handler 是如何發(fā)送消息的。

Handler 發(fā)送消息方法很多,實際上最后都是調(diào)用的 enqueueMessage 方法,看圖說話

Android Handler 消息機(jī)制原理解析

主要看 enqueueMessage 方法

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

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

可以看到在發(fā)送消息時給 Message 設(shè)置了 target = this 也就是當(dāng)前的 Handler 對象,并調(diào)用了 MessageQueue 的 enqueueMessage 方法,這樣就把消息存在消息隊列,然后由 Looper 處理了。

童鞋們應(yīng)該記得之前在講 Looper 時,說到 Looper 開啟消息循環(huán)后,會不斷從 MessageQueue 中取出Message,并調(diào)用 msg.target.dispatchMessage(msg) 來處理消息。

接下來,就來看看 Handler 是如何接收消息的也就是 dispatchMessage 方法

    public interface Callback {
        public boolean handleMessage(Message msg);
    }

    /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(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);
        }
    }

可以看出 Handler 接收消息,只是調(diào)用一個空方法 handleMessage 是不是有些眼熟呢,看下我們寫過很多次的 Handler 代碼

private Handler mHandler = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case value:

                    break;

                default:
                    break;
            }
        }

    };

沒錯這就是我們自己創(chuàng)建 Handler 時重寫的方法,由我們來處理消息,然后根據(jù) msg.what 標(biāo)識進(jìn)行消息處理。

總結(jié)

應(yīng)用啟動時會啟動 UI 線程也就是主線程 ActivityThread,在 ActivityThread 的 main 方法中會調(diào)用 Looper.prepareMainLooper( ) 和 Looper.loop ( ) 啟動 Looper 。
Looper 啟動時會創(chuàng)建一個 MessageQueue 實例,并且只有一個實例,然后不斷從 MessageQueue 中獲取消息,無則阻塞等待消息,有則調(diào)用 msg.target.dispatchMessage(msg) 處理消息。
我們在使用 Handler 時 需要先創(chuàng)建 Handler 實例,Handler 在創(chuàng)建時會獲取當(dāng)前線程關(guān)聯(lián)的 Looper 實例 ,和 Looper 中的消息隊列 MessageQueue。然后在發(fā)送消息時會自動給 Message 設(shè)置 target 為 Handler 本身,并把消息放入 MessageQueue 中,由 Looper 處理。Handler 在創(chuàng)建時會重寫的
handleMessage 方法中處理消息。
如果要在子線程中使用 Handler 就需要新建 Looper 實例,傳給 Handler 即可。

再看下流程圖
Android Handler 消息機(jī)制原理解析

最后

因篇幅較長,童鞋們的 Handler 肯定用的爐火純青了,所以最后就不寫例子了。
本人寫博客不久,寫的不好的地方,望各位海涵。


當(dāng)前題目:AndroidHandler消息機(jī)制原理解析
本文鏈接:http://www.dlmjj.cn/article/gcscoc.html