Handler, Message, Looper, ActivityThread
Handler, Message, Looper, ActivityThread
關鍵詞 nativePollOnce nativeWake ActivityThread並不是Thread ActivityThread使用attach方法將主進程與AMS服務綁定起來 消息隊列並不會阻塞 因爲所有的生命週期和UI刷新全部用Handler消息機制執行
Handler
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 = callback;
mAsynchronous = async;
}
Handler 主要的幾個方法
/*
*消息進隊
*/
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
MessageQueue
MessageQueue構造函數
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
MessageQueue的主要方法
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
Looper
Looper的生成函數
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class
final MessageQueue mQueue;
final Thread mThread;
/*
* 生成Looper
*/
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));
}
/**
* 生成ActivityThread中的Looper
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
Looper主要方法
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
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (slowDispatchThresholdMs > 0) {
final long time = end - start;
if (time > slowDispatchThresholdMs) {
Slog.w(TAG, "Dispatch took " + time + "ms on "
+ Thread.currentThread().getName() + ", h=" +
msg.target + " cb=" + msg.callback + " msg=" + msg.what);
}
}
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();
}
}
遇到主要問題
- 消息機制是怎麼運轉起來的?
一般認爲消息機制的Handler,Looper,MessageQueue在消息機制中承擔的角色分別是Handler是消息的發射器和分發器,Looper是消息機制的發動機,MessageQueue是消息的存儲隊列。
Looper.loop()會運行一個死循環去MessageQueue取得Message,拿到Message中的Callback然後分發執行。
在loop方法中通過for (;;)死循環調用queue.next()來得到下一個處理的Message示例來執行,當queue.next()返回爲null時,loop方法跳出執行,死循環結束。
而MessageQueue的next方法中同樣有一個for (;;)死循環去調用nativePollOnce(ptr, nextPollTimeoutMillis);得到MessageQueue中的下一個Message實例。nativePollOnce()方法和另外一個方法nativeWake(mPtr);實現了Linux的epoll的I/O阻塞模型。(Linux中的epoll阻塞模型是經典的阻塞模型之一)
所以之前的想法是錯誤的 假設消息隊列中有1000個消息,那麼Looper,loop()方法的死循環執行1000次便跳出的想法是錯誤的。不管在loop方法中還是queue的next方法中的死循環在應用的生命週期中都不會被跳出,假設有1000個消息,消息全部處理完後,循環阻塞在nativePollOnce()方法中等待喚醒,當有Handler發射一個消息並且執行enqueueMessage方法調用queue.enqueueMessage(msg, uptimeMillis);方法在enqueueMessage中的nativeWake(mPtr)方法使得阻塞釋放重新取出消息進行執行。
- Looper.loop方法會不會阻塞主線程ActivityThread?
簡單講有兩個原因
- epoll模型 當沒有消息的時候會epoll.wait,等待句柄寫的時候再喚醒,這個時候其實是阻塞的。
- 所有的ui操作都通過handler來發消息操作。比如屏幕刷新16ms一個消息,你的各種點擊事件,所以就會有句柄寫操作,喚醒上文的wait操作,所以不會被卡死了。
詳細講
要完全徹底理解這個問題,需要準備以下4方面的知識:Process/Thread,Android Binder IPC,Handler/Looper/MessageQueue消息機制,Linux pipe/epoll機制。總結一下樓主主要有3個疑惑:
- Android中爲什麼主線程不會因爲Looper.loop()裏的死循環卡死?
- 沒看見哪裏有相關代碼爲這個死循環準備了一個新線程去運轉?
- Activity的生命週期這些方法這些都是在主線程裏執行的吧,那這些生命週期方法是怎麼實現在死循環體外能夠執行起來的?
1. Android中爲什麼主線程不會因爲Looper.loop()裏的死循環卡死?
這裏涉及線程,先說說進程/線程,
進程:
每個app運行時前首先創建一個進程,該進程是由Zygote fork出來的,用於承載App上運行的各種Activity/Service等組件。進程對於上層應用來說是完全透明的,這也是google有意爲之,讓App程序都是運行在Android Runtime。大多數情況一個App就運行在一個進程中,除非在AndroidManifest.xml中配置Android:process屬性,或通過native代碼fork進程。
線程:
線程對應用來說非常常見,比如每次new Thread().start都會創建一個新的線程。該線程與App所在進程之間資源共享,從Linux角度來說進程與線程除了是否共享資源外,並沒有本質的區別,都是一個task_struct結構體,在CPU看來進程或線程無非就是一段可執行的代碼,CPU採用CFS調度算法,保證每個task都儘可能公平的享有CPU時間片。
有了這麼準備,再說說死循環問題:
對於線程既然是一段可執行的代碼,當可執行代碼執行完成後,線程生命週期便該終止了,線程退出。而對於主線程,我們是絕不希望會被運行一段時間,自己就退出,
那麼如何保證能一直存活呢?
簡單做法就是可執行代碼是能一直執行下去的,死循環便能保證不會被退出,例如,binder線程也是採用死循環的方法,通過循環方式不同與Binder驅動進行讀寫操作,當然並非簡單地死循環,無消息時會休眠。
但這裏可能又引發了另一個問題,既然是死循環又如何去處理其他事務呢?
通過創建新線程的方式。真正會卡死主線程的操作是在回調方法onCreate/onStart/onResume等操作時間過長,會導致掉幀,甚至發生ANR,looper.loop本身不會導致應用卡死。
2. 沒看見哪裏有相關代碼爲這個死循環準備了一個新線程去運轉? 事實上,會在進入死循環之前便創建了新
binder線程,在代碼ActivityThread.main()中:
public static void main(String[] args) {
//創建Looper和MessageQueue對象,用於處理主線程的消息
Looper.prepareMainLooper();
//創建ActivityThread對象
ActivityThread thread = new ActivityThread();
//建立Binder通道 (創建新線程)
thread.attach(false);
Looper.loop(); //消息循環運行
throw new RuntimeException("Main thread loop unexpectedly exited");
}
thread.attach(false);
便會創建一個Binder線程(具體是指ApplicationThread,Binder的服務端,用於接收系統服務AMS發送來的事件),該Binder線程通過Handler將Message發送給主線程,具體過程可查看 startService流程分析,這裏不展開說,簡單說Binder用於進程間通信,採用C/S架構。
另外,
ActivityThread實際上並非線程,不像HandlerThread類,ActivityThread並沒有真正繼承Thread類,只是往往運行在主線程,該人以線程的感覺,其實承載ActivityThread的主線程就是由Zygote fork而創建的進程。
主線程的死循環一直運行是不是特別消耗CPU資源呢?
其實不然,這裏就涉及到Linux pipe/epoll機制,簡單說就是在主線程的MessageQueue沒有消息時,便阻塞在loop的queue.next()中的nativePollOnce()方法裏,此時主線程會釋放CPU資源進入休眠狀態,直到下個消息到達或者有事務發生,通過往pipe管道寫端寫入數據來喚醒主線程工作。這裏採用的epoll機制,是一種IO多路複用機制,可以同時監控多個描述符,當某個描述符就緒(讀或寫就緒),則立刻通知相應程序進行讀或寫操作,本質同步I/O,即讀寫是阻塞的。 所以說,主線程大多數時候都是處於休眠狀態,並不會消耗大量CPU資源。
3. Activity的生命週期是怎麼實現在死循環體外能夠執行起來的?
ActivityThread的內部類H繼承於Handler,通過handler消息機制,簡單說Handler機制用於同一個進程的線程間通信。Activity的生命週期都是依靠主線程的Looper.loop,當收到不同Message時則採用相應措施:在H.handleMessage(msg)方法中,根據接收到不同的msg,執行相應的生命週期。 比如收到msg=H.LAUNCH_ACTIVITY,則調用ActivityThread.handleLaunchActivity()方法,最終會通過反射機制,創建Activity實例,然後再執行Activity.onCreate()等方法; 再比如收到msg=H.PAUSE_ACTIVITY,則調用ActivityThread.handlePauseActivity()方法,最終會執行Activity.onPause()等方法。 上述過程,我只挑核心邏輯講,真正該過程遠比這複雜。
**主線程的消息又是哪來的呢?**當然是App進程中的其他線程通過Handler發送給主線程,請看接下來的內容:--------------------------------------------------------------------------------------------------------------------最後,從進程與線程間通信的角度,通過一張圖加深大家對App運行過程的理解:
[外鏈圖片轉存失敗(img-0NCDxJ8m-1562897297656)(media/15564179928939/15564194168509.jpg)]
system_server進程是系統進程,java framework框架的核心載體,裏面運行了大量的系統服務,比如這裏提供ApplicationThreadProxy(簡稱ATP),ActivityManagerService(簡稱AMS),這個兩個服務都運行在system_server進程的不同線程中,由於ATP和AMS都是基於IBinder接口,都是binder線程,binder線程的創建與銷燬都是由binder驅動來決定的。
App進程則是我們常說的應用程序,主線程主要負責Activity/Service等組件的生命週期以及UI相關操作都運行在這個線程;
另外,
每個App進程中至少會有兩個binder線程 ApplicationThread(簡稱AT)和ActivityManagerProxy(簡稱AMP),除了圖中畫的線程,其中還有很多線程,比如signal catcher線程等,這裏就不一一列舉。Binder用於不同進程之間通信,由一個進程的Binder客戶端向另一個進程的服務端發送事務,比如圖中線程2向線程4發送事務;
而handler用於同一個進程中不同線程的通信,比如圖中線程4向主線程發送消息。結合圖說說Activity生命週期,比如暫停Activity,流程如下:線程1的AMS中調用線程2的ATP;(由於同一個進程的線程間資源共享,可以相互直接調用,但需要注意多線程併發問題)線程2通過binder傳輸到App進程的線程4;線程4通過handler消息機制,將暫停Activity的消息發送給主線程;主線程在looper.loop()中循環遍歷消息,當收到暫停Activity的消息時,便將消息分發給ActivityThread.H.handleMessage()方法,再經過方法的調用,最後便會調用到Activity.onPause(),當onPause()處理完後,繼續循環loop下去。
private class H extends Handler {},其實例final H mH = new H();通過發送Message的方式完成Activity的生命週期的調用。例如
public final void scheduleStopActivity(IBinder token, boolean showWindow,
int configChanges) {
int seq = getLifecycleSeq();
if (DEBUG_ORDER) Slog.d(TAG, "stopActivity " + ActivityThread.this
+ " operation received seq: " + seq);
sendMessage(
showWindow ? H.STOP_ACTIVITY_SHOW : H.STOP_ACTIVITY_HIDE,
token, 0, configChanges, seq);
}
會在ActivityThread中的handleMessage方法中
case STOP_ACTIVITY_SHOW: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
SomeArgs args = (SomeArgs) msg.obj;
handleStopActivity((IBinder) args.arg1, true, args.argi2, args.argi3);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
Looper 怎麼保證的在一個線程中只有一個Looper 的實例的?
在Looper中有這樣一個變量
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
這裏採用ThreadLocal來保證各個線程的Looper的唯一性
ThreadLocal的主要方法有
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
static class ThreadLocalMap {}
而在Thread中存在
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
通過這樣的方式保證了Looper的唯一性