首先來看一下如何創建Handler對象。你可能會覺得挺納悶的,創建Handler有什麼好看的呢,直接new一下不就行了?確實,不過即使只是簡單new一下,還是有不少地方需要注意的,我們嘗試在程序中創建兩個Handler對象,一個在主線程中創建,一個在子線程中創建,代碼如下所示:
- public class MainActivity extends Activity {
- private Handler handler1;
- private Handler handler2;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- handler1 = new Handler();
- new Thread(new Runnable() {
- @Override
- public void run() {
- handler2 = new Handler();
- }
- }).start();
- }
- }
- new Thread(new Runnable() {
- @Override
- public void run() {
- Looper.prepare();
- handler2 = new Handler();
- }
- }).start();
- public Handler() {
- 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 = null;
- }
- public static final Looper myLooper() {
- return (Looper)sThreadLocal.get();
- }
- public static final void prepare() {
- if (sThreadLocal.get() != null) {
- throw new RuntimeException("Only one Looper may be created per thread");
- }
- sThreadLocal.set(new Looper());
- }
可以看到,首先判斷sThreadLocal中是否已經存在Looper了,如果還沒有則創建一個新的Looper設置進去。這樣也就完全解釋了爲什麼我們要先調用Looper.prepare()方法,才能創建Handler對象。同時也可以看出每個線程中最多隻會有一個Looper對象。
咦?不對呀!主線程中的Handler也沒有調用Looper.prepare()方法,爲什麼就沒有崩潰呢?細心的朋友我相信都已經發現了這一點,這是由於在程序啓動的時候,系統已經幫我們自動調用了Looper.prepare()方法。查看ActivityThread中的main()方法,代碼如下所示:
- public static void main(String[] args) {
- SamplingProfilerIntegration.start();
- CloseGuard.setEnabled(false);
- Environment.initForCurrentUser();
- EventLogger.setReporter(new EventLoggingReporter());
- Process.setArgV0("<pre-initialized>");
- Looper.prepareMainLooper();
- ActivityThread thread = new ActivityThread();
- thread.attach(false);
- if (sMainThreadHandler == null) {
- sMainThreadHandler = thread.getHandler();
- }
- AsyncTask.init();
- if (false) {
- Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread"));
- }
- Looper.loop();
- throw new RuntimeException("Main thread loop unexpectedly exited");
- }
- public static final void prepareMainLooper() {
- prepare();
- setMainLooper(myLooper());
- if (Process.supportsProcesses()) {
- myLooper().mQueue.mQuitAllowed = false;
- }
- }
因此我們應用程序的主線程中會始終存在一個Looper對象,從而不需要再手動去調用Looper.prepare()方法了。
這樣基本就將Handler的創建過程完全搞明白了,總結一下就是在主線程中可以直接創建Handler對象,而在子線程中需要先調用Looper.prepare()才能創建Handler對象。
看完了如何創建Handler之後,接下來我們看一下如何發送消息,這個流程相信大家也已經非常熟悉了,new出一個Message對象,然後可以使用setData()方法或arg參數等方式爲消息攜帶一些數據,再借助Handler將消息發送出去就可以了,示例代碼如下:
- new Thread(new Runnable() {
- @Override
- public void run() {
- Message message = new Message();
- message.arg1 = 1;
- Bundle bundle = new Bundle();
- bundle.putString("data", "data");
- message.setData(bundle);
- handler.sendMessage(message);
- }
- }).start();
可是這裏Handler到底是把Message發送到哪裏去了呢?爲什麼之後又可以在Handler的handleMessage()方法中重新得到這條Message呢?看來又需要通過閱讀源碼才能解除我們心中的疑惑了,Handler中提供了很多個發送消息的方法,其中除了sendMessageAtFrontOfQueue()方法之外,其它的發送消息方法最終都會輾轉調用到sendMessageAtTime()方法中,這個方法的源碼如下所示:
- public boolean sendMessageAtTime(Message msg, long uptimeMillis)
- {
- boolean sent = false;
- MessageQueue queue = mQueue;
- if (queue != null) {
- msg.target = this;
- sent = queue.enqueueMessage(msg, uptimeMillis);
- }
- else {
- RuntimeException e = new RuntimeException(
- this + " sendMessageAtTime() called with no mQueue");
- Log.w("Looper", e.getMessage(), e);
- }
- return sent;
- }
那麼enqueueMessage()方法毫無疑問就是入隊的方法了,我們來看下這個方法的源碼:
- final boolean enqueueMessage(Message msg, long when) {
- if (msg.when != 0) {
- throw new AndroidRuntimeException(msg + " This message is already in use.");
- }
- if (msg.target == null && !mQuitAllowed) {
- throw new RuntimeException("Main thread not allowed to quit");
- }
- synchronized (this) {
- if (mQuiting) {
- RuntimeException e = new RuntimeException(msg.target + " sending message to a Handler on a dead thread");
- Log.w("MessageQueue", e.getMessage(), e);
- return false;
- } else if (msg.target == null) {
- mQuiting = true;
- }
- msg.when = when;
- Message p = mMessages;
- if (p == null || when == 0 || when < p.when) {
- msg.next = p;
- mMessages = msg;
- this.notify();
- } else {
- Message prev = null;
- while (p != null && p.when <= when) {
- prev = p;
- p = p.next;
- }
- msg.next = prev.next;
- prev.next = msg;
- this.notify();
- }
- }
- return true;
- }
現在入隊操作我們就已經看明白了,那出隊操作是在哪裏進行的呢?這個就需要看一看Looper.loop()方法的源碼了,如下所示:
- public static final void loop() {
- Looper me = myLooper();
- MessageQueue queue = me.mQueue;
- while (true) {
- Message msg = queue.next(); // might block
- if (msg != null) {
- if (msg.target == null) {
- return;
- }
- if (me.mLogging!= null) me.mLogging.println(
- ">>>>> Dispatching to " + msg.target + " "
- + msg.callback + ": " + msg.what
- );
- msg.target.dispatchMessage(msg);
- if (me.mLogging!= null) me.mLogging.println(
- "<<<<< Finished to " + msg.target + " "
- + msg.callback);
- msg.recycle();
- }
- }
- }
- public void dispatchMessage(Message msg) {
- if (msg.callback != null) {
- handleCallback(msg);
- } else {
- if (mCallback != null) {
- if (mCallback.handleMessage(msg)) {
- return;
- }
- }
- handleMessage(msg);
- }
- }
因此,一個最標準的異步消息處理線程的寫法應該是這樣:
- class LooperThread extends Thread {
- public Handler mHandler;
- public void run() {
- Looper.prepare();
- mHandler = new Handler() {
- public void handleMessage(Message msg) {
- // process incoming messages here
- }
- };
- Looper.loop();
- }
- }
那麼我們還是要來繼續分析一下,爲什麼使用異步消息處理的方式就可以對UI進行操作了呢?這是由於Handler總是依附於創建時所在的線程,比如我們的Handler是在主線程中創建的,而在子線程中又無法直接對UI進行操作,於是我們就通過一系列的發送消息、入隊、出隊等環節,最後調用到了Handler的handleMessage()方法中,這時的handleMessage()方法已經是在主線程中運行的,因而我們當然可以在這裏進行UI操作了。整個異步消息處理流程的示意圖如下圖所示:
另外除了發送消息之外,我們還有以下幾種方法可以在子線程中進行UI操作:
1. Handler的post()方法
2. View的post()方法
3. Activity的runOnUiThread()方法
我們先來看下Handler中的post()方法,代碼如下所示:
- public final boolean post(Runnable r)
- {
- return sendMessageDelayed(getPostMessage(r), 0);
- }
- private final Message getPostMessage(Runnable r) {
- Message m = Message.obtain();
- m.callback = r;
- return m;
- }
- private final void handleCallback(Message message) {
- message.callback.run();
- }
- public class MainActivity extends Activity {
- private Handler handler;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- handler = new Handler();
- new Thread(new Runnable() {
- @Override
- public void run() {
- handler.post(new Runnable() {
- @Override
- public void run() {
- // 在這裏進行UI操作
- }
- });
- }
- }).start();
- }
- }
然後再來看一下View中的post()方法,代碼如下所示:
- public boolean post(Runnable action) {
- Handler handler;
- if (mAttachInfo != null) {
- handler = mAttachInfo.mHandler;
- } else {
- ViewRoot.getRunQueue().post(action);
- return true;
- }
- return handler.post(action);
- }
最後再來看一下Activity中的runOnUiThread()方法,代碼如下所示:
- public final void runOnUiThread(Runnable action) {
- if (Thread.currentThread() != mUiThread) {
- mHandler.post(action);
- } else {
- action.run();
- }
- }
通過以上所有源碼的分析,我們已經發現了,不管是使用哪種方法在子線程中更新UI,其實背後的原理都是相同的,必須都要藉助異步消息處理的機制來實現,而我們又已經將這個機制的流程完全搞明白了,真是一件一本萬利的事情啊。