帶著問題學(xué)習(xí) Android Handler 消息機(jī)制

學(xué)習(xí) Android Handler 消息機(jī)制

一、提出問題

面試時(shí)常被問到的問題:

  • 簡述 Android 消息機(jī)制
  • Android 中 Handler,Looper,MessageQueue,Message 有什么關(guān)系?

這倆問題其實(shí)是一個(gè)問題,其實(shí)只要搞清楚了 Handler,Looper,MessageQueue,Message 的作用和聯(lián)系,就理解了 Android 的 Handler 消息機(jī)制。那么再具體一點(diǎn):

  1. 為什么在主線程可以直接使用 Handler?
  2. Looper 對(duì)象是如何綁定 MessageQueue 的?
  3. MessageQueue 里的消息從哪里來?Handler是如何往MessageQueue中插入消息的?
  4. Message 是如何綁定 Handler 的?
  5. Handler 如何綁定 MessageQueue?
  6. 關(guān)于 handler,在任何地方 new handler 都是什么線程下?
  7. Looper 循環(huán)拿到消息后怎么處理?

二、解決問題

那么,我們從主線程的消息機(jī)制開始分析:

2.1 主線程 Looper 的創(chuàng)建和循環(huán)

Android 應(yīng)用程序的入口是 main 函數(shù),主線程 Looper 的創(chuàng)建也是在這里完成的。

ActivityThread --> main() 函數(shù)

public static void main(){
        // step1: 創(chuàng)建主線程Looper對(duì)象
        Looper.prepareMainLooper();
        
        ActivityThread thread = new ActivityThread();
        // 綁定應(yīng)用進(jìn)程,布爾標(biāo)記是否為系統(tǒng)進(jìn)程
        thread.attach(false);
        // 實(shí)例化主線程 Handler
        if(sMainThreadHandler == null){
           sMainThreadHandler = thread.getHandler();
        }
        // step2: 開始循環(huán)
        Loop.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
}

Looper.prepareMainLooper()用來創(chuàng)建主線程的 Looper 對(duì)象,接下來先看這個(gè)方法的實(shí)現(xiàn)。

2.1.1 創(chuàng)建主線程 Looper

Looper --> prepareMainLooper()

private static Looper sMainLooper;  // guarded by Looper.class

public static void prepareMainLooper(){
        // step1: 調(diào)用本類 prepare 方法
        prepare(false);
        // 線程同步,如果變量 sMainLooper 不為空拋出主線程 Looper 已經(jīng)創(chuàng)建
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            // step2: 調(diào)用本類 myLooper 方法
            sMainLooper = myLooper();
        }
}

prepareMainLooper() 方法主要是使用 prepare(false) 創(chuàng)建當(dāng)前線程的 Looper 對(duì)象,再使用 myLooper() 方法來獲取當(dāng)前線程的 Looper 對(duì)象。

step1: Looper --> prepare()

// ThreadLocal 為每個(gè)線程保存單獨(dú)的變量
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
// Looper 類的 MessageQueue 變量
final MessageQueue mQueue;
// quitAllowed 是否允許退出,這里是主線程的 Looper 不可退出
private static void prepare(boolean quitAllowed) {
        // 首先判定 Looper 是否存在
        if(sThreadLocal.get() != null){
                throw new RuntimeException("Only one Looper may be created per thread");
        }
        // 保存線程的副本變量
        sThreadLoacal.set(new Looper(quitAllowed));
}

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}
  • prepare() 方法中用 ThreadLocal 來保存主線程的 Looper 對(duì)象。ThreadLocal 可以看作是一個(gè)用來儲(chǔ)存數(shù)據(jù)的類,類似 HashMap、ArrayList等集合類,它存放著屬于當(dāng)前線程的變量。

  • ThreadLocal 提供了 get/set 方法分別用來獲取和保存變量。
    比如在主線程通過 prepare() 方法來創(chuàng)建 Looper 對(duì)象,并使用 sThreadLoacal.set(new Looper(quitAllowed)) 來保存主線程的 Looper 對(duì)象,那么在主線程調(diào)用 myLooper()(實(shí)際調(diào)用了 sThreadLocal.get() 方法) 就是通過 ThreadLocal 來獲取主線程的 Looper 對(duì)象。如果在子線程調(diào)用這些方法就是通過 ThreadLocal 保存和獲取屬于子線程的 Looper 對(duì)象。

