對Handler的理解學習

一、Handler的作用

  • 官方解釋
    Handler允許你發送和處理Message和與線程的MessageQueue關聯的Runnable對象。每個Handler實例與一個線程和它的MessageQueue相關聯。當你創建一個新的Handler,它就被綁定到這個線程或者這個線程的消息隊列上–從那時起,它會傳遞messages和runnables到那個消息隊列,然後當他們離開消息隊列時執行他們。
  • 個人理解
    Handler是一個線程消息處理者,是UI線程和子線程(工作線程)之間的信使。他可以將工作線程中需更新UI的操作信息 傳遞到 UI主線程,從而實現在工作線程對UI的異步更新。
    ps:其實Handler可以完成任何線程之間的消息傳遞,不只是主線程與子線程。

二、爲啥用Handler

  • 在多個線程同時更新UI的時候,保證線程的安全。
  • 假如最初的設計沒有Handler,而是所有地方都能更新UI,那麼我有很多個線程同時在給一個文本控件setText,那肯定會引起界面混亂。如果這時候要解決怎麼辦?加鎖?這樣又會影響性能。
  • 所以Android在設計的時候就加入了這個Handler的機制讓我們去遵守而不用去考慮多線程的問題。

三、Handler的機制(原理)

一句話解釋:

Handler通過執行其綁定線程的消息隊列(MessageQueue)中不斷被Looper循環取出的消息(Message)來完成線程間的通信。

主要有幾個類需要理解:
1. Looper

  • 官方解釋
    用於運行線程的消息循環的類。默認情況下線程沒有與之關聯的消息循環;在要運行循環的線程中調用prepare來創建一個Looper,然後調用loop使其循環處理消息,直到循環結束爲止。
  • 主要方法
    首先看prepare方法:
	/**
     * 將當前線程初始化爲Looper,這使您有機會創建Handlers,然後在實際開始循環之前先引用這個Looper。
     * 確保在調用此方法後調用{@link #loop()},並通過調用{@link #quit()}結束該方法。
     */
    public static void prepare() {
        prepare(true);
    }

    /**
     *  這裏sThreadLocal是一個ThreadLocal的對象,可以在一個線程中存儲變量。
     *  這裏可以看到儲存的是一個Looper的實例,前三行判斷如果sThreadLocal.get()爲空,則拋出異常。
     *  說明此方法不能被調用兩次,也保證了一個線程中只有一個Loopper實例。
     */
    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)對象和將當前線程賦值給一個全局變量mThread 
     */
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

再看loop方法,只保留了重點代碼:

 	/**
     *在此線程中運行消息隊列。 確保調用{@link #quit()}以結束循環
     */
    public static void loop() {
    // 調用sThreadLocal.get()方法獲取之前儲存的Looper實例
        final Looper me = myLooper();
        if (me == null) {
        // 如果me爲null則拋出異常,也就是說loop方法必須在prepare方法之後運行。
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        // 拿到該looper實例中的mQueue(消息隊列)
        final MessageQueue queue = me.mQueue;
        // 開啓無限循環
        for (;;) {
        	// 取出一條消息,如果沒有消息則阻塞
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
        
            try {
            	// 使用調用 msg.target.dispatchMessage(msg);把消息交給msg的target的dispatchMessage方法去處理。
            	//Msg的target就是handler對象
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            //釋放消息佔據的資源
            msg.recycleUnchecked();
        }

接下來就走到了Handler的dispatchMessage方法了
2. Handler

  • 先看構造方法,通過as的structure可以看到Handler有7個構造方法,:Handler的構造方法
    這裏我們平時使用最多的可能是兩種,一個是無參的,一種是傳入Looper對象的。但是不管哪種,最終都是走到了最後兩個構造方法。來看倒數第二個方法:
public Handler(Callback callback, boolean async) {
        // 這個FIND_POTENTIAL_LEAKS變量恆爲false,看不懂
        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());
            }
        }
        // 獲取當前線程保存的Looper實例
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        // 獲取當前線程保存的Looper實例中實例化時保存的MessageQueue(消息隊列)
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

這個構造方法保證了handler的實例與我們Looper實例中MessageQueue關聯上了。
最後一種更粗暴,直接把需要的變量都傳進去了,全部賦值:

public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

總之,不管以哪種方式實例化Handler,目的都是要將其與MessageQueue關聯上。

  • sendMessage,這個方法平時是用的最多的了
	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);
    }
    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);
    }

