從源碼解析Handler機制

Handler機制是面試中的常客了,今天和你們一塊兒經過源碼層面來解析一下。
@[toc]java

前提知識點

Handler機制涉及到幾個類: MessageQueue, Looper, Message, ActivityThread。面試

  • ActivityThread: 主線程,開啓loop循環,管理application進程, 調度管理activity, 廣播及其餘操做。
  • Message: handler機制中消息的載體,包含相關描述和數據對象,包含屬性what, obj, arg1,arg2等。
  • MessageQueue: 消息隊列。
  • Looper:循環去MessageQueue中消息。

AppCompatActivity.java-> FragmentActivity.java->SupportActivity.java->Activity.javaapp

在Activity中默認有一個ActivityThread這是一個main thread,其中final Looper mLooper = Looper.myLooper();實例化了一個looper對象。
在ActivityThread.java中的main方法中,有以下代碼ide

public static void main(String[] args) {
           ...省略其餘代碼
        Looper.prepareMainLooper();
          ...省略其餘代碼
        Looper.loop();
    }
Looper.java

public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

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));
    }
  //  Looper構造函數中初始了消息隊列MessageQueue對象
private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

建立looper 對象以前,會判斷 sThreaLocal 中是否已經綁定過 Looper 對象,若是是則拋出異常,確保一個線程中Looper.prepare()只調用一次。
若是在MainActivity中調用,以下代碼,會報錯。
在這裏插入圖片描述
在這裏插入圖片描述函數

經過上述代碼能夠得知,Activity中默認開始了loop()循環,用於獲取handler發送的消息。oop

最簡單的應用

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private final String TAG = "MainActivity";
    public final int MSG_DOWN_FAIL = 1;
    public final int MSG_DOWN_SUCCESS = 2;
    public final int MSG_DOWN_START = 3;

    @BindView(R.id.btn_start_thread)
    Button btnStart;

    @BindView(R.id.tv_status)
    TextView tvShow;

    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_DOWN_START:
                    tvShow.setText("down start");
                    break;
                case MSG_DOWN_SUCCESS:
                    tvShow.setText("down success");
                    break;
                case MSG_DOWN_FAIL:
                    tvShow.setText("down fail");
                    break;
                default:
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        btnStart.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_start_thread:
                new MyThread().start();
                break;
            default:
                break;
        }
    }

    class MyThread extends Thread {

        @Override
        public void run() {
            handler.sendEmptyMessage(MSG_DOWN_START);
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            Message msg = Message.obtain();
            msg.what = MSG_DOWN_SUCCESS;
            handler.sendMessage(msg);
        }
    }
}

sendMessage以後發生了什麼

public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

sendMessage調用了sendMessageDelayed->sendMessageAtTime->enqueueMessage, 最終調用了MessageQueue的enqueueMessage的方法, 並將本身設置爲message的targetpost

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

接下來就到了消息隊列MessageQueue中了,來看一下在這裏插入圖片描述ui

  • 若是message的target爲null,則直接拋出異常
  • 按照message的時間when有序插入到MessageQueue中,因此MessageQueue是一個按時間排序的有序隊列。

怎麼取MessageQueue中的消息

其實這裏就是Looper.loop()方法從消息隊列中不斷循環取消息了。
在這裏插入圖片描述
不斷調用MessageQueue的next()方法取消息,若是message不爲null, 則調用handler的dispatchMessage分發消息。
在這裏插入圖片描述
在這裏插入圖片描述
這個handleMessage方法就是在建立Handler中覆蓋的方法。this

至此 Handler 的發送消息和消息處理流程已經介紹完畢。spa

面試常見問題

1.Looper.loop() 爲何不會阻塞主線程

Android的Ui線程,開啓了一個死循環,可是並無阻塞主線程是爲何呢?
在MessageQueue的next方法中有這樣一行代碼
在這裏插入圖片描述

private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/

nativePollOnce 方法是一個 native 方法,當調用此 native 方法時,主線程會釋放 CPU 資源進入休眠狀態,直到下條消息到達或者有事務發生,經過往 pipe 管道寫端寫入數據來喚醒主線程工做,這裏採用的 epoll 機制。

2.Handler 的 sendMessageDelayed 或者 postDelayed 是如何實現的

messageQueue的enqueueMessage是按照消息的when時間有序加入到隊列的,取的時候也是按照時間進行取的
在這裏插入圖片描述
能夠看到若是當前時間小於msg設置的時間,會計算一個timeout,在timeout到了以後,纔會將UI線程喚醒,交由CPU執行。

總結

1.APP啓動建立主線程的時候會經過ActivityThread執行Looper.prepare(),建立一個looper 對象,在私有的構造方法中又建立了 MessageQueue 做爲此 Looper 對象的成員變量,Looper 對象經過 ThreadLocal 綁定 MainThread 中。2.在建立Handler對象的時候,經過構造函數獲取ThreadLocal 綁定的looper對象,並經過looper獲取消息隊列MessageQueue做爲成員變量3.子線程發送消息時,將msg的target設置爲handler自身,以後調用成員MessageQueue的enqueueMessage將消息按照msg.when時間排序插入到消息隊列4.主線程經過Looper.loop()開啓不阻塞UI線程的死循環,經過綁定的looper對象獲取MessageQueue,調用next()方法不斷獲取msg, 並經過(handler)msg.target.dispatchMessage發送至咱們建立的handler時覆蓋的handleMessage()方法

相關文章
相關標籤/搜索