Handler, Message, Looper, ActivityThread

Handler, Message, Looper, ActivityThread

Handler, Message, Looper, ActivityThread

關鍵詞 nativePollOnce nativeWake ActivityThread並不是Thread ActivityThread使用attach方法將主進程與AMS服務綁定起來 消息隊列並不會阻塞 因爲所有的生命週期和UI刷新全部用Handler消息機制執行

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

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

Handler 主要的幾個方法
/*
*消息進隊
*/
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
MessageQueue
MessageQueue構造函數
 MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }
MessageQueue的主要方法
 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;
                }

                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }
Looper
Looper的生成函數
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // guarded by Looper.class

    final MessageQueue mQueue;
    final Thread mThread;
    /*
    * 生成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));
    }

    /**
     * 生成ActivityThread中的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();
        }
    }
Looper主要方法
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();
        }
    }

遇到主要問題

  • 消息機制是怎麼運轉起來的?

一般認爲消息機制的Handler,Looper,MessageQueue在消息機制中承擔的角色分別是Handler是消息的發射器和分發器,Looper是消息機制的發動機,MessageQueue是消息的存儲隊列。
Looper.loop()會運行一個死循環去MessageQueue取得Message,拿到Message中的Callback然後分發執行。
在loop方法中通過for (;;)死循環調用queue.next()來得到下一個處理的Message示例來執行,當queue.next()返回爲null時,loop方法跳出執行,死循環結束。
而MessageQueue的next方法中同樣有一個for (;;)死循環去調用nativePollOnce(ptr, nextPollTimeoutMillis);得到MessageQueue中的下一個Message實例。nativePollOnce()方法和另外一個方法nativeWake(mPtr);實現了Linux的epoll的I/O阻塞模型。(Linux中的epoll阻塞模型是經典的阻塞模型之一)
所以之前的想法是錯誤的 假設消息隊列中有1000個消息,那麼Looper,loop()方法的死循環執行1000次便跳出的想法是錯誤的。不管在loop方法中還是queue的next方法中的死循環在應用的生命週期中都不會被跳出,假設有1000個消息,消息全部處理完後,循環阻塞在nativePollOnce()方法中等待喚醒,當有Handler發射一個消息並且執行enqueueMessage方法調用queue.enqueueMessage(msg, uptimeMillis);方法在enqueueMessage中的nativeWake(mPtr)方法使得阻塞釋放重新取出消息進行執行。

  • Looper.loop方法會不會阻塞主線程ActivityThread?
簡單講有兩個原因
  1. epoll模型 當沒有消息的時候會epoll.wait,等待句柄寫的時候再喚醒,這個時候其實是阻塞的。
  2. 所有的ui操作都通過handler來發消息操作。比如屏幕刷新16ms一個消息,你的各種點擊事件,所以就會有句柄寫操作,喚醒上文的wait操作,所以不會被卡死了。
詳細講

要完全徹底理解這個問題,需要準備以下4方面的知識:Process/Thread,Android Binder IPC,Handler/Looper/MessageQueue消息機制,Linux pipe/epoll機制。總結一下樓主主要有3個疑惑:

  1. Android中爲什麼主線程不會因爲Looper.loop()裏的死循環卡死?
  2. 沒看見哪裏有相關代碼爲這個死循環準備了一個新線程去運轉?
  3. Activity的生命週期這些方法這些都是在主線程裏執行的吧,那這些生命週期方法是怎麼實現在死循環體外能夠執行起來的?

1. Android中爲什麼主線程不會因爲Looper.loop()裏的死循環卡死?

這裏涉及線程,先說說進程/線程,
進程:
每個app運行時前首先創建一個進程,該進程是由Zygote fork出來的,用於承載App上運行的各種Activity/Service等組件。進程對於上層應用來說是完全透明的,這也是google有意爲之,讓App程序都是運行在Android Runtime。大多數情況一個App就運行在一個進程中,除非在AndroidManifest.xml中配置Android:process屬性,或通過native代碼fork進程。

線程:
線程對應用來說非常常見,比如每次new Thread().start都會創建一個新的線程。該線程與App所在進程之間資源共享,從Linux角度來說進程與線程除了是否共享資源外,並沒有本質的區別,都是一個task_struct結構體,在CPU看來進程或線程無非就是一段可執行的代碼,CPU採用CFS調度算法,保證每個task都儘可能公平的享有CPU時間片。

有了這麼準備,再說說死循環問題:
對於線程既然是一段可執行的代碼,當可執行代碼執行完成後,線程生命週期便該終止了,線程退出。而對於主線程,我們是絕不希望會被運行一段時間,自己就退出,

那麼如何保證能一直存活呢?
簡單做法就是可執行代碼是能一直執行下去的,死循環便能保證不會被退出,例如,binder線程也是採用死循環的方法,通過循環方式不同與Binder驅動進行讀寫操作,當然並非簡單地死循環,無消息時會休眠。

但這裏可能又引發了另一個問題,既然是死循環又如何去處理其他事務呢?
通過創建新線程的方式。真正會卡死主線程的操作是在回調方法onCreate/onStart/onResume等操作時間過長,會導致掉幀,甚至發生ANR,looper.loop本身不會導致應用卡死。

2. 沒看見哪裏有相關代碼爲這個死循環準備了一個新線程去運轉? 事實上,會在進入死循環之前便創建了新
binder線程,在代碼ActivityThread.main()中:

public static void main(String[] args) {
        //創建Looper和MessageQueue對象,用於處理主線程的消息
        Looper.prepareMainLooper();

        //創建ActivityThread對象
        ActivityThread thread = new ActivityThread(); 

        //建立Binder通道 (創建新線程)
        thread.attach(false);

        Looper.loop(); //消息循環運行
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

thread.attach(false);
便會創建一個Binder線程(具體是指ApplicationThread,Binder的服務端,用於接收系統服務AMS發送來的事件),該Binder線程通過Handler將Message發送給主線程,具體過程可查看 startService流程分析,這裏不展開說,簡單說Binder用於進程間通信,採用C/S架構。
另外,
ActivityThread實際上並非線程,不像HandlerThread類,ActivityThread並沒有真正繼承Thread類,只是往往運行在主線程,該人以線程的感覺,其實承載ActivityThread的主線程就是由Zygote fork而創建的進程。

主線程的死循環一直運行是不是特別消耗CPU資源呢?
其實不然,這裏就涉及到Linux pipe/epoll機制,簡單說就是在主線程的MessageQueue沒有消息時,便阻塞在loop的queue.next()中的nativePollOnce()方法裏,此時主線程會釋放CPU資源進入休眠狀態,直到下個消息到達或者有事務發生,通過往pipe管道寫端寫入數據來喚醒主線程工作。這裏採用的epoll機制,是一種IO多路複用機制,可以同時監控多個描述符,當某個描述符就緒(讀或寫就緒),則立刻通知相應程序進行讀或寫操作,本質同步I/O,即讀寫是阻塞的。 所以說,主線程大多數時候都是處於休眠狀態,並不會消耗大量CPU資源。

3. Activity的生命週期是怎麼實現在死循環體外能夠執行起來的?
ActivityThread的內部類H繼承於Handler,通過handler消息機制,簡單說Handler機制用於同一個進程的線程間通信。Activity的生命週期都是依靠主線程的Looper.loop,當收到不同Message時則採用相應措施:在H.handleMessage(msg)方法中,根據接收到不同的msg,執行相應的生命週期。 比如收到msg=H.LAUNCH_ACTIVITY,則調用ActivityThread.handleLaunchActivity()方法,最終會通過反射機制,創建Activity實例,然後再執行Activity.onCreate()等方法; 再比如收到msg=H.PAUSE_ACTIVITY,則調用ActivityThread.handlePauseActivity()方法,最終會執行Activity.onPause()等方法。 上述過程,我只挑核心邏輯講,真正該過程遠比這複雜。

**主線程的消息又是哪來的呢?**當然是App進程中的其他線程通過Handler發送給主線程,請看接下來的內容:--------------------------------------------------------------------------------------------------------------------最後,從進程與線程間通信的角度,通過一張圖加深大家對App運行過程的理解:
[外鏈圖片轉存失敗(img-0NCDxJ8m-1562897297656)(media/15564179928939/15564194168509.jpg)]

system_server進程是系統進程,java framework框架的核心載體,裏面運行了大量的系統服務,比如這裏提供ApplicationThreadProxy(簡稱ATP),ActivityManagerService(簡稱AMS),這個兩個服務都運行在system_server進程的不同線程中,由於ATP和AMS都是基於IBinder接口,都是binder線程,binder線程的創建與銷燬都是由binder驅動來決定的。

App進程則是我們常說的應用程序,主線程主要負責Activity/Service等組件的生命週期以及UI相關操作都運行在這個線程;
另外,
每個App進程中至少會有兩個binder線程 ApplicationThread(簡稱AT)和ActivityManagerProxy(簡稱AMP),除了圖中畫的線程,其中還有很多線程,比如signal catcher線程等,這裏就不一一列舉。Binder用於不同進程之間通信,由一個進程的Binder客戶端向另一個進程的服務端發送事務,比如圖中線程2向線程4發送事務;

而handler用於同一個進程中不同線程的通信,比如圖中線程4向主線程發送消息。結合圖說說Activity生命週期,比如暫停Activity,流程如下:線程1的AMS中調用線程2的ATP;(由於同一個進程的線程間資源共享,可以相互直接調用,但需要注意多線程併發問題)線程2通過binder傳輸到App進程的線程4;線程4通過handler消息機制,將暫停Activity的消息發送給主線程;主線程在looper.loop()中循環遍歷消息,當收到暫停Activity的消息時,便將消息分發給ActivityThread.H.handleMessage()方法,再經過方法的調用,最後便會調用到Activity.onPause(),當onPause()處理完後,繼續循環loop下去。
private class H extends Handler {},其實例final H mH = new H();通過發送Message的方式完成Activity的生命週期的調用。例如

public final void scheduleStopActivity(IBinder token, boolean showWindow,
                int configChanges) {
            int seq = getLifecycleSeq();
            if (DEBUG_ORDER) Slog.d(TAG, "stopActivity " + ActivityThread.this
                    + " operation received seq: " + seq);
            sendMessage(
                showWindow ? H.STOP_ACTIVITY_SHOW : H.STOP_ACTIVITY_HIDE,
                token, 0, configChanges, seq);
        }

會在ActivityThread中的handleMessage方法中

case STOP_ACTIVITY_SHOW: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
                    SomeArgs args = (SomeArgs) msg.obj;
                    handleStopActivity((IBinder) args.arg1, true, args.argi2, args.argi3);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                } break;

Looper 怎麼保證的在一個線程中只有一個Looper 的實例的?
在Looper中有這樣一個變量

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

這裏採用ThreadLocal來保證各個線程的Looper的唯一性
ThreadLocal的主要方法有

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();
    }
    
public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
    
static class ThreadLocalMap {}

而在Thread中存在

/* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

通過這樣的方式保證了Looper的唯一性

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