經過一連串的調用以後到了sendMessageAtTime方法,在這裏獲取到MessageQueue然後調用enqueueMessage方法將其傳入。
在enqueueMessage方法中把Message.target 賦值爲this(在上面Looper的loop方法裏面就是利用了這個target.dispatchMessage(msg)來傳遞的消息),也就是將當前的Handler實例傳入Message賦值給其target屬性;最後調用queue.enqueueMessage方法把消息保存到消息隊列中。
說到這裏,倒是可以看看這個dispatchMessage方法了:

	/**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
        // 這裏mCallback是否爲空取決於調用哪個構造函數,但是目的都是爲了讓我們執行handleMessage方法,不管是Handler的還是Callback
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

最後來看handleMessage方法:

public interface Callback {
        /**
         * @param msg A {@link android.os.Message Message} object
         * @return True if no further handling is desired
         */
        public boolean handleMessage(Message msg);
    }
	/**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }

可以看到不管是不管是Handler的還是Callback的handleMessage方法,都是需要我們自己去重寫然後根據回調的msg進行消息處理。

  • 總結Handler機制
    • 1.Looper.prepare()在當前線程使用ThreadLocal保存一個通過構造方法生成的Looper實例,並保存一個MessageQueue實例。該方法在同一個線程只能調用一次,所以一個線程只會存在一個Looper和一個MessageQueue。
    • 2.Looper.loop()開啓無限循環,每次從MessageQueue實例取出一條消息,再通過該消息的 msg.target.dispatchMessage(msg) 方法將消息進行傳遞。
    • 3.通過Handler的構造方法獲取當前線程中的Looper實例及獲取當該Looper對象中實例化時保存的MessageQueue(消息隊列)。
    • 4.通過handler.sendMessage(Message msg)給msg的target賦值爲handler自身,然後加入MessageQueue中。
    • 5.msg.target.dispatchMessage(msg) 調用到最後實際是回調了Handler的handleMessage方法,由我們自行重寫處理消息。

四、Handler的用法

常用有兩種

  • 直接new Handler(),通過handler.sendMessage()發消息,然後重寫handleMessage()方法等待回調處理消息。
Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            logic();
        }
    };
  • 傳入Looper,此處爲封裝主線程,傳入Looper.getMainLooper(),保證了不管在什麼線程中實例化,都是在主線程回調。然後調用handler.post(Runnable r)方法或postDelayed。

ps:在主線程中創建Handler時,主線程會自動創建Looper的,而自己在子線程中自定義handler時,需要手動創建Looper,並調用Looper的prepare方法,或者傳Looper.getMainLooper()

public class MainThread {

    private Handler mHandler;

    public MainThread() {
        mHandler = new Handler(Looper.getMainLooper());
    }

    public void post(Runnable runnable) {
        postDelayed(runnable, 0);
    }

    public void postDelayed(Runnable runnable, long delayMillis) {
        mHandler.postDelayed(runnable, delayMillis);
    }

    public void removeCallbacks(Runnable runnable) {
        mHandler.removeCallbacks(runnable);
    }
}
  • 子線程創建Handler,如果不給Handler指定一個Looper就會出現異常
class MyThread extends Thread{
    public  Handler handler;
    @Override
    public void run() {
          Looper.prepare(); //創建一個Looper對象
          handler = new Handler(){
               //此方法運行在子線程中
               @Override
               public void handleMessage(Message msg) {
                   System.out.println("當前的線程-->" + Thread.currentThread());
               }
          };
      Looper.loop(); //開始輪詢
    }
}

MyThread thread = new MyThread();
thread.start(); //啓動線程
//發送消息就會執行該handler的handleMessage方法(在子線程中)
thread.handler.sendEmptyMessage(1);

五、用到Handler的地方

  • HandlerThread
    本身是一個子線程,已經在run()方法中指定了Looper,可以直接進行異步操作。

HandlerThread

  • Activity的runOnUiThread(Runnable action)方法,實際上是用了handler.post(action)方式。內部先判斷當前線程是不是主線程,如果不是就通過handler發送一個消息。
 public final void runOnUiThread(Runnable action) {
        if (Thread.currentThread() != mUiThread) {
            mHandler.post(action);
        } else {
            action.run();
        }
    }
  • Activity的啓動流程中(在ActivityThread的main方法裏面調用Looper.prepareMainLooper())
    在這裏插入圖片描述
    在這裏插入圖片描述
    給sMainLooper賦值當前線程(即主線程)的Looper。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章