被字節跳動、小米、美團面試官問的AndroidFramework難倒了? 這里有23道面試真題,助力成為offer收割機!


目錄

1.Android中多進程通信的方式有哪些?
a.進程通信你用過哪些?原理是什么?(字節跳動、小米)
2.描述下Binder機制原理?(東方頭條)
3.Binder線程池的工作過程是什么樣?(東方頭條)
4.Handler怎么進行線程通信,原理是什么?(東方頭條)
5.Handler如果沒有消息處理是阻塞的還是非阻塞的?(字節跳動、小米)
6.handler.post(Runnable) runnable是如何執行的?(字節跳動、小米)
7.handler的Callback和handlemessage都存在,但callback返回true handleMessage還會執行么?(字節跳動、小米)
8.Handler的sendMessage和postDelay的區別?(字節跳動)
9.IdleHandler是什么?怎么使用,能解決什么問題?
10.為什么Looper.loop不阻塞主線程?
a.Looper無限循環為啥沒有ANR(B站)
11.Looper如何在子線程中創建?(字節跳動、小米)
12.Looper、handler、線程間的關系。例如一個線程可以有幾個Looper可以對應幾個Handler?(字節跳動、小米)
13.如何更新UI,為什么子線程不能更新UI?(美團)
14.ThreadLocal的原理,以及在Looper是如何應用的?(字節跳動、小米)
15.Android 有哪些存儲數據的方式?
16.SharedPreference原理,commit與apply的區別是什么?使用時需要有哪些注意?
17.如何判斷一個 APP 在前台還是后台?
18.如何做應用保活?
19.一張圖片100x100在內存中的大小?(字節跳動)
20.Intent的原理,作用,可以傳遞哪些類型的參數?
21.如果需要在Activity間傳遞大量的數據怎么辦?
22.打開多個頁面,如何實現一鍵退出?
23.LiveData的生命周期如何監聽的?(B站)

參考解析

1.Android 線程間通信有哪幾種方式

跨進程通信要求把方法調用及其數據分解至操作系統可以識別的程度,並將其從本地進程和地址空間傳輸至遠程進程和地址空間,然后在遠程進程中重新組裝並執行該調用。

然后,返回值將沿相反方向傳輸回來。

Android 為我們提供了以下幾種進程通信機制(供開發者使用的進程通信 API)

  • 文件
  • AIDL (基於 Binder)
  • Messenger (基於 Binder)
  • ContentProvider (基於 Binder)
  • Socket

在上述通信機制的基礎上,我們只需集中精力定義和實現 RPC 編程接口即可。

2.描述下Binder機制原理?(東方頭條)

Linux系統將一個進程分為用戶空間和內核空間。對於進程之間來說,用戶空間的數據不可共享,內核空間的數據可共享,為了保證安全性和獨立性,一個進程不能直接操作或者訪問另一個進程,即Android的進程是相互獨立、隔離的,這就需要跨進程之間的數據通信方式。普通的跨進程通信方式一般需要2次內存拷貝,如下圖所示:

一次完整的 Binder IPC 通信過程通常是這樣:

  • 首先 Binder 驅動在內核空間創建一個數據接收緩存區。
  • 接着在內核空間開辟一塊內核緩存區,建立內核緩存區和內核中數據接收緩存區之間的映射關系,以及內核中數據接收緩存區和接收進程用戶空間地址的映射關系。
  • 發送方進程通過系統調用 copyfromuser() 將數據 copy 到內核中的內核緩存區,由於內核緩存區和接收進程的用戶空間存在內存映射,因此也就相當於把數據發送到了接收進程的用戶空間,這樣便完成了一次進程間的通信。

3.Binder線程池的工作過程是什么樣?(東方頭條)

Binder設計架構中,只有第一個Binder主線程(也就是Binder_1線程)是由應用程序主動創建,Binder線程池的普通線程都是由Binder驅動根據IPC通信需求創建,Binder線程的創建流程圖:

每次由Zygote fork出新進程的過程中,伴隨着創建binder線程池,調用spawnPooledThread來創建binder主線程。當線程執行binder_thread_read的過程中,發現當前沒有空閑線程,沒有請求創建線程,且沒有達到上限,則創建新的binder線程。

Binder的transaction有3種類型:

