一個執行緒多個handler會有多少個looper,looper如何區分handler,會不會導致訊息錯亂。
阿新 • • 發佈:2018-12-12
面試題:
問題1:一個執行緒中初始化多個handler,會產生多少個looper?
問題2:如果只有一個looper,looper如何區分handler,handler傳送了訊息會不會導致Looper錯亂,最終不知道誰處理。
1 一個執行緒中初始化多個handler,會產生多少個looper
分析一下:做過android開發的都知道Handler是android的訊息機制,在主執行緒可以直接使用handler,那是因為主執行緒已經預設幫我們初始化了Looper,呼叫了Looper.prepare()和loop(),我們可以在主執行緒定義多個handler都不用自己生成或繫結Looper,所以一個執行緒只有一個Looper,可能大家會信這個分析,來看看Looper原始碼:
執行緒只有一個Looper:
// sThreadLocal.get() will return null unless you've called prepare(). static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); public static void prepare() { prepare(true); } //如果不為null,會報異常,所以一個執行緒只能呼叫一次prepare,生成一個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)); }
主執行緒幫我們初始化了Looper:
這裡的主執行緒就是UI執行緒,Activity由ActivityThread啟動,會呼叫ActivityThread的main函式:
//ActivityThread.java public static void main(String[] args) { //呼叫了Looper的prepareMainLooper 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")); } //呼叫了loop,開始迴圈 Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
public static void prepareMainLooper() {
prepare(false);//初始化looper,放入sThreadLocal
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
//返回和當前執行緒關聯的Looper
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
對於Looper.loop()後面會進行分析
2 一個looper,多個Handler,handler傳送的訊息利用dispatchMessage處理時如何區分
首先說明,一個handler傳送的訊息只會被自己接收,所以是可以正常處理的(就不demo演示了)
傳送訊息除了利用Handler之外還有Message,Message一般利用Obtain獲取,其實obtain中還可以傳遞引數,可以接收Message,還可以接收Handler,message有多個屬性,常用的有what,arg1,arg2,data等,其實還有一個屬性叫做target,這個target屬性就是標識handler的。
handler傳送message一般呼叫sendMessage:
Handler.java
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);
}
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);
}
//最終呼叫的函式時enqueueMessage,
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//會把this賦值給msg.target,此時target就指向當前Handler
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//之後呼叫MessageQueue的enqueueMessage分發訊息進行處理
return queue.enqueueMessage(msg, uptimeMillis);
}
最終是Looper取Messagequeue中的訊息,交給Handler處理:
Looper.java
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;
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 {
//////這裡可以很明顯的看到呼叫message.target.dispatchMessage
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();
}
}
分析完畢!