Android Handler的原理

簡介

在 Android 中,只有主線程才能操做 UI,可是主線程不能進行耗時操做,不然會阻塞線程,產生 ANR 異常,因此經常把耗時操做放到其它子線程進行。若是在子線程中須要更新 UI,通常是經過 Handler 發送消息,主線程接受消息而且進行相應的邏輯處理。除了直接使用 Handler,還能夠經過 View 的 post 方法以及 Activity 的 runOnUiThread 方法來更新 UI,它們內部也是利用了 Handler 。在上一篇文章 Android AsyncTask源碼分析 中也講到,其內部使用了 Handler 把任務的處理結果傳回 UI 線程。segmentfault

本文深刻分析 Android 的消息處理機制,瞭解 Handler 的工做原理。async

Handler

先經過一個例子看一下 Handler 的用法。ide

public class MainActivity extends AppCompatActivity {
    private static final int MESSAGE_TEXT_VIEW = 0;
    
    private TextView mTextView;
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MESSAGE_TEXT_VIEW:
                    mTextView.setText("UI成功更新");
                default:
                    super.handleMessage(msg);
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        mTextView = (TextView) findViewById(R.id.text_view);

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                mHandler.obtainMessage(MESSAGE_TEXT_VIEW).sendToTarget();
            }
        }).start();

    }
}

上面的代碼先是新建了一個 Handler的實例,而且重寫了 handleMessage 方法,在這個方法裏,即是根據接受到的消息的類型進行相應的 UI 更新。那麼看一下 Handler的構造方法的源碼:oop

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 that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

在構造方法中,經過調用 Looper.myLooper() 得到了 Looper 對象。若是 mLooper 爲空,那麼會拋出異常:"Can't create handler inside thread that has not called Looper.prepare()",意思是:不能在未調用 Looper.prepare() 的線程建立 handler。上面的例子並無調用這個方法,可是卻沒有拋出異常。實際上是由於主線程在啓動的時候已經幫咱們調用過了,因此能夠直接建立 Handler 。若是是在其它子線程,直接建立 Handler 是會致使應用崩潰的。源碼分析

在獲得 Handler 以後,又獲取了它的內部變量 mQueue, 這是 MessageQueue 對象,也就是消息隊列,用於保存 Handler 發送的消息。post

到此,Android 消息機制的三個重要角色所有出現了,分別是 HandlerLooper 以及 MessageQueue。 通常在代碼咱們接觸比較多的是 Handler ,但 LooperMessageQueue 倒是 Handler 運行時不可或缺的。ui

Looper

上一節分析了 Handler 的構造,其中調用了 Looper.myLooper() 方法,下面是它的源碼:this

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

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

這個方法的代碼很簡單,就是從 sThreadLocal 中獲取 Looper 對象。sThreadLocalThreadLocal 對象,這說明 Looper 是線程獨立的。spa

Handler 的構造中,從拋出的異常可知,每一個線程想要得到 Looper 須要調用 prepare() 方法,繼續看它的代碼:線程

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 設置一個 Looper。不過須要注意的是若是 sThreadLocal 已經設置過了,那麼會拋出異常,也就是說一個線程只會有一個 Looper。建立 Looper 的時候,內部會建立一個消息隊列:

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

如今的問題是, Looper看上去很重要的樣子,它究竟是幹嗎的?
回答: Looper 開啓消息循環系統,不斷從消息隊列 MessageQueue 取出消息交由 Handler 處理。

爲何這樣說呢,看一下 Looperloop方法:

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;

    // Make sure the identity of this thread is that of the local process,
    // and keep track of what that identity token actually is.
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();
    
    //無限循環
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        // This must be in a local variable, in case a UI event sets the logger
        Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }

        msg.target.dispatchMessage(msg);

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        // Make sure that during the course of dispatching the
        // identity of the thread wasn't corrupted.
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            Log.wtf(TAG, "Thread identity changed from 0x"+ Long.toHexString(ident) + " to 0x"+ Long.toHexString(newIdent) + " while dispatching to "+ msg.target.getClass().getName() + " "+ msg.callback + " what=" + msg.what);
        }

        msg.recycleUnchecked();
    }
}

這個方法的代碼有點長,不去追究細節,只看總體邏輯。能夠看出,在這個方法內部有個死循環,裏面經過 MessageQueuenext() 方法獲取下一條消息,沒有獲取到會阻塞。若是成功獲取新消息,便調用 msg.target.dispatchMessage(msg)msg.targetHandler 對象(下一節會看到),dispatchMessage 則是分發消息(此時已經運行在 UI 線程),下面分析消息的發送及處理流程。

消息發送與處理

在子線程發送消息時,是調用一系列的 sendMessagesendMessageDelayed 以及 sendMessageAtTime 等方法,最終會展轉調用 sendMessageAtTime(Message msg, long 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);
}

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

這個方法就是調用 enqueueMessage 在消息隊列中插入一條消息,在 enqueueMessage總中,會把 msg.target 設置爲當前的 Handler 對象。

消息插入消息隊列後, Looper 負責從隊列中取出,而後調用 HandlerdispatchMessage 方法。接下來看看這個方法是怎麼處理消息的:

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

首先,若是消息的 callback 不是空,便調用 handleCallback 處理。不然判斷 HandlermCallback 是否爲空,不爲空則調用它的 handleMessage方法。若是仍然爲空,才調用 Handler 自身的 handleMessage,也就是咱們建立 Handler 時重寫的方法。

若是發送消息時調用 Handlerpost(Runnable r)方法,會把 Runnable封裝到消息對象的 callback,而後調用 sendMessageDelayed,相關代碼以下:

public final boolean post(Runnable r)
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

此時在 dispatchMessage中便會調用 handleCallback進行處理:

private static void handleCallback(Message message) {
    message.callback.run();
}

能夠看到是直接調用了 run 方法處理消息。

若是在建立 Handler時,直接提供一個 Callback 對象,消息就交給這個對象的 handleMessage 方法處理。CallbackHandler 內部的一個接口:

public interface Callback {
    public boolean handleMessage(Message msg);
}

以上即是消息發送與處理的流程,發送時是在子線程,但處理時 dispatchMessage 方法運行在主線程。

總結

至此,Android消息處理機制的原理就分析結束了。如今能夠知道,消息處理是經過 HandlerLooper 以及 MessageQueue共同完成。 Handler 負責發送以及處理消息,Looper 建立消息隊列並不斷從隊列中取出消息交給 HandlerMessageQueue 則用於保存消息。

若是個人文章對您有幫助,不妨點個贊鼓勵一下(^_^)

相關文章
相關標籤/搜索