更多關(guān)于 ThreadLocal 的原理:

深入剖析ThreadLocal實(shí)現(xiàn)原理以及內(nèi)存泄漏問題

問題1:為什么在主線程可以直接使用 Handler?
因?yàn)橹骶€程已經(jīng)創(chuàng)建了 Looper 對(duì)象并開啟了消息循環(huán),通過上文的代碼就可以看出來。

問題2:Looper 對(duì)象是如何綁定 MessageQueue 的?或者說 Looper 對(duì)象創(chuàng)建 MessageQueue 過程。
很簡單,Looper 有個(gè)一成員變量 mQueue,它就是 Looper 對(duì)象默認(rèn)保存的 MessageQueue。上面代碼中 Looper 有一個(gè)構(gòu)造器,新建 Looper 對(duì)象時(shí)會(huì)直接創(chuàng)建 MessageQueue 并賦值給 mQueue。
問題2解決:在 new Looper 時(shí)就創(chuàng)建了 MessageQueue 對(duì)象并賦值給 Looper 的成員變量 mQueue。

step2: Looper --> myLooper()

// 也就是使用本類的ThreadLocal對(duì)象獲取之前創(chuàng)建保存的Looper對(duì)象
public static @Nullable Looper myLooper() {
     return sThreadLocal.get();
}

這個(gè)方法就是通過 sThreadLocal 變量獲取當(dāng)前線程的 Looper 對(duì)象,比較常用的一個(gè)方法。上文主線程 Looper 對(duì)象創(chuàng)建后使用該方法獲取了 Looper 對(duì)象。

2.1.2 開始循環(huán)處理消息

回到最開始的 main() 函數(shù),在創(chuàng)建了 Looper 對(duì)象以后就調(diào)用了 Looper.loop() 來循環(huán)處理消息,貼一下大致代碼:

public static void main(){
        // step1: 創(chuàng)建主線程Looper對(duì)象
        Looper.prepareMainLooper();
        ...
        // step2: 開始循環(huán)
        Loop.loop();
}

Looper --> loop()

public static void loop() {
    // step1: 獲取當(dāng)前線程的 Looper 對(duì)象
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    // step2: 獲取 Looper 保存的 MessageQueue 對(duì)象
    final MessageQueue queue = me.mQueue;

    ...
    // step3: 循環(huán)讀取消息,如果有則調(diào)用消息對(duì)象中儲(chǔ)存的 handler 進(jìn)行發(fā)送
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        ...
        try {
            // step4: 使用 Message 對(duì)象保存的 handler 對(duì)象處理消息
            msg.target.dispatchMessage(msg);
            end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        ...
        msg.recycleUnchecked();
    }
}
  • step1 : myLooper() 方法就是通過 ThreadLocal 獲取當(dāng)前線程的 Looper 對(duì)象,注意在哪個(gè)線程使用該方法就獲取的該線程的 Looper 對(duì)象。
  • step2 :me.mQueue,這個(gè) mQueue 就是上面問題2所說的在 Looper 對(duì)象創(chuàng)建時(shí)新建的 MessageQueue 變量。
  • step3 :接下來是一個(gè) for 循環(huán),首先通過 queue.next() 來提取下一條消息,具體是怎么提取的可以參考下面文章的 4.2 節(jié):

Android消息機(jī)制1-Handler(Java層)

獲取到下一條消息,如果 MessageQueue 中沒有消息,就會(huì)進(jìn)行阻塞。那么如果存在消息,它又是怎么放入 MessageQueue 的呢?或者說MessageQueue 里的消息從哪里來?Handler是如何往MessageQueue中插入消息的?先不說這個(gè),把這個(gè)問題叫作問題3后面分析。

  • step4 :msg.target.dispatchMessage(msg);這個(gè)方法最終會(huì)調(diào)用 Handler 的 handleMessage(msg) 方法。同時(shí)這里又產(chǎn)生個(gè)問題:msg.target 是何時(shí)被賦值的?,也就是說Message 是如何綁定 Handler 的?先稱之為問題4。那么接著看 Handler 的 dispatchMessage 方法:

Handler --> dispatchMessage

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

private static void handleCallback(Message message) {
   message.callback.run();
}

public void handleMessage(Message msg) {
}

可以看到該方法最后執(zhí)行了 handleMessage() 方法,這是一個(gè)空方法也就是需要我們覆寫并實(shí)現(xiàn)的。另外 dispatchMessage() 也體現(xiàn)出一個(gè)問題:

消息分發(fā)的優(yōu)先級(jí):

  • Message 的回調(diào)方法:message.callback.run(); 優(yōu)先級(jí)最高;
  • Handler 的回調(diào)方法:mCallback.handleMessage(msg)優(yōu)先級(jí)次于上方;
  • Handler 的回調(diào)方法:handleMessage() 優(yōu)先級(jí)最低。

到這里 Looper 循環(huán)并通過 Handler 發(fā)送消息有一個(gè)整體的流程了,接下來分析 Handler 在消息機(jī)制中的主要作用以及和 Looper、Message 的關(guān)系。

2.2 Handler 的創(chuàng)建和作用

上面說到 loop() 方法在不斷從消息隊(duì)列 MessageQueue 中取出消息(queue.next() 方法),如果沒有消息則阻塞,反之交給 Message 綁定的 Handler 處理。回顧一下沒解決的兩個(gè)問題:

  • 問題3:MessageQueue 里的消息從哪里來?Handler 是如何往 MessageQueue 中插入消息的?
  • 問題4:msg.target 是何時(shí)被賦值的?,也就是說Message 是如何綁定 Handler 的?

既然要解決 Handler 插入消息的問題,就要看 Handler 發(fā)送消息的過程。

2.2.1 Handler 發(fā)送消息

Handler --> sendMessage(Message msg);

final MessageQueue mQueue;

public final boolean sendMessage(Message msg){
    return sendMessageDelayed(msg, 0);
}
// 發(fā)送延時(shí)消息
public final boolean sendMessageDelayed(Message msg, long delayMillis){
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
// 指定時(shí)間發(fā)送消息
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);
}
// 處理消息,賦值 Message 對(duì)象的 target,消息隊(duì)列插入消息
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

可以看到調(diào)用 sendMessage(Message msg) 方法最終會(huì)調(diào)用到 enqueueMessage() 方法,這個(gè)方法主要有兩個(gè)作用:賦值 Message 對(duì)象的 target、消息隊(duì)列插入消息。

  • 賦值 msg 的 target:msg.target = this 把發(fā)送消息的 Handler 賦值給 msg 對(duì)象的 target。那么問題 4 就解決了:Handler 執(zhí)行發(fā)送消息的過程中將自己綁定給了 Message 的 target,這樣兩者之間就產(chǎn)生了聯(lián)系
  • 消息隊(duì)列插入消息:queue.enqueueMessage(msg, uptimeMillis) queue 是 MessageQueue 的一個(gè)實(shí)例,queue.enqueueMessage(msg, uptimeMillis)是執(zhí)行 MessageQueue 的enqueueMessage方法來插入消息。這樣問題 3 就找到答案:Handler 在發(fā)送消息的時(shí)候執(zhí)行 MessageQueue 的enqueueMessage方法來插入消息;關(guān)于 MessageQueue 是怎么執(zhí)行插入消息的過程,參考下方文章 4.3 節(jié)

Android消息機(jī)制1-Handler(Java層)

  • 上面 Handler 發(fā)送消息使用了 MessageQueue 的實(shí)例 queue,可以看到這個(gè) queue 是上一個(gè)方法 sendMessageAtTime 中由 Handler 的成員變量 mQueue 賦值的,那么 mQueue 是哪來的?問題 5:Handler 如何綁定 MessageQueue?先劇透一下 Handler 綁定的是 Looper 的 MessageQueue 對(duì)象,Looper 的 MessageQueue 對(duì)象是在 Looper 創(chuàng)建時(shí)就 new 的。

要了解 Handler 的 MessageQueue 對(duì)象是怎么賦值的就要看 Handler 的構(gòu)造函數(shù)了,Handler 創(chuàng)建的時(shí)候作了一些列操作比如獲取當(dāng)前線程的 Looper,綁定 MessageQueue 對(duì)象等。

