常用場景:
子線程發送Message?
主線程處理Message
子線程發送消息
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {Message msg = Message.obtain();msg.what = what;return sendMessageDelayed(msg, delayMillis);}public final boolean sendEmptyMessage(int what){return sendEmptyMessageDelayed(what, 0);}
不管那種方式發送小時最終都走到 sendMessageAtTime
public boolean sendMessageAtTime(@NonNull 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(@NonNull MessageQueue queue, @NonNull Message msg,long uptimeMillis) {msg.target = this;msg.workSourceUid = ThreadLocalWorkSource.getUid();if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis);}
通過enqueueMessage將消息按照時間先手插入到MessageQueue中
主線程處理消息
應用啟動:
frameworks\base\core\java\android\app\ActivityThread.java
main方法中關注Looper.prepareMainLooper()和Looper.loop().
Looper.prepareMainLooper()
public static void prepareMainLooper() {prepare(false);synchronized (Looper.class) {if (sMainLooper != null) {//Looper的唯一性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));}private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);mThread = Thread.currentThread();//綁定當前線程}MessageQueue(boolean quitAllowed) {mQuitAllowed = quitAllowed;mPtr = nativeInit();}void quit(boolean safe) {if (!mQuitAllowed) {throw new IllegalStateException("Main thread not allowed to quit.");}
......
}
1.?prepare創建 Looper 并設置給了sThreadLocal 后面loop中要獲取
2.prepare創建不可退出的MessageQueue(因為在主線程)
Looper.loop()
public static void loop() {final Looper me = myLooper();......me.mInLoop = true;final MessageQueue queue = me.mQueue;for (;;) { //死循環Message msg = queue.next(); // might block......try {msg.target.dispatchMessage(msg);if (observer != null) {observer.messageDispatched(token, msg);}dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;} catch (Exception exception) {if (observer != null) {observer.dispatchingThrewException(token, msg, exception);}throw exception;} finally {ThreadLocalWorkSource.restore(origWorkSource);if (traceTag != 0) {Trace.traceEnd(traceTag);}}......msg.recycleUnchecked();}}public static @Nullable Looper myLooper() {return sThreadLocal.get();}
1.Looper持有了當前線程的MessageQueue
2.通過queue.next() 獲取當前時間下一次要執行的Message
3.處理消息 msg.target.dispatchMessage(msg) 分發到內部類中處理(msg.target就是當前消息綁定的Handler)
4. 消息回收(消息復用) msg.recycleUnchecked()
流程:應用啟動-->ActivityThread main 啟動?--> 準備Looper? --> Looper死循環? 一直取隊列中的消息??-->處理消息?--> 消息回收處理
PS: 應用異常(Runtime)時壓棧最底下ActivityThread.main()? ?倒數第二行Looper.loop()? ? 這也印證了啟動的順序
Handler創建
常見創建:
Handler handler=new Handler(){@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);}
};public Handler() {this(null, false);}public Handler(@Nullable Callback callback, boolean async) {mLooper = Looper.myLooper();//獲取Looperif (mLooper == null) {throw new RuntimeException("Can't create handler inside thread " + Thread.currentThread()+ " that has not called Looper.prepare()");}mQueue = mLooper.mQueue;//獲取消息隊列mCallback = callback;//注冊回調mAsynchronous = async;}public interface Callback {boolean handleMessage(@NonNull Message msg);}
消息創建
new Message
/** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).*/public Message() {}
Google 沒有詳細說明new? Message 當通過我們實際直接new 就完事了無需多言,不過Google推薦使用Message.obtain() 去創建
方法一:
Message msg = Message.obtain()
--------------------------------源碼-------------------------------public static Message obtain() {synchronized (sPoolSync) {if (sPool != null) {Message m = sPool;sPool = m.next;m.next = null;m.flags = 0; // clear in-use flagsPoolSize--;return m;}}return new Message();}
方法二:
Handler handler = new Handler();
Message msg = handler.obtainMessage();//綁定了handler
--------------------------------源碼-------------------------------
public final Message obtainMessage(){return Message.obtain(this);// this==handler}public static Message obtain(Handler h) {Message m = obtain();m.target = h;return m;}
不管那種方式創建最終都是走無參的obtain方法
1.sPool等于空時通過new 創建Message
2.sPool 是在Looper dispatch 出去后通過recycleUnchecked清空后到Message
3.sPool最前面的空Messsage返回回去,sPool指針后移隊列到下一個保證下一個obtain可以正確獲獲取到sPool中的空Message
Message和handler的綁定
1.可以在創建Message是綁定,參考Message創建的方法二
2.消息發送是綁定
回到發送Message
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this; //綁定動作
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
總結:
?
同步問題
Handler是如何保證自己是線程安全?
從總結圖可以看出整個過程只要保證MessageQueue 中msg的入隊和出隊即可
enqueueMessage方法中 通過synchronized (this) {} 鎖住了關鍵的代碼塊
1,synchronized 是內置鎖? ?鎖的lock 和unlock 是系統(JVM)執行
2.? 鎖的是this,就相當于鎖的是?MessageQueue? 相當于 調用同一個MessageQueue的對象是互斥的
3. 一個線程持有一個Looper,一個Looper持有一個MessageQueue
所以:主線程只有一個MessageQueue子線程 發送消息的時候,子線程一次只能處理一個消息,其他的消息需要等鎖,這樣就保證了不管有多少子線程發送消息 主線程的MessageQueue時刻始終只處理一個消息的入隊? ??
next() 方法中 同樣通過synchronized (this) {} 鎖住了按照時間節點返回消息的關鍵代碼
既然這里都是一直要放回當前隊列的時間最靠前的msg(頭消息),加鎖的意義在哪里?
這里就是 鎖this的魅力 鎖住了MessgaeQueue,鎖的范圍是所有 this正在訪問的代碼塊都會有保護作用,即代表next方法和enqueueMessage方法能夠實現互斥統一時間MessageQueue只能入隊或者出隊這樣就保證了MessageQueue的有序性。??
HandlerThread
首先我們看下下面這段代碼創建Handler?
Thread thread = new Thread(new Runnable() {Looper looper;
@Override
public void run() {Looper.prepare();looper =Looper.myLooper();Looper.loop();
}
public Looper getLooper() {return looper;
}
});thread.start();Handler handler = new Handler(thread.getLooper());
子線程去獲取Looper對象然后通過子線程拿到Looper,這段代碼看似么有問題其實有雷運行時可能是出現new Hanlder 中參數Looper 為空
1.可能在執行new Handler對象時子線程沒有走完導致looper沒有賦值完成
解決:thread.start 后延時去new Handler 從而保證looper不為空,但此時線程依舊是不安全的
看看Goolge是如何解決子線程獲取Looper 且線程安全?
public class HandlerThread extends Thread {Looper mLooper;@Overridepublic void run() {mTid = Process.myTid();Looper.prepare();synchronized (this) {mLooper = Looper.myLooper();notifyAll();}Process.setThreadPriority(mPriority);onLooperPrepared();Looper.loop();mTid = -1;}public Looper getLooper() {if (!isAlive()) {return null;}// If the thread has been started, wait until the looper has been created.synchronized (this) {while (isAlive() && mLooper == null) {try {wait();} catch (InterruptedException e) {}}}return mLooper;}
1.HandlerThread 是Thread的子類 new HandlerThread? start 執行其run方法
?prepare 后通過獲取Looper關鍵代碼
?synchronized (this) {
? ? ? ? ? ? mLooper = Looper.myLooper();
? ? ? ? ? ? notifyAll();
? ? ? ? }
getLooper關鍵代碼
synchronized (this) {
? ? ? ? ? ? while (isAlive() && mLooper == null) {
? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? wait();
? ? ? ? ? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
1.通過synchronized (this)? 實現了run和getLooper的互斥
2.兩種情況?
2.1 :run 先拿到鎖
????????synchronized鎖住的代碼先執行,完完全全拿到Looper后通過notifyAll()通知所有?HandlerThread 對象 結束等鎖,準備拿鎖 ,然后釋放鎖?
2.2:getLooper??先拿到鎖
此時當線程活著時mLooper肯定為空線程執行wait() 等待且釋放鎖,getLooper??釋放鎖的同事run方法就會持有鎖,因為HandlerThread 就這兩個方法會獲取鎖。
如果是sleep()? 線程會阻塞 不會釋放鎖。
此類寫法就保證了不管什么樣的情況下當你通過HandlerThread 去getLooper是一定能獲取到線程的唯一Looper.此時線程是安全的。? ? ? ? ? ? ?
消息機制之同步屏障
揭秘 Android 消息機制之同步屏障:target==null ?我們知道,Android的消息機制就是Handle - 掘金
?
?
?
??