call: 發起進程的線程不一定是在Binder線程, 大多數情況下,接收者只指向進程,並不確定會有哪個線程來處理,所以不指定線程;

reply: 發起者一定是binder線程,並且接收者線程便是上次call時的發起線程(該線程不一定是binder線程,可以是任意線程)。

async: 與call類型差不多,唯一不同的是async是oneway方式不需要回復,發起進程的線程不一定是在Binder線程, 接收者只指向進程,並不確定會有哪個線程來處理,所以不指定線程。

Binder系統中可分為3類binder線程:

Binder主線程:進程創建過程會調用startThreadPool()過程中再進入spawnPooledThread(true),來創建Binder主線程。編號從1開始,也就是意味着binder主線程名為binder_1,並且主線程是不會退出的。
Binder普通線程:是由Binder Driver來根據是否有空閑的binder線程來決定是否創建binder線程,回調spawnPooledThread(false) ,isMain=false,該線程名格式為binder_x。
Binder其他線程:其他線程是指並沒有調用spawnPooledThread方法,而是直接調用IPC.joinThreadPool(),將當前線程直接加入binder線程隊列。例如: mediaserver和servicemanager的主線程都是binder線程,但system_server的主線程並非binder線程。

4.Handler怎么進行線程通信,原理是什么?(東方頭條)

Handler的消息傳遞機制涉及到四個部分:

  1. Message:線程間傳遞的對象。
  2. MessageQueue: 消息隊列,用來存放Handler發布的Message.
  3. Handler:負責將Message插入到MessageQueue中以及對MessageQueue中的Message進行處理。
  4. Looper:負責從MessageQueue中取出Message,並交給Handler.

其中:

  • Looper存儲在ThreadLocal中,Looper在創建時會同時創建MessageQueue,作為其成員對象.因此Looper和MessageQueue是屬於創建者線程的,各線程之間的Looper和MessageQueue相互獨立。
  • Handler在創建時會從當前線程的ThreadLocal中取得Looper.
  • 發送消息時,在發送線程中調用接收線程中的Handler的sendMessage方法,過程中,Handler會將自身賦予到Message的target中,並將Message插入到Handler對應的MessageQueue中。
  • 而接收線程中的Looper在循環過程中會取出這個Message,通過Message.target取出接收線程中的Handler,並將消息交Handler對象處理。由此實現了跨線程通信。
  • 要注意的是:線程與Looper和MessageQueue是一對一的關系,即一個線程只維護一個Looper和一個MessageQueue;而線程與Handler的關系是一對多,即一個線程可以有很多Handler,一個Handler只對應一個線程,這也是為什么Handler在發送消息時,為什么要將自身賦給Message.target的原因。

5.Handler如果沒有消息處理是阻塞的還是非阻塞的?(字節跳動、小米)

// 下面這個方法有可能會產生阻塞,主要是通過native層的epoll機制,監聽文件描述符的寫入事件實現的。
// -1:一直阻塞;0:不阻塞;n>0:最多阻塞n秒
nativePollOnce(ptr, nextPollTimeoutMillis);

6.handler.post(Runnable) runnable是如何執行的?(字節跳動、小米)

關於這個handler.post(Runnable r)這個方法,用過很多次,

看下源碼,它到底是怎樣處理的。

public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); } 

看下getPostMessage(r)這個方法的源碼,

private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; } 

給message設置了回調,
然后,looper進行消息循環,進行消息的分發,

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

如果回調不為空,handleCallback(msg) 找個方法就會執行,

private static void handleCallback(Message message) { message.callback.run(); }<span style="font-family: Arial, Helvetica, sans-serif;">調用了run方法。</span> 

7.handler的Callback和handlemessage都存在,但callback返回true handleMessage還會執行么?(字節跳動、小米)

sendMessageAtTime 使用的uptimeMillis依賴的是系統以開機時間的絕對時間;

而sendMessageDelayed使用的delayMillis依賴的是系統以開機時間的相對時間。

啥意思呢:就是說delayMillis使用的時候要加一個SystemClock.uptimeMillis(),

也就是sendMessageAtTime等於snedMessageDelayed的情況為uptimeMillis == delayMillis - SystemClock.uptimeMillis()[這是一個相對時間].