2.2.2 Handler 的創(chuàng)建

下面是 Handler 無參構(gòu)造器和主要的構(gòu)造器,另外幾個(gè)重載的構(gòu)造器有些是通過傳遞不同參數(shù)調(diào)用包含兩個(gè)參數(shù)的構(gòu)造器。兩個(gè)參數(shù)構(gòu)造函數(shù)第一個(gè)參數(shù)為 callback 回調(diào),第二個(gè)函數(shù)用來標(biāo)記消息是否異步。

// 無參構(gòu)造器
public Handler() {
     this(null, false);
}

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());
        }
    }
    // step1:獲取當(dāng)前線程 Looper
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
    }
    // step2:獲取 Looper 對(duì)象綁定的 MessageQueue 對(duì)象并賦值給 Handler 的 mQueue
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}
  • step1:調(diào)用myLooper() 方法,該方法是使用 sThreadLocal 對(duì)象獲取當(dāng)前線程的 Looper 對(duì)象,回顧一下:
public static @Nullable Looper myLooper() {
     return sThreadLocal.get();
}

如果獲取的 Looper 對(duì)象為 null,說明沒有執(zhí)行 Looper.prepare() 為當(dāng)前線程保存 Looper 變量,就會(huì)拋出 RuntimeException。這里又說明了Handler 必須在有 Looper 的線程中使用,報(bào)錯(cuò)不說,沒有 Looper 就無法綁定 MessageQueue 對(duì)象也就無法進(jìn)行更多有關(guān)消息的操作。

  • step2:mQueue = mLooper.mQueue 說明了 Handler 的 MessageQueue 對(duì)象是由當(dāng)前線程 Looper 的 MessageQueue 對(duì)象賦值的。這里問題 5 解決:Handler 在創(chuàng)建時(shí)綁定了當(dāng)前線程 Looper 的 MessageQueue 對(duì)象。
  • 由于 Handler 和 Looper 可以看作使用的是同一個(gè) MessageQueue 對(duì)象,所以 Handler 和 Looper 可以共享消息隊(duì)列 MessageQueue。Handler 發(fā)送消息(用 mQueue 往消息對(duì)列插入消息),Looper 可以方便的循環(huán)使用 mQueue 查詢消息,如果查詢到消息,就可以用 Message 對(duì)象綁定的 Handler 對(duì)象 target 去處理消息,反之則阻塞。

既然說到了 Handler 的構(gòu)造器,就想到一個(gè)問題:問題 6:關(guān)于 handler,在任何地方 new handler 都是什么線程下?這個(gè)問題要分是否傳遞 Looper 對(duì)象來看。

  1. 不傳遞 Looper 創(chuàng)建 Handler:Handler handler = new Handler();上文就是 Handler 無參創(chuàng)建的源碼,可以看到是通過 Looper.myLooper() 來獲取 Looper 對(duì)象,也就是說對(duì)于不傳遞 Looper 對(duì)象的情況下,在哪個(gè)線程創(chuàng)建 Handler 默認(rèn)獲取的就是該線程的 Looper 對(duì)象,那么 Handler 的一系列操作都是在該線程進(jìn)行的。
  2. 傳遞 Looper 對(duì)象創(chuàng)建 Handler:Handler handler = new Handler(looper);那么看看傳入 Looper 的構(gòu)造函數(shù):
public Handler(Looper looper) {
    this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
    this(looper, callback, false);
}
// 第一個(gè)參數(shù)是 looper 對(duì)象,第二個(gè) callback 對(duì)象,第三個(gè)消息處理方式(是否異步)
public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

可以看出來傳遞 Looper 對(duì)象 Handler 就直接使用了。所以對(duì)于傳遞 Looper 對(duì)象創(chuàng)建 Handler 的情況下,傳遞的 Looper 是哪個(gè)線程的,Handler 綁定的就是該線程。

到這里 Looper 和 Handler 就有一個(gè)大概的流程了,接下來看一個(gè)簡單的子線程 Handler 使用例子:

