Android線程管理(一)——線程通信

線程通信、ActivityThread及Thread類是理解Android線程管理的關(guān)鍵。

線程,作為CPU調(diào)度資源的基本單位,在Android等針對(duì)嵌入式設(shè)備的操作系統(tǒng)中,有著非常重要和基礎(chǔ)的作用。本小節(jié)主要從以下三個(gè)方面進(jìn)行分析:

  1. Android線程管理(一)——線程通信
  2. Android線程管理(二)——ActivityThread
  3. Android線程管理(三)——Thread

一、Handler、MessageQueue、Message及Looper四者的關(guān)系

在開(kāi)發(fā)Android多線程應(yīng)用時(shí),Handler、MessageQueue、Message及Looper是老生常談的話題。但想徹底理清它們之間的關(guān)系,卻需要深入的研究下它們各自的實(shí)現(xiàn)才行。首先,給出一張它們之間的關(guān)系圖:

Handler、MessageQueue、Message及Looper四者的關(guān)系
  • Looper依賴于MessageQueue和Thread,因?yàn)槊總€(gè)Thread只對(duì)應(yīng)一個(gè)Looper,每個(gè)Looper只對(duì)應(yīng)一個(gè)MessageQueue。
  • MessageQueue依賴于Message,每個(gè)MessageQueue對(duì)應(yīng)多個(gè)Message。即Message被壓入MessageQueue中,形成一個(gè)Message集合。
  • Message依賴于Handler進(jìn)行處理,且每個(gè)Message最多指定一個(gè)Handler來(lái)處理。Handler依賴于MessageQueue、Looper及Callback。

從運(yùn)行機(jī)制來(lái)看,Handler將Message壓入MessageQueue,Looper不斷從MessageQueue中取出Message(當(dāng)MessageQueue為空時(shí),進(jìn)入休眠狀態(tài)),其target handler則進(jìn)行消息處理。因此,要徹底弄清Android的線程通信機(jī)制,需要了解以下三個(gè)問(wèn)題:

  • Handler的消息分發(fā)、處理流程
  • MessageQueue的屬性及操作
  • Looper的工作原理

1.1 Handler的消息分發(fā)、處理流程

Handler主要完成Message的入隊(duì)(MessageQueue)和處理,下面將通過(guò)Handler的源碼分析其消息分發(fā)、處理流程。首先,來(lái)看下Handler類的方法列表:

Handler源碼中的方法列表

從上圖中可以看出,Handler類核心的方法包括:1)構(gòu)造器;2)分發(fā)消息;3)處理消息;4)post發(fā)送消息;5)send發(fā)送消息;6)remove消息和回調(diào)。
首先,從構(gòu)造方法來(lái)看,構(gòu)造器的多態(tài)最終通過(guò)調(diào)用如下方法實(shí)現(xiàn),即將實(shí)參賦值給Handler類的內(nèi)部域。

final MessageQueue mQueue;
final Looper mLooper;
final Callback mCallback;
final boolean mAsynchronous;

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

其次,消息的入隊(duì)是通過(guò)post方法和send方法來(lái)實(shí)現(xiàn)的。

public final boolean postAtTime(Runnable r, long uptimeMillis) {
    return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageAtTime(msg, 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);
}

兩者的區(qū)別在于參數(shù)類型不同,post方法傳入的實(shí)例對(duì)象實(shí)現(xiàn)了Runnable接口,然后在內(nèi)部通過(guò)getPostMessage方法將其轉(zhuǎn)換為Message,最終通過(guò)send方法發(fā)出;send方法傳入的實(shí)例對(duì)象為Message類型,在實(shí)現(xiàn)中,將Message壓入MessageQueue。

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

通過(guò)Handler將Message壓入MessageQueue之后,Looper將其輪詢后交由Message的target handler處理。Handler首先會(huì)對(duì)消息進(jìn)行分發(fā)。首先判斷Message的回調(diào)處理接口Callback是否為null,不為null則調(diào)用該Callback進(jìn)行處理;否判斷Handler的回調(diào)接口mCallback是否為null,不為null則調(diào)用該Callback進(jìn)行處理;如果上述Callback均為null,則調(diào)用handleMessage方法處理。

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