SystemClock.uptimeMillis()是獲取系統從開機啟動到現在的時間,期間不包括休眠的時間,這里獲得到的時間是一個相對的時間,而不是通過獲取當前的時間(絕對時間)。

而之所以使用這種方式來計算時間,而不是獲得當前currenttime來計算,在於handler會受到阻塞,掛起狀態,睡眠等,這些時候是不應該執行的;如果使用絕對時間的話,就會搶占資源來執行當前handler的內容,顯然這是不應該出現的情況,所以要避免。

查看源碼如下:

    /** * Enqueue a message into the message queue after all pending messages * before (current time + delayMillis). You will receive it in * {@link #handleMessage}, in the thread attached to this handler. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the message will be processed -- if * the looper is quit before the delivery time of the message * occurs then the message will be dropped. */ public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); } 
    /** * Enqueue a message into the message queue after all pending messages * before the absolute time (in milliseconds) <var>uptimeMillis</var>. * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b> * You will receive it in {@link #handleMessage}, in the thread attached * to this handler. * * @param uptimeMillis The absolute time at which the message should be * delivered, using the * {@link android.os.SystemClock#uptimeMillis} time-base. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the message will be processed -- if * the looper is quit before the delivery time of the message * occurs then the message will be dropped. */ public boolean sendMessageAtTime(Message msg, long uptimeMillis) { boolean sent = false; MessageQueue queue = mQueue; if (queue != null) { msg.target = this; sent = queue.enqueueMessage(msg, uptimeMillis); } else { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); } return sent; } 

8.Handler的sendMessage和postDelay的區別?(字節跳動)

Thread/Hander/Looper是Android在Java線程基礎之上提供的線程通信/消息處理機制,這個眾所周知,不再細說。Handler提供了兩個發送延遲處理任務的api:

/** * Enqueue a message into the message queue after all pending messages * before (current time + delayMillis). You will receive it in * {@link #handleMessage}, in the thread attached to this handler. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the message will be processed -- if * the looper is quit before the delivery time of the message * occurs then the message will be dropped. */ public final boolean sendMessageDelayed(Message msg, long delayMillis) 
/** * Causes the Runnable r to be added to the message queue, to be run * after the specified amount of time elapses. * The runnable will be run on the thread to which this handler * is attached. * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b> * Time spent in deep sleep will add an additional delay to execution. * * @param r The Runnable that will be executed. * @param delayMillis The delay (in milliseconds) until the Runnable * will be executed. * * @return Returns true if the Runnable was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. Note that a * result of true does not mean the Runnable will be processed -- * if the looper is quit before the delivery time of the message * occurs then the message will be dropped. */ public final boolean postDelayed(Runnable r, long delayMillis) 

問題在於,這兩個delay的精度到底能有多大?如何理解?很多APP的定時處理機制都是使用這兩個api遞歸拋延遲任務來實現的。所以有必要研究一下框架層的實現,心中有數。Android這套消息循環機制工作在最上層,距離Linux kernel的時間管理甚遠。本文仍然采用跟蹤分析代碼的方式,基於android7.1.1。

postDelayed()實際上封裝了sendMessageDelayed(),第一時間便殊途同歸:
    public final boolean postDelayed(Runnable r, long delayMillis) { return sendMessageDelayed(getPostMessage(r), delayMillis); } public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); } 

postDelayed()首先通過getPostMessage()將傳入的Runnable對象封裝成一個Message,調用sendMessageDelayed(),而sendMessageDelayed()增加了一個delay時間參數的健壯性檢查,然后轉化成絕對時間,調用sendMessageAtTime()。至此,再多說一句:最簡單的sendMessage()和post()實際上也是sendMessageDelayed(0)的封裝。所以,Handler五花八門的post/send api們本質上無差別。只是為了讓使用者在簡單的情況下避免手動封裝Message,只需提供一個Runnable即可。Handler調用關系整理如下:
post()/postDelayed()/sendMessage()->sendMessageDelayed()->sendMessageAtTime()->enqueueMessage()

postAtTime()->sendMessageAtTime()->enqueueMessage()

postAtFrontOfQueue()->sendMessageAtFrontOfQueue()->enqueueMessage()

最后都以enqueueMessage()告終

enqueueMessage()->MessageQueue.enqueueMessage(Message msg, long when)