new Thread() {
    @Override
    public void run() {
        // step1
        Looper.prepare();
         // step2
        Handler handler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                if(msg.what == 1){
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(MainActivity.this,"HandlerTest",Toast.LENGTH_SHORT).show();
                        }
                    });
                     // step5
                    Looper.myLooper().quit();
                }
            }
        };
         // step3
        handler.sendEmptyMessage(1);
         // step4
        Looper.loop();
    }
}.start();
  • step1: 調(diào)用 Looper.prepare(); 為當(dāng)前線程創(chuàng)建 Looper 對(duì)象,同時(shí)也就創(chuàng)建了 MessageQueue,之后將該線程的 Looper 對(duì)象保存在 ThreadLocal 中。注意這里的一切操作都在子線程中,如果不調(diào)用 Looper.prepare() 就使用 Handler 會(huì)報(bào)錯(cuò)。
  • step2: 創(chuàng)建 Handler 對(duì)象,覆寫 handleMessage 處理消息,等待該 Handler 發(fā)送的消息處理時(shí)會(huì)調(diào)用該方法。
  • step3: 使用 handler 發(fā)送消息,這里只是示例,畢竟自己給自己發(fā)送消息沒啥必要。發(fā)送的過程中會(huì)將自己賦值給 msg.target,然后再將消息插入到 Looper 綁定的 MessageQueue 對(duì)象中。
  • step4: 調(diào)用 Looper.loop(); 首先獲取當(dāng)前線程的 Looper 對(duì)象,根據(jù) Looper 對(duì)象就可以拿到 Looper 保存的 MessageQueue 對(duì)象 mQueue。有了 MessageQueue 對(duì)象就可以 for 循環(huán)獲取它保存的消息 Message 對(duì)象,如果消息不存在就返回 null 阻塞,反之則使用 Message 中保存的 Handler:msg.target 來處理消息,最終調(diào)用 handleMessage 也就是之前覆寫的方法來處理消息。
  • step5: 邏輯處理完畢以后,應(yīng)在最后使用 quit 方法來終止消息循環(huán),否則這個(gè)子線程就會(huì)一直處于等待的狀態(tài),而如果退出Looper以后,這個(gè)線程就會(huì)立刻終止,因此建議不需要的時(shí)候終止Looper。

三、總結(jié)和其它

3.1 Handler、Looper、MessageQueue、Message

  1. Handler 用來發(fā)送消息,創(chuàng)建時(shí)先獲取默認(rèn)或傳遞來的 Looper 對(duì)象,并持有 Looper 對(duì)象包含的 MessageQueue,發(fā)送消息時(shí)使用該 MessageQueue 對(duì)象來插入消息并把自己封裝到具體的 Message 中;
  2. Looper 用來為某個(gè)線程作消息循環(huán)。Looper 持有一個(gè) MessageQueue 對(duì)象 mQueue,這樣就可以通過循環(huán)來獲取 MessageQueue 所維護(hù)的 Message。如果獲取的 MessageQueue 沒有消息時(shí),便阻塞在 loop 的queue.next() 中的 nativePollOnce() 方法里,反之則喚醒主線程繼續(xù)工作,之后便使用 Message 封裝的 handler 對(duì)象進(jìn)行處理。
  3. MessageQueue 是一個(gè)消息隊(duì)列,它不直接添加消息,而是通過與 Looper 關(guān)聯(lián)的 Handler 對(duì)象來添加消息。
  4. Message 包含了要傳遞的數(shù)據(jù)和信息。

3.2 Android中為什么主線程不會(huì)因?yàn)長ooper.loop()里的死循環(huán)卡死?

這是知乎上的問題,感覺問的挺有意思。平時(shí)可能不太會(huì)太深究這些問題,正好有大神回答那就記錄一下吧。

  1. 為什么不會(huì)因?yàn)樗姥h(huán)卡死?
    線程可以看作是一段可執(zhí)行代碼,當(dāng)代碼執(zhí)行完畢線程的生命周期就該終止了。對(duì)于主線程來說我們不希望它執(zhí)行一段時(shí)間后退出,所以簡單做法就是可執(zhí)行代碼是能一直執(zhí)行下去的,死循環(huán)便能保證不會(huì)被退出。既然是死循環(huán)那么怎么去處理消息呢,通過創(chuàng)建新線程的方式。
  2. 為這個(gè)死循環(huán)準(zhǔn)備了一個(gè)新線程
    在進(jìn)入死循環(huán)之前便創(chuàng)建了新binder線程,在代碼ActivityThread.main()中:
