爲什麼主線程可以new Handler?
在ActivityThread.java裏有一個main()函數,它是Android每一個應用最早執行的函數。
public static void main(String[] args) {
.....
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
它先prepare的,看這個prepare幹了些什麼事情?
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
prepare是初始化,初始化了一個Looper。
然後又接着調用的Looper.loop()函數,這個loop()函數其實就是執行了那個for循環,它不斷的調用next()函數,通過調用next()函數去輪詢我們的MessageQueue。
如果不調用prepare(),Looper沒有被初始化;如果不調用loop(),Looper的機制滾動不起來。所以,所有的執行必須先prepare(),然後再loop()。
回到問題,爲什麼主線程不需要做這些操作呢?
因爲主線程一啓動的時候,在main()函數中,由系統已經幫我們完成了,我們主線程中的所有代碼,全都運行在這兩個函數(prepare() 和 loop())之間。
在子線程中new Handler需要做哪些準備?
在上一題 爲什麼主線程可以new Handler?中其實已經解答,所有的線程都必須要prepare()和loop(),如果子線程中想要進行Handler操作,就必須在子線程中執行prepare() 和 loop()。
子線程中維護的Looper,消息隊列無消息的時候的處理方案是什麼?
我們先來創建一個子線程,然後子線程中創建了一個Looper,並且發送了一個消息,消息處理完了,看一下會發生什麼樣的事情。
Handler threadHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
if (threadHandler == null){
Looper.prepare();
threadHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.e("seas","handle Message");
}
};
Log.e("seas","Looper loop() UP");
Looper.loop();
Log.e("seas","Looper loop() DOWN");
}
}
}).start();
}
public void click(View view){
Log.e("seas","click");
threadHandler.sendMessage(threadHandler.obtainMessage());
}
我運行之後,再點擊執行click方法,就出現瞭如下的log。
如果這個子線程結束,那麼他應該是有調用到 Log.e("seas","Looper loop() DOWN"); 這個語句纔對,但是它並沒有,這就意味着這個Looper一直處於一個阻塞狀態。
也就是說它一直卡在了這個地方:
for (;;) {
Message msg = queue.next(); // might block
.....
}
所以它就一直在等待,這個子線程也已經幹不了其他的事情了,其實也被卡死了。
子線程中維護的Looper,消息隊列無消息的時候的處理方案是什麼?有什麼用?
在Handler機制裏面有一個Looper,在Looper機制裏面有一個函數,叫做quitSafely()和quit()函數,這兩個函數是調用的MessageQueue的quit()。
/**
* Quits the looper.
* <p>
* Causes the {@link #loop} method to terminate without processing any
* more messages in the message queue.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p class="note">
* Using this method may be unsafe because some messages may not be delivered
* before the looper terminates. Consider using {@link #quitSafely} instead to ensure
* that all pending work is completed in an orderly manner.
* </p>
*
* @see #quitSafely
*/
public void quit() {
mQueue.quit(false);
}
/**
* Quits the looper safely.
* <p>
* Causes the {@link #loop} method to terminate as soon as all remaining messages
* in the message queue that are already due to be delivered have been handled.
* However pending delayed messages with due times in the future will not be
* delivered before the loop terminates.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p>
*/
public void quitSafely() {
mQueue.quit(true);
}
再進入到MessageQueue的quit()函數。
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
它會remove消息,把消息隊列中的全部消息給幹掉。
把消息全部幹掉,也就釋放了內存。
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
if (p.when > now) {
removeAllMessagesLocked();
} else {
Message n;
for (;;) {
n = p.next;
if (n == null) {
return;
}
if (n.when > now) {
break;
}
p = n;
}
p.next = null;
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}
而在quit()函數的最後一行,有一個nativeWake()函數。
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
這個函數的調用,就會叫醒等待的地方,醒來之後,就接着往下執行。
//native的方法,在沒有消息的時候回阻塞管道讀取端,只有nativePollOnce返回之後才能往下執行
//阻塞操作,等待nextPollTimeoutMillis時長
nativePollOnce(ptr, nextPollTimeoutMillis);
往下執行後,發現 Message msg = mMessages; 是空的,然後就執行了這個,就接着往下走。
if (msg != null) {
......
} else {
// No more messages.
//沒有消息,nextPollTimeoutMillis復位
nextPollTimeoutMillis = -1;
}
然後又調用了這個方法,並且return了null。
// Process the quit message now that all pending messages have been handled.
//如果消息隊列正在處於退出狀態返回null,調用dispose();釋放該消息隊列
if (mQuitting) {
dispose();
return null;
}
所以說,這個時候Looper就結束了(跳出了死循環),則達成了第二個作用:釋放線程。
如果我在剛纔的例子上加入退出操作。
public void stopclick(View view){
Log.e("seas","click");
threadHandler.getLooper().quit();
}
打印如下:
這個線程就真正的執行了,否則這個線程就一直在等待。
如果在子線程中創建了一個Handler,那麼就必須做三個操作:
1. prepare();
2. loop();
3. quit();
主線程中能否調用quit()方法?
是不能的。它會拋出一個異常,讓程序掛掉。
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
......
}