如前所述,這時候when已經轉化成絕對系統時間。轉入消息隊列類MessageQueue看一下enqueueMessage()這個方法:

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

這個方法比較簡單,采用線程安全的方式將Message插入到消息隊列中,插入的新消息有三種可能成為消息隊列的head:

(1)消息隊列為空;

(2)參數when為0,因為此時when已經轉成絕對時間,所以只有AtFrontOfQueue系列的API才會滿足這個條件;

(3)當前的head Message執行時間在when之后,即消息隊列中無需要在此Message之前執行的Message。

接下來就要看看消息循環(Looper)如何使用when,這是本文問題的關鍵。關鍵的方法,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 final Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } final long traceTag = me.mTraceTag; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } try { msg.target.dispatchMessage(msg); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } 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(); } } 

從for(;;)可以看到一次循環開始於從消息隊列中去取一個消息,MessageQueue.next(),如果next()返回null,則loop()會返回,本次消息循環結束。取出消息之后,通過Handler.dispatchMessage()處理消息:
msg.target.dispatchMessage(msg);

也就是說,取下一個消息的實際執行時間取決於上一個消息什么時候處理完。再看MessageQueue.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; } // 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; } } 

看到next()實際上也有一個for(;;),而出口只有兩個:消息隊列已經退出,返回null;找到了一個合適的消息,將其返回。如果沒有合適的消息,或者消息隊列為空,會block或者由IdleHandler處理,不在本文問題范疇,暫不展開。主要看找到合適的消息的邏輯:

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

可以看到,如果在消息隊列中順序找到了一個消息msg(前文分析過,消息隊列的插入是由when順序排列,所以如果當前的消息沒有到執行時間,其后的也一定不會到),當前的系統時間小於msg.when,那么會計算一個timeout,以便在到執行時間時wake up;如果當前系統時間大於或等於msg.when,那么會返回msg給Looper.loop()。所以這個邏輯只能保證在when之前消息不被處理,不能夠保證一定在when時被處理。很好理解:

(1)在Loop.loop()中是順序處理消息,如果前一個消息處理耗時較長,完成之后已經超過了when,消息不可能在when時間點被處理。

(2)即使when的時間點沒有被處理其他消息所占用,線程也有可能被調度失去cpu時間片。

(3)在等待時間點when的過程中有可能入隊處理時間更早的消息,會被優先處理,又增加了(1)的可能性。

所以由上述三點可知,Handler提供的指定處理時間的api諸如postDelayed()/postAtTime()/sendMessageDelayed()/sendMessageAtTime(),只能保證在指定時間之前不被執行,不能保證在指定時間點被執行。

9.IdleHandler是什么?怎么使用,能解決什么問題?

那么首先我們就先來通過源碼分析了解一下 IdleHandler 的工作原理吧。

文章出自:IdleHandler原理分析

IdleHandler的源碼分析

首先,我們來看下 IdleHandler 的添加和移除。

    public void addIdleHandler(@NonNull IdleHandler handler) { if (handler == null) { throw new NullPointerException("Can't add a null IdleHandler"); } synchronized (this) { mIdleHandlers.add(handler); } } public void removeIdleHandler(@NonNull IdleHandler handler) { synchronized (this) { mIdleHandlers.remove(handler); } } 

我們可以通過當前線程的 Looper 獲取消息隊列,然后調用 addIdleHandler(IdleHandler) 和 removeIdleHandler(IdleHandler) 添加和移除 IdleHandler。

知道了如何添加和移除 IdleHandler,我們再來看下 IdleHandler 的使用。

