Android消息機(jī)制

所謂的Android消息機(jī)制其實(shí)就是Handler機(jī)制,其主要的作用是將一個(gè)任務(wù)放到另外一個(gè)線(xiàn)程中去執(zhí)行。一般來(lái)說(shuō)用于網(wǎng)絡(luò)請(qǐng)求之后更新UI的情況較多,但是這并不意味著Handler只能用于這種場(chǎng)景,為什么更新UI的時(shí)候要使用到Handler呢?因?yàn)锳ndroid規(guī)定只能在UI線(xiàn)程中訪問(wèn)UI,否則會(huì)報(bào)錯(cuò)!這個(gè)線(xiàn)程檢查的操作是在ViewRootImpl的checkThread方法中去做的

void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
                "Only the original thread that created a view hierarchy can touch its views.");
    }
}

其中這個(gè)mThread就是UI線(xiàn)程。如果說(shuō)沒(méi)有Handler的話(huà)哪我們?cè)撛趺慈ニ⑿耈I呢?

基本使用

private Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        switch (msg.what) {
            case 0:
            {
                textView.setText("change");
            }
        }
    }
};
...
new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        handler.sendEmptyMessage(0);
    }
}).start();

以上就是我們最常見(jiàn)的Handler最常見(jiàn)的寫(xiě)法,但是這樣寫(xiě)存在一個(gè)很大的問(wèn)題,那就是內(nèi)存泄漏。在Java語(yǔ)言中,非靜態(tài)內(nèi)部類(lèi)會(huì)持有外部類(lèi)的一個(gè)隱試引用,這樣就可能造成外部類(lèi)無(wú)法被垃圾回收。而導(dǎo)致內(nèi)存泄漏。很顯然在這里Handler就是一個(gè)非靜態(tài)內(nèi)部類(lèi),它會(huì)持有Activity的應(yīng)用導(dǎo)致Activity無(wú)法正常釋放。

內(nèi)存泄漏問(wèn)題

上面說(shuō)到Handler默認(rèn)的使用方式歲會(huì)造成內(nèi)存泄露,那么該如何去寫(xiě)呢?正確的寫(xiě)法應(yīng)該是使用靜態(tài)內(nèi)部類(lèi)的形式,但是如果只使用靜態(tài)內(nèi)部類(lèi)的話(huà)handler調(diào)用activity中的方法又成了一個(gè)問(wèn)題,因此使用弱引用來(lái)持有外部activity對(duì)象成為了很好的解決方案。代碼如下:

final MyHandler handler=new MyHandler(this);
…
private static class MyHandler extends Handler {
    //創(chuàng)建一個(gè)弱引用持有外部類(lèi)的對(duì)象
    private final WeakReference<MainActivity> content;

    private MyHandler(MainActivity content) {
        this.content = new WeakReference<MainActivity>(content);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        MainActivity activity= content.get();
        if (activity != null) {
            switch (msg.what) {
                case 0: {
                    activity.notifyUI();
                }
            }
        }
    }
}
…
@Override
protected void onDestroy() {
    super.onDestroy();
    handler.removeCallbacksAndMessages(this);

}

消息機(jī)制中的成員

Hander機(jī)制當(dāng)中的主要成員有Handler、Looper、MessageQueue、Message這四個(gè)成員,當(dāng)然Threadlocal也會(huì)存在一些蹤跡,但是個(gè)人認(rèn)為它并不屬于Handler機(jī)制中的成員!

Handler

從名字的英文含義上你就能大概知道它是消息處理者,負(fù)責(zé)發(fā)送消息和處理消息。

Looper

是一個(gè)查詢(xún)消息的循環(huán)結(jié)構(gòu),負(fù)責(zé)查詢(xún)MessageQueue當(dāng)中的消息

Message

這就是我們的消息,它能攜帶一個(gè)int數(shù)據(jù)和一個(gè)Object數(shù)據(jù)

MessageQueue

它是Message的一個(gè)集合

源碼分析Handler機(jī)制的工作流程


image.png

我們先從Handler發(fā)送消息開(kāi)始,上面Demo中我們使用的是sendEmptyMessage方法,但其實(shí)我們還有一些了其他的send方法和post方法,但是這些方法最終都是要調(diào)用sendMessageAtTime具體代碼如下

/**
 * 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>
 * Time spent in deep sleep will add an additional delay to execution.
 * 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) {
    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);
}

在sendMessageAtTime有一個(gè)點(diǎn)就是mQueue這個(gè)變量,它是一個(gè)MessageQueue的對(duì)象。最終我們調(diào)用了enqueueMessage方法

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

首先msg要綁定Handler,msg.target = this;這個(gè)好理解,一個(gè)Message對(duì)象只能由一個(gè)Handler來(lái)處理。然后

if (mAsynchronous) {
    msg.setAsynchronous(true);
}

如果mAsynchronous為true表示該消息是異步的。最后一步是將消息交給我們的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對(duì)象就添加到MessageQueue當(dāng)中了!到這里我們理解了Handler的send和post方法的實(shí)際作用就是將Message消息添加到MessageQueue之中,但是這一系列的操作之中我們并沒(méi)有看見(jiàn)創(chuàng)建MessageQueue對(duì)象的過(guò)程,似乎在這之前它已經(jīng)創(chuàng)建好了,于是我們想起了之前的一個(gè)變量叫mQueue,它是一個(gè)MessageQueue的對(duì)象

final MessageQueue mQueue;

那他是在哪得到的呢?我們看一下Handler的構(gòu)造方法,以我們最常用的來(lái)看

/**
 * Default constructor associates this handler with the {@link Looper} for the
 * current thread.
 *
 * If this thread does not have a looper, this handler won't be able to receive messages
 * so an exception is thrown.
 */
public Handler() {
    this(null, false);
}

該方法調(diào)用了

/**
 * Use the {@link Looper} for the current thread with the specified callback interface
 * and set whether the handler should be asynchronous.
 *
 * Handlers are synchronous by default unless this constructor is used to make
 * one that is strictly asynchronous.
 *
 * Asynchronous messages represent interrupts or events that do not require global ordering
 * with respect to synchronous messages.  Asynchronous messages are not subject to
 * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
 *
 * @param callback The callback interface in which to handle messages, or null.
 * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
 * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
 *
 * @hide
 */
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());
        }
    }

    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
                    + " that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

好了我們看到了mQueue實(shí)際上是通過(guò)mLooper.mQueue這獲取到的。而mLooper又是通過(guò)

Looper.myLooper();

這個(gè)方法來(lái)獲取到的,分析到這終于又出現(xiàn)了一個(gè)關(guān)鍵字Looper,那我們看一下myLooper這個(gè)方法

/**
 * Return the Looper object associated with the current thread.  Returns
 * null if the calling thread is not associated with a Looper.
 */
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

哈!一臉懵逼。但是大概可以知道Looper對(duì)象被存在了一個(gè)對(duì)象里面,看到了get方法我很容易想到它也許還有set方法,我們先來(lái)看一下這個(gè)sThreadLocal是什么?

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

好了我們知道ThreadLocal這東西了,但是到這線(xiàn)索似乎斷了set方法在哪里?這時(shí)候我忽然想到如果在子線(xiàn)程中使用Handler是一個(gè)什么樣的場(chǎng)景!如果不先調(diào)用Looper.prepare()方法是會(huì)報(bào)錯(cuò)吧!問(wèn)題的關(guān)鍵就在于這,我們看一下代碼

/** Initialize the current thread as a looper.
  * This gives you a chance to create handlers that then reference
  * this looper, before actually starting the loop. Be sure to call
  * {@link #loop()} after calling this method, and end it by calling
  * {@link #quit()}.
  */
public static void prepare() {
    prepare(true);
}

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

sThreadLocal的set方法被我們找到了,并且new了一個(gè)Looper。由此可見(jiàn)我們的Looper也是存在ThreadLocal中的。Looper的構(gòu)造方法如下所示

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

好了mQueue這個(gè)對(duì)象的由來(lái)算是講清楚了,他就是一個(gè)MessageQueue對(duì)象!可以說(shuō)Looper和MessageQueue是一一對(duì)應(yīng)的,一個(gè)Looper對(duì)象中含有一個(gè)MessageQueue。ThreadLocal通過(guò)set方法將Looper存在其中,那么我們具體看一下set方法實(shí)現(xiàn)

/**
 * Sets the current thread's copy of this thread-local variable
 * to the specified value.  Most subclasses will have no need to
 * override this method, relying solely on the {@link #initialValue}
 * method to set the values of thread-locals.
 *
 * @param value the value to be stored in the current thread's copy of
 *        this thread-local.
 */
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

可以看到Looper對(duì)象最終被存儲(chǔ)在了一個(gè)叫ThreadLocalMap的數(shù)據(jù)結(jié)構(gòu)里面,createMap方法用于創(chuàng)建ThreadLcalMap對(duì)象,createMap方法中會(huì)new一個(gè)ThreadLocalMap對(duì)象并將這個(gè)對(duì)象賦給t的threadlocals屬性

/**
 * Create the map associated with a ThreadLocal. Overridden in
 * InheritableThreadLocal.
 *
 * @param t the current thread
 * @param firstValue value for the initial entry of the map
 */
void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

ThreadLocalMap個(gè)人認(rèn)為是一個(gè)hash表,同樣是<key,value>的形式有些和HashMap類(lèi)似,它同樣存在自己的擴(kuò)容機(jī)制,同樣存在自己的hash函數(shù)。到這里我們明白了MessageQueue是存在Looper里面的,而Looper又是存在ThreadLocal里面的,Thread當(dāng)中有且只有一個(gè)ThreadLocal.ThreadLocalMap對(duì)象,因此Thread、Looper和MessageQueue三者形成了一一對(duì)應(yīng)的關(guān)系,然而Handler于他們沒(méi)有一點(diǎn)關(guān)系,Handler只和Message對(duì)象成對(duì)應(yīng)的關(guān)系,所以Thread、Looper、MessageQueue、Handler四者的關(guān)系是一個(gè)線(xiàn)程中只能有一個(gè)Looper和一個(gè)MessageQueue但是可以存在一個(gè)或者多個(gè)Handler。到這里他們之間的關(guān)系我們搞清楚了!另外我們也知道在調(diào)用了MessageQueue的enqueueMessage方法之后我們就把Message對(duì)象添加到了MessageQueue當(dāng)中了,剩下的事情就是Message是如何被處理的,在子線(xiàn)程當(dāng)中使用Handler的時(shí)候除了要先寫(xiě)Looper.prepare()之外,還要寫(xiě)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;

    ...
    boolean slowDeliveryDetected = false;

    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

      ...

        try {
            msg.target.dispatchMessage(msg);
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
       
       ...

        msg.recycleUnchecked();
    }
}

可以看到loop方法中有一個(gè)無(wú)限的for循環(huán),在循環(huán)中通過(guò)queue.next()來(lái)便利Message,然后我們看到了這句代碼
msg.target.dispatchMessage(msg);
這個(gè)target就是我們之前綁定的Handler,也就是說(shuō)我們?cè)谶@里調(diào)用了Handler的dispatchMessage()方法并且將msg作為參數(shù)傳遞了過(guò)去,我們看看dispatchMessage的代碼

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

如果說(shuō)msg的callback不為空調(diào)用handleCallback將消息交給子線(xiàn)程去處理,這種處理方式主要是對(duì)應(yīng)了post(runable)這種形式發(fā)送消息的情況。另外就是調(diào)用handleMessage方法了,好了這個(gè)方法我們?cè)偈煜げ贿^(guò)了,到這里消息從MessageQueue中取出并交由Handler處理的過(guò)程也完成了。最后在loop方法中調(diào)用msg.recycleUnchecked(),到這Handler消息機(jī)制我們就算是分析完成了。那么Handler是這么實(shí)現(xiàn)跨線(xiàn)程通訊的呢?就是通過(guò)方法回調(diào),和接口回調(diào)。

?著作權(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)容