Android訊息機制
本質上是一個執行緒開啟無限迴圈並持續監聽其他執行緒給他發的訊息,如果沒有訊息自身就堵塞(相當於wait)
looper是要被呼叫的,android-main方法的呼叫:https://www.jianshu.com/p/302fe75d6778(未看)
大致的流程
先來大致梳理下整個流程:
-
應用程式啟動的時候,在主執行緒中會預設呼叫了 Looper.prepare()方法,初始化Looper物件並繫結到當前執行緒中,並在Looper內部維護一個MessageQueue
-
接著呼叫handler.sendMessage()傳送訊息,會通過MessageQueue.enqueueMessage()向MessageQueue中新增一條訊息
-
主執行緒呼叫Looper.looper()開啟迴圈,不斷輪詢訊息佇列,通過MessageQueue.next()取出訊息
-
取出的message不為空則呼叫msg.target.dispatchMessage()傳遞分發訊息,目標handler收到訊息後會執行handler.handlerMessage()方法處理訊息
Looper:
//建構函式:不允許別人建立 同時建立了一個MessageQueue物件
privateLooper(booleanquitAllowed) {
mQueue=newMessageQueue(quitAllowed);
mThread=Thread.currentThread();
}
-
ActivityThread.main()方法中系統已經幫我們建立好Looper物件,而子執行緒的looper是我們自己建立的。
主執行緒中的Looper的prepare與子執行緒不一樣:
從prepare()可以看出而呼叫prepare的執行緒如果是第二次構建looper物件會丟擲異常
//Looper類中擁有一個ThreadLocal<Looper>池
//ThreadLocal中裝著所有執行緒的Looper物件
// sThreadLocal.get() will return null unless you've called prepare().
staticfinalThreadLocal<Looper>sThreadLocal=newThreadLocal<Looper>();
//Looper類中擁有主執行緒的looper物件!
privatestaticLoopersMainLooper;
//所有子執行緒要構建looper之前需要呼叫
privatestaticvoidprepare(booleanquitAllowed) {
if(sThreadLocal.get()!=null) {
thrownewRuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(newLooper(quitAllowed));
}
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. See also: {@link #prepare()}
*
* @deprecated The main looper for your application is created by the Android environment,
* so you should never need to call this function yourself.
*/
//主執行緒中不需要自己建立Looper
publicstaticvoidmain(String[]args) {
......
Looper.prepareMainLooper();//為主執行緒建立Looper,該方法內部又呼叫 Looper.prepare()
......
Looper.loop();//開啟訊息輪詢
......
}
//子執行緒
publicclassLooperThreadextendsThread{
Loop()函式:
LoopOnce函式:挑重點
Loop迴圈不會卡死的原因:
主執行緒的任何程式碼都是被looper從佇列中取出來的執行的,也就是說主執行緒的是接受所有其他執行緒給他傳送訊息來執行動作的,生命週期的回撥也是通過系統服務系統服務ActivityManagerService通過Binder傳送IPC呼叫給APP程序,App程序接到到呼叫後,通過App程序的Binder執行緒給主執行緒的訊息佇列插入一條訊息來實現的。而Binder執行緒是在主執行緒進入無限迴圈的時候建立的,而當呼叫messageQueue方法的next如果沒有訊息的時候,主執行緒會釋放當前cpu資源並且進入相當於wait狀態(之後版本實現方法不一樣)
其中Looper與ThreadLocal息息相關:不妨先看一下ThreadLocal<T>
ThreadLocal: 執行緒本地儲存區(Thread Local Storage,簡稱為TLS),每個執行緒都有自己的私有的本地儲存區域,不同執行緒之間彼此不能訪問對方的TLS區域。這裡執行緒自己的本地儲存區域存放是執行緒自己的Looper。
不同的執行緒執行相對應的函式的時候都會呼叫Looper類中的sThreadLoacl.get獲得對應的執行緒例項
publicTget() {
Threadt=Thread.currentThread();
ThreadLocalMapmap=getMap(t);
if(map!=null) {
ThreadLocalMap.Entrye=map.getEntry(this);
if(e!=null) {
小結一下:
Looper中管理這一個ThreadLocal物件,用該物件來實現不同執行緒間的looper物件切換以及使用其他功能
Handler
作用:用於同一個程序間的執行緒通訊,
handler為了防止記憶體洩漏會有檢查
//可以隨時獲得主執行緒的handler
publicstaticHandlergetMain() {
if(MAIN_THREAD_HANDLER==null) {
MAIN_THREAD_HANDLER=newHandler(Looper.getMainLooper());
}
returnMAIN_THREAD_HANDLER;
}
//Callback,這也表明了callback函式是執行在當前執行緒上的,不可以執行ui工作
publicinterfaceCallback{
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
booleanhandleMessage(
構造方法:新建一個Handler會得到當前執行緒的looper物件以及裡面的MessageQueue,並且獲得一個傳進來的callback物件
publicHandler() {
this(null,false);
}
publicHandler(Callbackcallback,booleanasync) {
//不是static 發出可能記憶體洩露的警告!
if(FIND_POTENTIAL_LEAKS) {
finalClass<?extendsHandler>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,還記得前面講過 Looper.myLooper()方法了嗎?
//Looper.myLooper()內部實現可以先簡單理解成:map.get(Thread.currentThread())
//獲取當前執行緒的Looper
mLooper=Looper.myLooper();
if(mLooper==null) {
//當前執行緒不是Looper 執行緒,沒有呼叫Looper.prepare()給執行緒建立Looper物件
thrownewRuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//讓Handler 持有當前執行緒訊息佇列的引用
mQueue=mLooper.mQueue;
//這些callback先不管,主要用於handler的訊息傳送的回撥,優先順序是比handlerMessage高,但是不常用
mCallback=callback;
mAsynchronous=async;
}
Handler 在sendMessage的時候就通過mQueue引用往訊息佇列裡插入新訊息。Handler 的另外一個作用,就是能統一處理訊息的回撥。這樣一個Handler發出訊息又確保訊息處理也是自己來做,這樣的設計非常的贊。具體做法就是在佇列裡面的Message持有Handler的引用(哪個handler 把它放到佇列裡,message就持有了這個handler的引用),然後等到主執行緒輪詢到這個message的時候,就來回調我們經常重寫的Handler的handleMessage(Message msg)
方法。
在準備啟動一個Activity的時候,系統服務程序下的ActivityManagerService
(簡稱AMS)執行緒會通過Binder傳送IPC呼叫給APP程序,App程序接到到呼叫後,通過App程序下的Binder執行緒最終呼叫ActivityThread
類下面的scheduleLaunchActivity
方法來準備啟動Activity,看下scheduleLaunchActivity方法:
注:Binder執行緒:具體是指ApplicationThread,在App程序中接受系統程序傳遞過來的資訊的執行緒(在主執行緒進入死迴圈之前建立了這個執行緒)。
最後呼叫!!!
publicvoidhandleMessage(
MessageQueue
存在於Looper中,MessageQueue 存在的原因很簡單,就是同一執行緒在同一時間只能處理一個訊息,同一執行緒程式碼執行是不具有併發性,所以需要佇列來儲存訊息和安排每個訊息的處理順序。多個其他執行緒往UI執行緒傳送訊息,UI執行緒必須把這些訊息保持到一個列表(它同一時間不能處理那麼多工),然後挨個拿出來處理,這種設計很簡單,我們平時寫程式碼其實也經常這麼做。每一個Looper執行緒都會維護這樣一個佇列,而且僅此一個,這個佇列的訊息只能由該執行緒處理
booleanenqueueMessage(Messagemsg,longwhen) {
// msg 必須有target也就是必須有handler
if(msg.target==null) {
thrownewIllegalArgumentException("Message must have a target.");
}
if(msg.isInUse()) {
thrownewIllegalStateException(msg+" This message is already in use.");
}
//插入訊息佇列的時候需要做同步,因為會有多個執行緒同時做往這個佇列插入訊息
synchronized(this) {
if(mQuitting) {
IllegalStateExceptione=newIllegalStateException(
msg.target+" sending message to a Handler on a dead thread");
Log.w(TAG,e.getMessage(),e);
msg.recycle();
returnfalse;
}
msg.markInUse();
//when 表示這個訊息執行的時間,佇列是按照訊息執行時間排序的
//如果handler 呼叫的是postDelay 那麼when=SystemClock.uptimeMillis()+delayMillis
msg.when=when;
Messagep=mMessages;
booleanneedWake;
if(p==null||when==0||when<p.when) {
// p==null 表示當前訊息佇列沒有訊息
msg.next=p;
mMessages=msg;
//需要喚醒主執行緒,如果佇列沒有元素,主執行緒會堵塞在管道的讀端,這時
//候佇列突然有訊息了,就會往管道寫入字元,喚醒主執行緒
needWake=mBlocked;
}else{
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake=mBlocked&&p.target==null&&msg.isAsynchronous();
Messageprev;
//將訊息放到佇列的確切位置,佇列是按照msg的when 排序的,連結串列操作自己看咯,從連結串列頭開始放訊息,根據時間塞進去
for(;;) {
prev=p;
p=p.next;
if(p==null||when<p.when) {
break;
}
if(needWake&&p.isAsynchronous()) {
needWake=false;
}
}
msg.next=p;// invariant: p == prev.next
prev.next=msg;
}
// 如果需要喚醒Looper執行緒,這裡呼叫native的方法實現epoll機制喚醒執行緒,我們就不在深入探討了
if(needWake) {
nativeWake(mPtr);
}
}
returntrue;
}
//兩個版本!!!
privatebooleanenqueueMessage(MessageQueuequeue,Messagemsg,longuptimeMillis) {
//這句話很重要,讓訊息持有當前Handler的引用,在訊息被Looper執行緒輪詢到的時候
//回撥handler的handleMessage方法
msg.target=this;
if(mAsynchronous) {
msg.setAsynchronous(true);
}
//呼叫MessageQueue 的enqueueMessage 方法把訊息放入佇列
returnqueue.