public static void main(){
        ...
        Looper.prepareMainLooper();

        //創(chuàng)建ActivityThread對(duì)象
        ActivityThread thread = new ActivityThread();

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

        if(sMainThreadHandler == null){
           sMainThreadHandler = thread.getHandler();
        }
        // step2: 開始循環(huán)
        Loop.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
}

thread.attach(false);便會(huì)創(chuàng)建一個(gè)Binder線程(具體是指ApplicationThread,Binder的服務(wù)端,用于接收系統(tǒng)服務(wù)AMS發(fā)送來的事件),該Binder線程通過Handler將Message發(fā)送給主線程。

  1. 主線程的死循環(huán)一直運(yùn)行是不是特別消耗CPU資源呢?

其實(shí)不然,這里就涉及到Linux pipe/epoll機(jī)制,簡單說就是在主線程的MessageQueue沒有消息時(shí),便阻塞在loop的queue.next()中的nativePollOnce()方法里,詳情見Android消息機(jī)制1-Handler(Java層),此時(shí)主線程會(huì)釋放CPU資源進(jìn)入休眠狀態(tài),直到下個(gè)消息到達(dá)或者有事務(wù)發(fā)生,通過往pipe管道寫端寫入數(shù)據(jù)來喚醒主線程工作。這里采用的epoll機(jī)制,是一種IO多路復(fù)用機(jī)制,可以同時(shí)監(jiān)控多個(gè)描述符,當(dāng)某個(gè)描述符就緒(讀或?qū)懢途w),則立刻通知相應(yīng)程序進(jìn)行讀或?qū)懖僮鳎举|(zhì)同步I/O,即讀寫是阻塞的。所以說,主線程大多數(shù)時(shí)候都是處于休眠狀態(tài),并不會(huì)消耗大量CPU資。

  1. Activity的生命周期是怎么實(shí)現(xiàn)在死循環(huán)體外能夠執(zhí)行起來的?
    上文 main 函數(shù)有一部分獲取 sMainThreadHandler 的代碼:
final H mH = new H();

public static void main(){
        ...
        if(sMainThreadHandler == null){
           sMainThreadHandler = thread.getHandler();
        }
        ...
}

final Handler getHandler() {
    return mH;
}

類 H 繼承了 Handler,在主線程創(chuàng)建時(shí)就創(chuàng)建了這個(gè) Handler 用于處理 Binder 線程發(fā)送來的消息。

Activity的生命周期都是依靠主線程的Looper.loop,當(dāng)收到不同Message時(shí)則采用相應(yīng)措施:

在H.handleMessage(msg)方法中,根據(jù)接收到不同的msg,執(zhí)行相應(yīng)的生命周期。
比如收到msg=H.LAUNCH_ACTIVITY,則調(diào)用ActivityThread.handleLaunchActivity()方法,最終會(huì)通過反射機(jī)制,創(chuàng)建Activity實(shí)例,然后再執(zhí)行Activity.onCreate()等方法;
再比如收到msg=H.PAUSE_ACTIVITY,則調(diào)用ActivityThread.handlePauseActivity()方法,最終會(huì)執(zhí)行Activity.onPause()等方法。 上述過程,我只挑核心邏輯講,真正該過程遠(yuǎn)比這復(fù)雜。

3.3 Handler 使用造成內(nèi)存泄露

  1. 有延時(shí)消息,要在Activity銷毀的時(shí)候移除Messages
  2. 匿名內(nèi)部類導(dǎo)致的泄露改為匿名靜態(tài)內(nèi)部類,并且對(duì)上下文或者Activity使用弱引用。

具體操作可以參考文章:

Handler內(nèi)存泄露原理及解決方法

參考資料:

《Android 開發(fā)藝術(shù)探索》
Android消息機(jī)制1-Handler(Java層)
Android Handler消息機(jī)制實(shí)現(xiàn)原理

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容