Android Handler機制完全解析

1.爲什麼要引入Handler機制?

舉個栗子:通過網絡獲取數據然後顯示在TextView中,由於網絡通信屬於耗時操作,所以必須在子線程中完成,但是子線程中是不能更新UI的(特殊情況除外),爲了解決以上問題,Android引入了Handler機制,由Handler來負責與子線程進行通信,從而使子線程與主線程之間建立起協作的橋樑,使Android的UI更新問題得到完美的解決。

注:Android系統是在onResume方法回調之後檢查當前線程的,在此之前是可以在子線程中更新UI的,Google並不建議這麼做,瞭解就好。

2.Handler原理

看圖說話:

Handler機制

先說說幾個重要的類:

  • Message:消息,由MessageQueue統一隊列,然後交由Handler處理。

  • MessageQueue:消息隊列,用來存放Handler發送過來的Message,並且按照先入先出的規則執行。

  • Handler:處理者,負責發送和處理Message。

  • Looper:消息輪詢器,不斷的從MessageQqueue中抽取Message並執行。

1.ActivityThread在主線程中啓動消息循環器Looper

在創建Activity之前,當程序啓動的時候,系統會先加載ActivityThread這個類,在這個類的main函數中,調用Looper.prepareMainLooper()初始化Looper對象並創建消息隊列,然後調用Looper.loop()方法,不斷的輪詢消息隊列中的消息。

package android.app;

public final class ActivityThread {

    public static void main(String[] args) {

        ...

        // 初始化Looper對象並創建消息隊列
        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"));
        }

        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        // 開啓消息輪詢
        Looper.loop();

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

看下Looper.prepareMainLooper()與Looper.loop()方法中發生了什麼:

package android.os;

public final class Looper {

    /**
     * 爲當前線程初始化Looper對象並創建消息隊列
     * 消息循環可以被終止
     */
    public static void prepare() {
        prepare(true);
    }

    /**
     * 爲當前線程初始化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));
    }

    /**
     * 爲UI線程初始化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();
        }
    }

    /**
     * 啓動消息輪詢
     */
    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;

        // 消息循環
        for (;;) {
            // 獲取消息隊列中的消息,可能會阻塞
            Message msg = queue.next();
            if (msg == null) {
                return;
            }

            try {
                // 處理消息
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

            // 回收消息
            msg.recycleUnchecked();
        }
    }

    /**
     * 獲取當前線程的Looper對象
     */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

    /**
     * 獲取當前線程的消息隊列
     */
    public static @NonNull MessageQueue myQueue() {
        return myLooper().mQueue;
    }

    /**
     * Looper構造方法,初始化消息隊列與當前線程
     */
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
}

到這裏,Handler發送和接收消息的準備工作就已經完成了,接下來讓我們來初始化一個Handler試試吧!

2.在主線程中創建Handler

通常我們都會在主線程中創建Handler來接收子線程的消息,看下Handler是如何創建的:

// 定義一個Handler對象,並實現handleMessage方法
Handler handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        // 在此接收子線程發送的消息
    }
};

Handler中的構造方法最終都會調用Handler(Callback callback, boolean async)方法,獲取當前線程的Lopper對象,與之關聯,然後獲取消息隊列。

package android.os;

public class Handler {

    public Handler(Callback callback, boolean async) {

        ...

        // 獲取當前線程的Looper對象
        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;
    }
}

3.使用sendMessage發送消息

package android.os;

public class Handler {

    /**
     * 發送消息
     */
    public final boolean sendMessage(Message msg){
        return sendMessageDelayed(msg, 0);
    }

    /**
     * 延時發送消息
     */
    public final boolean sendMessageDelayed(Message msg, long delayMillis){
        if (delayMillis < 0) {
            delayMillis = 0;
        }

        // 計算消息入列的時間
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

    /**
     * 發送定時消息
     */
    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);
    }

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

看下enqueueMessage方法:

boolean enqueueMessage(Message msg, long when) {

    ...

    synchronized (this) {

        ...

        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;

        // 如果有新消息首次加入隊列或者新消息的延遲時間小於隊列中首個消息的延遲時間,就將新消息放在隊列頭部
        if (p == null || when == 0 || when < p.when) {
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            // 如果隊列不爲空,將新消息按照時間排序放入隊列
            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;
            prev.next = msg;
        }

        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

4.消息隊列將消息分發給Handler處理

發送消息到消息隊列後,Looper就會在消息隊列中按順序取出消息分發給Handler處理,看下dispatchMessage方法,如果callback對象爲null的話,就會回調handleMessage方法,如果不爲空會回調callback的run方法。我們平時使用的sendMessage方法沒有設置callback,所以會回調handleMessage方法,如果使用post(Runnable callback)方法,則會回調callback的run方法。

package android.os;

public class Handler {

    /**
     * 消息分發
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            // 回調callback的run方法
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            // 回調handleMessage方法
            handleMessage(msg);
        }
    }
}

OK,通過對源碼的分析,Handler的原理已經學習完了,接下來讓我們來學習一下如何在子線程中創建Handler。

3.在子線程中創建Handler

通常我們使用Handler都是從子線程向主線程發送消息,如果需要主線程通知子線程做一些耗時邏輯,或者子線程之間進行通信的話,直接在子線程中創建Handler會拋出異常:

Can't create handler inside thread that has not called Looper.prepare()

看下源碼:

package android.os;

public class Handler {

    public Handler(Callback callback, boolean async) {

        ...

        // 獲取當前線程的Looper對象
        mLooper = Looper.myLooper();
        // 如果looper爲空則會拋出異常
        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;
    }
}

由於每個線程都用自己的Looper,這個Looper可以爲null,系統默認在主線程中創建了Looper,但在子線程中需要手動設置,否則就會拋出異常。

代碼實現:

new Thread("子線程") {
    @Override
    public void run() {
        super.run();
        // 初始化Looper對象並創建消息隊列
        Looper.prepare();
        Handler handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                Log.i(TAG, (String) msg.obj + "___" + Thread.currentThread().getName());
            }
        };
        Message message = new Message();
        message.obj = "消息";
        handler.sendMessage(message);
        // 開始輪詢
        // 由於loop方法是死循環,所以要寫在最後
        Looper.loop();

    }
}.start();

還有沒有更優雅的方式呢?答案是肯定的,爲了解決上面的問題,Android系統爲我們提供了HandlerThread類,看下源碼:

package android.os;

public class HandlerThread extends Thread {

    ...

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            // 初始化Looper對象並創建消息隊列
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        // 開啓消息輪詢
        Looper.loop();
        mTid = -1;
    }
}

有沒有很熟悉,HandlerThread類的run方法中做了和ActivityThread類中一樣的處理,這樣就不用再手動初始化Looper了,nice,接下來讓我們用HandlerThread來實現一下:

HandlerThread handlerThread = new HandlerThread("HandlerThread");
handlerThread.start();

Handler handler = new Handler(handlerThread.getLooper()){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        Log.i(TAG, (String) msg.obj + "___" + Thread.currentThread().getName());            }
};

Message message = new Message();
message.obj = "消息";
handler.sendMessage(message);

4.寫在最後

歡迎同學們吐槽評論,如果你覺得本篇博客對你有用,那麼就留個言或者頂一下吧(^-^)

發佈了64 篇原創文章 · 獲贊 275 · 訪問量 27萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章