handleMessage方法在Handler的子類中必須實(shí)現(xiàn)。即消息具體的處理交由應(yīng)用軟件實(shí)現(xiàn)。

/**
 * Subclasses must implement this to receive messages.
 */
public void handleMessage(Message msg) {
}

回到Activity(Fragment),在Handler的子類中實(shí)現(xiàn)handleMessage方法。這里需要注意一個(gè)內(nèi)存泄露的問(wèn)題,比較下述兩種實(shí)現(xiàn)方式,第一種直接定義Handler的實(shí)現(xiàn),第二種通過(guò)靜態(tài)內(nèi)部類繼承Handler,定義繼承類的實(shí)例。

Handler mHandler = new Handler() {
            
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
            
        // 根據(jù)msg調(diào)用Activity的方法
    }
};
static class MyHandler extends Handler {

    WeakReference<DemoActivity> mActivity;

    public MyHandler(DemoActivity demoActivity) {
        mActivity = new WeakReference<DemoActivity>(demoActivity);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        DemoActivity theActivity = mActivity.get();

        // 根據(jù)msg調(diào)用theActivity的方法
}

不繞彎子,直接說(shuō)明為什么第一種方式會(huì)引起內(nèi)存泄露,而第二種不會(huì)。

在第一種方式中,mHandler通過(guò)匿名內(nèi)部類方式實(shí)例化,在Java中,內(nèi)部類會(huì)強(qiáng)持有外部類的引用(handleMessage方法中可以直接調(diào)用Activity的方法),在外部Activity調(diào)用onDestroy()方法之后,如果Handler的MessageQueue依然有未處理的消息,那么由于Handler持有Activity的引用導(dǎo)致Activity無(wú)法被系統(tǒng)GC回收,從而引起內(nèi)存泄露。

在第二種方式中,首先繼承Handler定義靜態(tài)內(nèi)部類,由于MyHandler為靜態(tài)類,即使定義在Activity的內(nèi)部,也與Activity沒(méi)有邏輯上的聯(lián)系,即不會(huì)持有外部Activity的引用;其次,在靜態(tài)類內(nèi)部,定義外部Activity的弱引用,弱引用在系統(tǒng)資源緊張時(shí)會(huì)被系統(tǒng)優(yōu)先回收。最后,在handleMessage()方法中,通過(guò)WeakReference的get方法獲取外部Activity的引用,如果該弱引用已被回收,則get方法返回null。

struct GcSpec {
  /* If true, only the application heap is threatened. */
  bool isPartial;
  /* If true, the trace is run concurrently with the mutator. */
  bool isConcurrent;
  /* Toggles for the soft reference clearing policy. */
  bool doPreserve;
  /* A name for this garbage collection mode. */
  const char *reason;
};

這段代碼定義在dalvik/vm/alloc/Heap.h中,其中doPreserve為true時(shí),表示在執(zhí)行GC的過(guò)程中,不回收軟引用引用的對(duì)象;為false時(shí),表示在執(zhí)行GC的過(guò)程中,回收軟引用引用的對(duì)象。

最后,使用Handler的過(guò)程中,還需要注意一點(diǎn),在前面的方法列表圖中已經(jīng)提到。為避免Activity調(diào)用onDestroy后,Handler的MessageQueue中仍存在Message,一般會(huì)在onDestroy中調(diào)用removeCallbacksAndMessages()方法。

@Override
protected void onDestroy() {
    super.onDestroy();
    // 清空Message隊(duì)列
    myHandler.removeCallbacksAndMessages(null);
}
public final void removeCallbacksAndMessages(Object token) {
    mQueue.removeCallbacksAndMessages(this, token);
}

removeCallbacksAndMessages()方法會(huì)移除obj為token的由post發(fā)送的callback和send發(fā)送的message,當(dāng)token為null時(shí),會(huì)移除所有callback和message。

1.2 MessageQueue的屬性及操作

MessageQueue,消息隊(duì)列,其屬性與常規(guī)隊(duì)列相似,包括入隊(duì)、出隊(duì)等,這里簡(jiǎn)要介紹一下MessageQueue的實(shí)現(xiàn)。

首先,MessageQueue新建隊(duì)列的工作是通過(guò)在其構(gòu)造器中調(diào)用本地方法nativeInit實(shí)現(xiàn)的。nativeInit會(huì)創(chuàng)建NativeMessageQueue對(duì)象,然后賦值給MessageQueue成員變量mPtr。mPtr是int類型數(shù)據(jù),代表NativeMessageQueue的內(nèi)存指針。

MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    mPtr = nativeInit();
}

其次,Message入隊(duì)的通過(guò)enqueueMessage方法實(shí)現(xiàn)。首先檢查message是否符合入隊(duì)要求(是否正在使用,target handler是否為null),符合要求后通過(guò)設(shè)置prev.next = msg隊(duì)列的指針完成入隊(duì)操作。

boolean enqueueMessage(Message msg, long when);

再次,出隊(duì)是通過(guò)next()方法完成的。涉及到同步、鎖等問(wèn)題,這里不詳細(xì)展開(kāi)了。

再次,刪除元素有兩個(gè)實(shí)現(xiàn)。即分別通過(guò)p.callback == r和p.what == what來(lái)進(jìn)行消息識(shí)別。

void removeMessages(Handler h, int what, Object object);
void removeMessages(Handler h, Runnable r, Object object);

最后,銷毀隊(duì)列和創(chuàng)建隊(duì)列一樣,是通過(guò)本地函數(shù)完成的。傳入的參數(shù)為MessageQueue的內(nèi)存指針。

private native static void nativeDestroy(int ptr);

1.3 Looper的工作原理

Looper是線程通信的關(guān)鍵,正是因?yàn)長(zhǎng)ooper,整個(gè)線程通信機(jī)制才真正實(shí)現(xiàn)“通”。

在應(yīng)用開(kāi)發(fā)過(guò)程中,一般當(dāng)主線程需要傳遞消息給用戶自定義線程時(shí),會(huì)在自定義線程中定義Handler進(jìn)行消息處理,并在Handler實(shí)現(xiàn)的前后分別調(diào)用Looper的prepare()方法和loop()方法。大致實(shí)現(xiàn)如下:

new Thread(new Runnable() {
            
    private Handler mHandler;
            
    @Override
    public void run() {
        Looper.prepare();
        mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                        
            }
        };
        Looper.loop();
    }
});

這里重點(diǎn)說(shuō)明prepare()方法和loop()方法,實(shí)際項(xiàng)目中不建議定義匿名線程。

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

可以看出,prepare方法的重點(diǎn)是sThreadLocal變量,sThreadLocal變量是什么呢?

// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

ThreadLocal實(shí)現(xiàn)了線程本地存儲(chǔ)。簡(jiǎn)單看一下它的類注解文檔,ThreadLocal是一種特殊的全局變量,全局性在于它存儲(chǔ)于自己所在線程相關(guān)的數(shù)據(jù),而其他線程無(wú)法訪問(wèn)。

/**
 * Implements a thread-local storage, that is, a variable for which each thread
 * has its own value. All threads share the same {@code ThreadLocal} object,
 * but each sees a different value when accessing it, and changes made by one
 * thread do not affect the other threads. The implementation supports
 * {@code null} values.
 *
 * @see java.lang.Thread
 * @author Bob Lee
 */
public class ThreadLocal<T> {
}

回到prepare方法中,sThreadLocal添加了一個(gè)針對(duì)當(dāng)前線程的Looper對(duì)象。并且prepare方法只能調(diào)用一次,否則會(huì)拋出運(yùn)行時(shí)異常。

初始化完畢之后,Handler通過(guò)post和send方法如何保證消息投遞到Looper所持有的MessageQueue中呢?其實(shí),MessageQueue是Handler和Looper的橋梁。在前面Handler章節(jié)中提到Handler的初始化方法,Handler的mLooper對(duì)象是通過(guò)Looper的靜態(tài)方法myLooper()獲取的,而myLooper()是通過(guò)調(diào)用sThreadLocal.get()來(lái)得到的,即Handler的mLooper就是當(dāng)前線程的Looper對(duì)象,Handler的mQueue就是mLooper.mQueue。

……
mLooper = Looper.myLooper();
if (mLooper == null) {
   throw new RuntimeException(
        "Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
……
public static Looper myLooper() {
    return sThreadLocal.get();
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容