在 Looper 的 loop()方法中,會調用 MessageQueue 中的 next() 來取消息處理,而 IdleHandler 就是在此方法中使用了。

    Message next() { // ...... int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { // ...... // 1 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; } // 2 if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } for (int i = 0; i < pendingIdleHandlerCount; i++) { // 3 final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; boolean keep = false; try { // 4 keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf(TAG, "IdleHandler threw exception", t); } // 5 if (!keep) { synchronized (this) { mIdleHandlers.remove(idler); } } } 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; } } 

next()中取消息的部分我們省略了,具體邏輯可參考之前介紹的Android消息機制。現在我們只分析有關 IdleHandler 的這一段代碼。

首先 pendingIdleHandlerCount 默認為-1,所以注釋1中的 pendingIdleHandlerCount<0 條件是成立的,可是還有一個 mMessages == null || now < mMessages.when 條件,這個條件的意思是當前消息隊列沒有消息了,或者說消息隊列有消息,但是消息不是現在處理的(之后某個時間再處理)。通俗說就是當前沒有可處理的消息的時候,就會進入注釋1中計算 IdleHandler 的數量。也就是說,IdleHandler是用在消息隊列閑暇的時候的,當消息隊列中有消息的時候 IdlerHandler 不會起作用,只有在消息隊列處理完消息的時候才會發生作用。

接着在注釋2中,創建了一個數量至少為4的 IdleHandler 數組。並且將消息隊列中的 IdleHandler 全部賦值為數組中的元素。

注釋3中,獲取 IdleHandler,之后釋放數組中的 IdleHandler 引用。

注釋4中,調用 IdleHandler的 queueIdle() 函數,並且返回一個 bool 值。

注釋5中,利用 queueIdle()的返回值來判斷是否需要移除 IdleHandler, 當 queueIdle() 返回 false 的時候,消息隊列會移除該 IdleHandler, 返回為 true時,則繼續保留。

從上述的代碼中我們可以了解到 IdleHandler 的幾點特性。

  • IdleHandler 是在消息隊列當前無可用消息的時候發生作用的,如果你想在消息隊列空暇時做一些處理,那么你就可以在當前線程的消息隊列中添加 IdleHandler,並重寫它的 queueIdle() 函數。
  • IdleHandler 作用次數可為一次或者多次。這取決於它的 queueIdle() 的返回值,如果 queueIdle() 返回 false, 那么消息隊列第一次空暇調用完 queueIdle() 之后便會將該 IdleHandler 移除。如果返回 true, 那意味着每次只要消息隊列空閑,就會調用一次 queueIdle()

可能有些小伙伴這個時候會有些疑問,如果 queueIdle() 返回 true 的時候,如果消息隊列一直沒有消息處於空閑狀態,是不是就會無限循環調用 queueIdle() ? 回答這個問題之前,我們需要回想起以前介紹的在 next()中的函數 nativePollOnce()。我們之前介紹過,這個函數在 native 層會將線程阻塞,等有新事件來臨的時候再進行喚醒,所以就不會出現我們之前猜測的無限調用 queueIdle() 的問題。

IdleHandler 有什么作用呢?

那從源碼角度討論完 IdleHandler ,了解了它的特性和工作原理,接下來我們就來分析一下它有什么作用。

其實分析 IdleHandler 的作用也是需要從它的特性和工作原理來思考的。
首先,IdleHandler 是在消息隊列當前無可用消息的時候發生作用的,那么我們就可以猜測 IdleHandler 是不是可用來執行一些優化動作。比如,處理一些不是十分重要的初始化操作。在啟動 Activity 的時候,如果將一些不太重要的初始化動作 onCreate()onResume()等函數中可能會造成頁面顯示緩慢的問題,影響應用啟動速度。而如果將這些操作使用 IdleHandler 的 queueIdle() 來進行初始化,那么就可以在線程空閑的時候來進行這些初始化動作,加快用戶看到界面的速度,從而提高用戶體驗。但同時需要注意一點, IdleHandler 依賴於消息隊列中的消息,如果當前一直有消息進入消息隊列,那么 IdleHandler 可能就一直都無法有執行的機會。

其次,我們有時候想獲取某個控件的寬高,又或者是想要某個View繪制完之后添加依賴於這個View的View,那么就可以使用 IdleHandler 來進行獲取寬高或者添加View。 當然也可以使用 View.post()來實現,區別是前者是在消息隊列空閑的時候執行, 后者是作為一個消息來執行。

LeakCanary 中也有使用到 IdleHandler, LeakCanary 2.0中的源碼使用Kotlin編寫的,還沒有研究。等之后看了再補上。

IdleHandler 在系統源碼中的運用

IdleHandler 在系統源碼中也有過使用,其中 GcIdler便實現了 IdleHandler 接口。我們來看下 GcIdle 是如何工作的。

   final class GcIdler implements MessageQueue.IdleHandler { @Override public final boolean queueIdle() { doGcIfNeeded(); return false; } } void doGcIfNeeded() { mGcIdlerScheduled = false; final long now = SystemClock.uptimeMillis(); //Slog.i(TAG, "**** WE MIGHT WANT TO GC: then=" + Binder.getLastGcTime() // + "m now=" + now); if ((BinderInternal.getLastGcTime()+MIN_TIME_BETWEEN_GCS) < now) { //Slog.i(TAG, "**** WE DO, WE DO WANT TO GC!"); BinderInternal.forceGc("bg"); } } 

GcIdler 用來實現強制性GC操作的。當現在時間超過了上一次GC的 MIN_TIME_BETWEEN_GCS == 5000, 也即離上一次GC超過5秒之后就會再一次強制性觸發GC, 並且每個GcIdler 返回 false,只觸發一次。而 GcIdler是怎么添加的呢?我們可以看下。

    void scheduleGcIdler() { if (!mGcIdlerScheduled) { mGcIdlerScheduled = true; Looper.myQueue().addIdleHandler(mGcIdler); } mH.removeMessages(H.GC_WHEN_IDLE); } 

通過調用 scheduleGcIdler() 來進行觸發。而 scheduleGcIdler()又是通過 ActivityThread 中的 Handler H發送消息觸發的。再往上追溯,我們可以知道是在 AMS 中的這兩個方法調用之后觸發:

  • doLowMemReportIfNeededLocked
  • activityIdle

所以Android 會在內存不夠的時候使用 IdleHandler 來進行強制性GC 優化。或者可能當 ActivityThread 的 handleResumeActivity方法被調用時觸發的。

10.為什么Looper.loop不阻塞主線程?

我們平時看IntentService時看到了Thread的run方法如下:

@Override public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = -1; } 

代碼Looper.loop()是一個for死循環,然后突然想到主線程中也有Looper為什么不卡主線程於是找到了ActivityThread的源碼

public static void main(String[] args) { 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方法的退出就是Looper.loop();的執行完畢,所有事件都是Looper的監聽,主線程本事就是個阻塞。

Android是事件驅動,Looper內部是一個while死循華,只有程序退出后循環才會停止,如果Looper使用中死掉了,任何事件都不會有反應了。事件只會阻塞Looper,而Looper不會阻塞事件。

最后

關於如何學習Android Framework開發知識,最近有幸在前阿里技術總監手里扒到這份Android framework高級開發筆記,部分知識章節發布到了在知乎上竟然1000+點贊,今天就拿出來分享給大家。

本筆記講解了Framework的主要模塊,從環境的部署到技術的應用,再到項目實戰,讓我們不僅是學習框架技術的使用,而且可以學習到使用架構如何解決實際的問題,由淺入深,詳細解析Framework,讓你簡單高效學完這塊知識!

由於篇幅有限,僅展示部分內容,所有的知識點 整理的詳細內容都放在了我的【GitHub】,有需要的朋友自取。

第一章:深入解析Binder

Binder機制作為進程間通信的一種手段,基本上貫穿了andorid框架層的全部。所以首先必須要搞懂的Android Binder的基本通信機制。Binder機制作為進程間通信的一種手段,基本上貫穿了andorid框架層的全部。所以首先必須要搞懂的Android Binder的基本通信機制。

第二章:深入解析Handler

相信大家都有這樣的感受:網上分析 Handler 機制原理的文章那么多, 為啥還要畫蛇添足整理這份筆記呢?不是說前人們寫的文章不好,我就是覺得他們寫的不細, 有些點不講清楚,邏輯很難通順的,每次我學個什么東西時遇到這種情況都賊難受。

本章先宏觀理論分析與 Message 源碼分析,再到MessageQueue 的源碼分析,Looper 的源碼分析,handler 的源碼分析,Handler 機制實現原理總結。最后還整理Handler 所有面試題大全解析。

Handler這章內容很長,但思路是循序漸進的,如果你能堅持讀完我相信肯定不會讓你失望。

第三章:Dalvik VM 進程系統

Andorid系統啟動、init 進程、Zygote、SystemServer啟動流程、 應用程序的創建使用,Activity的創建、銷毀 Handler和Looper。

第四章 深入解析 WMS

窗口管理框架 系統動畫框架 View的工作原理。

第五章 PackagerManagerService

包管理服務,資源管理相關類。

如有需要獲取完整的資料文檔的朋友點擊我的GitHub免費獲取。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM