【干貨】快速理解Android 中的 Handler機制

本文目標(biāo)归斤,采用簡單易懂的方式理解Android中的Handler機制枝冀,以及自己動手去模擬實現(xiàn)Android系統(tǒng)的Handler機制舞丛;

Handler 是為了解決Android中子線程與主線程之間通信的相關(guān)問題而存在的,主要涉及到:Handler果漾、Message球切、MessageQueue、Looper這幾個系統(tǒng)類绒障,它們存放在android.os包下吨凑;
簡單介紹下它們的作用:

  • Handler :子線程向主線程發(fā)送消息、主線程處理接收到的消息户辱;
  • Message:消息載體鸵钝,如果傳輸數(shù)據(jù)簡單可以直接使用arg1、arg2這兩個整型數(shù)據(jù)庐镐,如果需要傳復(fù)雜的消息恩商,使用obj傳輸對象數(shù)據(jù);
  • MessageQueue:消息隊列必逆,用來存儲管理當(dāng)前的線程中的所有Message消息怠堪;
  • Looper :消息輪詢器,不斷的從消息隊列MessageQueue中取出Message進行分發(fā)名眉;

一粟矿、基本使用方式

講解之前我們先看下使用案例,如下:
功能描述:點擊按鈕開始倒計時损拢,將當(dāng)前剩余的時間更新到UI控件TextView中陌粹;


Handler機制講解效果圖.gif
public class HandlerActivity extends AppCompatActivity {
    private static final int UPDATE = 0x1;
    public Button btnStartCountDown;
    public TextView tvCountDown;

    //默認(rèn)是在主線程中執(zhí)行
    private final MyHandler mHandler = new MyHandler(this);

    //繼承實現(xiàn)自己的Handler,處理子線程與主線程的通訊交互
    static class MyHandler extends Handler {
        //使用弱引用福压,防止內(nèi)存泄露
        private final WeakReference<HandlerActivity> mActivity;

        private MyHandler(HandlerActivity mActivity) {
            this.mActivity = new WeakReference<>(mActivity);
        }

        //重寫handleMessage進行處理接收到的Message
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //獲取引用的UI主線程Activity申屹,用來獲取UI線程的控件等
            HandlerActivity activity = mActivity.get();
            if (activity != null) {
                //分發(fā)處理消息
                switch (msg.what) {
                    case UPDATE:
                        activity.tvCountDown.setText("還有" + String.valueOf(msg.arg1) + "秒");
                        break;
                }
            }
        }
    }

    //模擬子線程進行耗時任務(wù)
    private final Runnable mRunnable = new Runnable() {
        @Override
        public void run() {
            //這里做的是一個倒計時定時一秒發(fā)送一次數(shù)據(jù)
            for (int i = 60; i > 0; i--) {
                //構(gòu)建屬于子線程的Message
                Message msg = new Message();
                msg.what = UPDATE;
                msg.arg1 = i;

                //通過主線程中的Handler實例進行消息發(fā)送绘证,將子線程中的消息發(fā)送到主線程中
                mHandler.sendMessage(msg);
                try {
                    //休眠1秒
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                //打印Log
                Log.i("TAG", "還有 " + i + " 秒");
            }
        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);
        btnStartCountDown = findViewById(R.id.btn_start_count_down);
        tvCountDown = findViewById(R.id.tv_count_down);
        btnStartCountDown.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //開啟子線程運行耗時操作
                new Thread(mRunnable).start();
            }
        });
    }
}

可以看出Handler的使用很簡單,但是他卻可以幫助我們有效的解決子線程與主線程間通信的問題哗讥,接下來講解下原理;

二胞枕、源碼分析

快速理解Handler機制.png

對照上面的流程圖杆煞,我們進行源碼片段分析,關(guān)鍵的幾個地方提取出來腐泻,如下:

public final class Looper {
    // 線程本地存儲,通過它就可以在指定的線程中存儲數(shù)據(jù)决乎,然后只有在這個指定的線程中才能夠訪問得到之前存儲的數(shù)據(jù)。
    // 但是對于其他線程來說派桩,是獲取不到保存在另外線程中的數(shù)據(jù)的构诚。
    // 一般來說,當(dāng)某些數(shù)據(jù)是以線程為作用域并且不同的線程對應(yīng)著不同的數(shù)據(jù)副本的時候铆惑,就可以考慮使用ThreadLocal了
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    //消息隊列
    final MessageQueue mQueue;
        private Looper() {
        //初始化消息隊列
        mQueue = new MessageQueue();
    }

    /**
     * 在當(dāng)前的線程中準(zhǔn)備一個消息輪詢器
     */
    public static void prepare() {
        //一個線程只能對應(yīng)一個輪詢器
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //向ThreadLocal添加一個輪詢器
        sThreadLocal.set(new Looper());
    }

    /**
     * 返回當(dāng)前線程對應(yīng)的輪詢器
     *
     * @return Looper
     */
    public static Looper myLooper() {
        return sThreadLocal.get();
    }

    /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        //取出當(dāng)前的線程的Looper對象
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //取出Looper對應(yīng)的MessageUeue
        final MessageQueue queue = me.mQueue;
        //……
        for (;;) {
            //取出MessageQueue中的棧頂?shù)南?            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            //……
            try {
                // 調(diào)用Message對應(yīng)的Handler中的dispatchMessage(Message msg)進行消息的分發(fā)處理
                msg.target.dispatchMessage(msg);
               //……
            } finally {
             //……
            }               
            //釋放資源
            msg.recycleUnchecked();
        }
    }
}
public class Handler {

    //持有當(dāng)前線程的Looper
    final Looper mLooper;
    //持有Looper中的MessageQueue消息隊列范嘱,sendMessage需要向隊列插入消息
    final MessageQueue mQueue;

    public Handler() {
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                    "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
    }

    /**
     * 子類需要重寫這個方法寫入自己的處理邏輯
     *
     * @param msg Message消息
     */
    public void handleMessage(Message msg) {
    }

    /**
     * 調(diào)度消息
     *
     * @param msg Message消息
     */
    public void dispatchMessage(Message msg) {
        //這里為了簡單說明原理與原碼有些不一樣,刪減了一些判斷邏輯
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            handleMessage(msg);
        }
    }

    /**
     * 處理回調(diào)
     *
     * @param message Message消息
     */
    private static void handleCallback(Message message) {
        //取出Message中 Runnable 進行運行
        message.callback.run();
    }


    /**
     * 發(fā)送一條Message消息
     *
     * @param msg 要發(fā)送的Message消息
     * @return 是否發(fā)送成功
     */
    public final boolean sendMessage(Message msg) {
        return sendMessageDelayed(msg, 0);
    }

    /**
     * 發(fā)送延遲消息
     *
     * @param msg         要發(fā)送的Message消息
     * @param delayMillis 要延遲的時間
     * @return 是否發(fā)送成功
     */
    public final boolean sendMessageDelayed(Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        //這里很巧妙的將消息的時間點進行延長员魏,從而達(dá)到了延遲發(fā)送的效果
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

    /**
     * 發(fā)送消息所對應(yīng)的時間
     *
     * @param msg          要發(fā)送的Message消息
     * @param uptimeMillis 對應(yīng)的消息所在的時間點
     * @return
     */
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        //獲取當(dāng)前線程對應(yīng)的Looper中的MessageQueue
        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);
    }

    /**
     * 消息壓入消息隊列操作
     *
     * @param queue        當(dāng)前Handler對應(yīng)的消息隊列
     * @param msg          要發(fā)送的Message消息
     * @param uptimeMillis 壓入隊列的時間丑蛤,系統(tǒng)的實習(xí)會對這個時間進行排序,從而保證消息的有序出棧 {@link MessageQueue#next()}
     * @return 是否壓入隊列成功
     */
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        return queue.enqueueMessage(msg, uptimeMillis);
    }
}
public class Message implements Parcelable {
    //用戶定義的消息代碼撕阎,以便收件人可以識別此消息的內(nèi)容
    public int what;
    //如果只是傳遞整形數(shù)據(jù)可以使用 arg1受裹、arg2
    public int arg1;
    public int arg2;

    //如果傳遞復(fù)雜數(shù)據(jù)可以使用這個
    public Object obj;

    //標(biāo)記當(dāng)前的Message要作用在那個Handler中
    Handler target;

    //當(dāng)前的Message要執(zhí)行的子線程
    Runnable callback;

    // sometimes we store linked lists of these things
    Message next;

    private static final Object sPoolSync = new Object();
    private static Message sPool;

    public Message() {
    }


    /**
     * 這里是性能優(yōu)化,從線程池獲取一個對象虏束,避免重新創(chuàng)建對象棉饶,本案例中沒有使用到這個特性
     *
     * @return Message
     */
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                return m;
            }
        }
        return new Message();
    }

    //////////////////////////////////////下面是實現(xiàn)Parcelable固定的寫法與邏輯關(guān)系不大/////////////////////////////////////////////
    public static final Parcelable.Creator<Message> CREATOR
            = new Parcelable.Creator<Message>() {
        public Message createFromParcel(Parcel source) {
            Message msg = Message.obtain();
            msg.readFromParcel(source);
            return msg;
        }

        public Message[] newArray(int size) {
            return new Message[size];
        }
    };

    public int describeContents() {
        return 0;
    }

    public void writeToParcel(Parcel dest, int flags) {
        if (callback != null) {
            throw new RuntimeException(
                    "Can't marshal callbacks across processes.");
        }
        dest.writeInt(what);
        dest.writeInt(arg1);
        dest.writeInt(arg2);
        if (obj != null) {
            try {
                Parcelable p = (Parcelable) obj;
                dest.writeInt(1);
                dest.writeParcelable(p, flags);
            } catch (ClassCastException e) {
                throw new RuntimeException(
                        "Can't marshal non-Parcelable objects across processes.");
            }
        } else {
            dest.writeInt(0);
        }
    }

    private void readFromParcel(Parcel source) {
        what = source.readInt();
        arg1 = source.readInt();
        arg2 = source.readInt();
        if (source.readInt() != 0) {
            obj = source.readParcelable(getClass().getClassLoader());
        }
    }
}
public final class MessageQueue {

    //獲取消息隊列的下一個數(shù)據(jù)
    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;
        }
    }
    
    //向隊列中添加一個消息
    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) {
                //這里調(diào)用的jni進行了Message存儲
                nativeWake(mPtr);
            }
        }
        return true;
    }
}

三、自定義實現(xiàn)Handler機制

通過上面的源碼分析我們不難看出Handler镇匀、Message照藻、MessageQueue、Looper之間的關(guān)系坑律,畫一個類圖解釋一下岩梳,可以看到下圖中所描述的,這四個類相互持有晃择,并且是一一對應(yīng)的關(guān)系冀值;

快速理解Handler機制類圖.png

經(jīng)過上面的一系列分析我們來自己手動實現(xiàn)Handler機制。

public class Looper {
    //靜態(tài)常量宫屠,整個APP共享這一個
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<>();

    //跟當(dāng)前的線程持有的Looper綁定的消息隊列
    final MessageQueue mQueue;

    private Looper() {
        //初始化消息隊列
        mQueue = new MessageQueue();
    }

    /**
     * 在當(dāng)前的線程中準(zhǔn)備一個消息輪詢器
     */
    public static void prepare() {
        //一個線程只能對應(yīng)一個輪詢器
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //向ThreadLocal添加一個輪詢器
        sThreadLocal.set(new Looper());
    }

    /**
     * 返回當(dāng)前線程對應(yīng)的輪詢器
     *
     * @return Looper
     */
    public static Looper myLooper() {
        return sThreadLocal.get();
    }

    /**
     * 開啟輪詢
     */
    public static void loop() {
        //獲取當(dāng)前線程的輪詢器
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //獲取輪詢器對應(yīng)的消息隊列
        final MessageQueue queue = me.mQueue;

        //永真循環(huán)不斷地去取消息隊列中的消息
        for (; ; ) {
            //取出消息隊列中Message消息
            Message msg = queue.next(); // might block

            // 由于我們這里采用的是java幫我們實現(xiàn)的BlockingQueue列疗,
            // 這里跟系統(tǒng)的實現(xiàn)判斷有些不一樣
            if (msg != null) {
                //將取出消息給發(fā)出當(dāng)前消息的Handler的dispatchMessage進行消息的調(diào)度
                msg.target.dispatchMessage(msg);
            }
        }
    }
}
public class Handler {

    //持有當(dāng)前線程的Looper
    final Looper mLooper;
    //持有Looper中的MessageQueue消息隊列,sendMessage需要向隊列插入消息
    final MessageQueue mQueue;

    public Handler() {
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                    "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
    }

    /**
     * 子類需要重寫這個方法寫入自己的處理邏輯
     *
     * @param msg Message消息
     */
    public void handleMessage(Message msg) {
    }

    /**
     * 調(diào)度消息
     *
     * @param msg Message消息
     */
    public void dispatchMessage(Message msg) {
        //這里為了簡單說明原理與原碼有些不一樣浪蹂,刪減了一些判斷邏輯
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            handleMessage(msg);
        }
    }

    /**
     * 處理回調(diào)
     *
     * @param message Message消息
     */
    private static void handleCallback(Message message) {
        //取出Message中 Runnable 進行運行
        message.callback.run();
    }


    /**
     * 發(fā)送一條Message消息
     *
     * @param msg 要發(fā)送的Message消息
     * @return 是否發(fā)送成功
     */
    public final boolean sendMessage(Message msg) {
        return sendMessageDelayed(msg, 0);
    }

    /**
     * 發(fā)送延遲消息
     *
     * @param msg         要發(fā)送的Message消息
     * @param delayMillis 要延遲的時間
     * @return 是否發(fā)送成功
     */
    public final boolean sendMessageDelayed(Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        //這里很巧妙的將消息的時間點進行延長抵栈,從而達(dá)到了延遲發(fā)送的效果
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

    /**
     * 發(fā)送消息所對應(yīng)的時間
     *
     * @param msg          要發(fā)送的Message消息
     * @param uptimeMillis 對應(yīng)的消息所在的時間點
     * @return
     */
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        //獲取當(dāng)前線程對應(yīng)的Looper中的MessageQueue
        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);
    }

    /**
     * 消息壓入消息隊列操作
     *
     * @param queue        當(dāng)前Handler對應(yīng)的消息隊列
     * @param msg          要發(fā)送的Message消息
     * @param uptimeMillis 壓入隊列的時間告材,系統(tǒng)的實習(xí)會對這個時間進行排序,從而保證消息的有序出棧 {@link MessageQueue#next()}
     * @return 是否壓入隊列成功
     */
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        return queue.enqueueMessage(msg, uptimeMillis);
    }
}
public class Message implements Parcelable {
    //用戶定義的消息代碼古劲,以便收件人可以識別此消息的內(nèi)容
    public int what;
    //如果只是傳遞整形數(shù)據(jù)可以使用 arg1斥赋、arg2
    public int arg1;
    public int arg2;

    //如果傳遞復(fù)雜數(shù)據(jù)可以使用這個
    public Object obj;

    //標(biāo)記當(dāng)前的Message要作用在那個Handler中
    Handler target;

    //當(dāng)前的Message要執(zhí)行的子線程
    Runnable callback;

    // sometimes we store linked lists of these things
    Message next;

    private static final Object sPoolSync = new Object();
    private static Message sPool;

    public Message() {
    }


    /**
     * 這里是性能優(yōu)化,從線程池獲取一個對象产艾,避免重新創(chuàng)建對象疤剑,本案例中沒有使用到這個特性
     *
     * @return Message
     */
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                return m;
            }
        }
        return new Message();
    }

    //////////////////////////////////////下面是實現(xiàn)Parcelable固定的寫法與邏輯關(guān)系不大/////////////////////////////////////////////
    public static final Parcelable.Creator<Message> CREATOR
            = new Parcelable.Creator<Message>() {
        public Message createFromParcel(Parcel source) {
            Message msg = Message.obtain();
            msg.readFromParcel(source);
            return msg;
        }

        public Message[] newArray(int size) {
            return new Message[size];
        }
    };

    public int describeContents() {
        return 0;
    }

    public void writeToParcel(Parcel dest, int flags) {
        if (callback != null) {
            throw new RuntimeException(
                    "Can't marshal callbacks across processes.");
        }
        dest.writeInt(what);
        dest.writeInt(arg1);
        dest.writeInt(arg2);
        if (obj != null) {
            try {
                Parcelable p = (Parcelable) obj;
                dest.writeInt(1);
                dest.writeParcelable(p, flags);
            } catch (ClassCastException e) {
                throw new RuntimeException(
                        "Can't marshal non-Parcelable objects across processes.");
            }
        } else {
            dest.writeInt(0);
        }
    }

    private void readFromParcel(Parcel source) {
        what = source.readInt();
        arg1 = source.readInt();
        arg2 = source.readInt();
        if (source.readInt() != 0) {
            obj = source.readParcelable(getClass().getClassLoader());
        }
    }
}
public class MessageQueue {
    private static final int MAX_QUEUE_SIZE = 50;

    // 這里使用BlockingQueue集合進行模擬Native層的隊列
    // 后續(xù)在進行講解Android中的C++如何實現(xiàn)的Message隊列操作
    final BlockingQueue<Message> mMessages;

    MessageQueue() {
        //創(chuàng)建固定大小的消息隊列
        mMessages = new ArrayBlockingQueue<>(MAX_QUEUE_SIZE);
    }

    /**
     * 取出隊列中的下一個消息
     *
     * @return Message消息
     */
    Message next() {
        Message msg = null;
        try {
            //從隊列中取出頭部的消息,并從隊列中移除闷堡,通知 {@link BlockingQueue#put()} 可以入棧
            msg = mMessages.take();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //將取出的消息返回
        return msg;
    }

    /**
     * 消息壓入隊列操作
     *
     * @param msg  要操作的消息
     * @param when 壓入的時間
     * @return 是否要入成功
     */
    boolean enqueueMessage(Message msg, long when) {
        try {
            //壓入消息隊列隘膘,如果消息隊列處于飽和狀態(tài),這里則會出現(xiàn) block,直到 有調(diào)用 {@link BlockingQueue#take()}
            mMessages.put(msg);

            return true;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return false;
    }
}

改寫一下測試代碼杠览,測試我們自己定義的Handler機制是否能夠?qū)崿F(xiàn)子線程與主線程的通信

public class CustomerHandlerActivity extends AppCompatActivity {
    private static final int UPDATE = 0x1;

    //默認(rèn)是在主線程中執(zhí)行
    private MyHandler mHandler = null;

    //繼承實現(xiàn)自己的Handler弯菊,處理子線程與主線程的通訊交互
    static class MyHandler extends Handler {
        //使用弱引用,防止內(nèi)存泄露
        private final WeakReference<CustomerHandlerActivity> mActivity;

        private MyHandler(CustomerHandlerActivity mActivity) {
            this.mActivity = new WeakReference<>(mActivity);
        }

        //重寫handleMessage進行處理接收到的Message
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //獲取引用的UI主線程Activity踱阿,用來獲取UI線程的控件等
            CustomerHandlerActivity activity = mActivity.get();
            if (activity != null) {
                //分發(fā)處理消息
                switch (msg.what) {
                    case UPDATE:
                        //打印Log
                        Log.i("TAG", "還有 " + String.valueOf(msg.arg1) + " 秒");
                        break;
                }
            }
        }
    }

    //模擬子線程進行耗時任務(wù)
    private final Runnable mRunnable = new Runnable() {
        @Override
        public void run() {
            //這里做的是一個倒計時定時一秒發(fā)送一次數(shù)據(jù)
            for (int i = 60; i > 0; i--) {
                //構(gòu)建屬于子線程的Message
                Message msg = new Message();
                msg.what = UPDATE;
                msg.arg1 = i;

                //通過主線程中的Handler實例進行消息發(fā)送管钳,將子線程中的消息發(fā)送到主線程中
                mHandler.sendMessage(msg);
                try {
                    //休眠1秒
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);

        //在當(dāng)前的線程中準(zhǔn)備一個消息輪詢器
        Looper.prepare();

        //創(chuàng)建主線程的Handler
        mHandler = new MyHandler(this);

        //開啟子線程運行耗時操作
        new Thread(mRunnable).start();

        //這里開啟消息輪詢器會造成UI的繪制阻塞
        Looper.loop();
    }
}
image.png

可以通過日志查看我們自己定義的Handler機制是可以實現(xiàn)線程之間通訊的。

至于我們自己編寫的Looper.loop();造成的阻塞問題我們后面在研究扫茅;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蹋嵌,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子葫隙,更是在濱河造成了極大的恐慌栽烂,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件恋脚,死亡現(xiàn)場離奇詭異腺办,居然都是意外死亡,警方通過查閱死者的電腦和手機糟描,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進店門怀喉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人船响,你說我怎么就攤上這事躬拢。” “怎么了见间?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵聊闯,是天一觀的道長。 經(jīng)常有香客問我米诉,道長菱蔬,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮拴泌,結(jié)果婚禮上魏身,老公的妹妹穿的比我還像新娘。我一直安慰自己蚪腐,他們只是感情好箭昵,可當(dāng)我...
    茶點故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著削茁,像睡著了一般宙枷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上茧跋,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天,我揣著相機與錄音卓囚,去河邊找鬼瘾杭。 笑死,一個胖子當(dāng)著我的面吹牛哪亿,可吹牛的內(nèi)容都是我干的粥烁。 我是一名探鬼主播,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼蝇棉,長吁一口氣:“原來是場噩夢啊……” “哼讨阻!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起篡殷,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤钝吮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后板辽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體奇瘦,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡劲弦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年耳标,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片邑跪。...
    茶點故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡次坡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出画畅,到底是詐尸還是另有隱情砸琅,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布夜赵,位于F島的核電站明棍,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏寇僧。R本人自食惡果不足惜摊腋,卻給世界環(huán)境...
    茶點故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一沸版、第九天 我趴在偏房一處隱蔽的房頂上張望尤泽。 院中可真熱鬧尚氛,春花似錦、人聲如沸土浸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至岛啸,卻和暖如春钓觉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背坚踩。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工荡灾, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瞬铸。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓批幌,卻偏偏與公主長得像,于是被迫代替她去往敵國和親嗓节。 傳聞我的和親對象是個殘疾皇子荧缘,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,472評論 2 348

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