我們都知道ActivityThread是程序的入口,以下是App入口處的部分代碼
// 創(chuàng)建主線程Looper對象
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
// 創(chuàng)建主線程Handler
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);
//開始消息循環(huán)
Looper.loop();
我們先來看主線程Looper消息循環(huán)的創(chuàng)建過程
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對象真正創(chuàng)建的地方践樱,可以看到傳遞了一個quitAllowed值相寇,重字面意思上來看是否允許退出获三,而prepareMainLooper方法里傳遞的是false, 因此主線程的Looper是不允許退出的弄唧,然后同時把創(chuàng)建的Looper對象跟線程的sThreadLocal綁定袜瞬,同時可以看到判空處理每個線程只能持有一個Looper對象冲杀,并且每個線程之前的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));
}
上面prepareMainLooper方法還調(diào)用了myLooper方法來給Looper賦值
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
public static @NonNull MessageQueue myQueue() {
return myLooper().mQueue;
}
// Looper 對象持有了對應(yīng)線程的引用,同時內(nèi)部維護了一個單鏈表的消息隊列
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
寫到這权谁,我們簡單總結(jié)下結(jié)論剩檀,一個線程只能有一個Looper對象,每個Looper對象內(nèi)部只有一個單鏈表的消息隊列(MessageQueue).
我們再來看下Handler類
// handler持有了所在Looper的引用
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
// 快捷方式獲取一個跟主線程關(guān)聯(lián)的Handler
/** @hide */
@NonNull
public static Handler getMain() {
if (MAIN_THREAD_HANDLER == null) {
MAIN_THREAD_HANDLER = new Handler(Looper.getMainLooper());
}
return MAIN_THREAD_HANDLER;
}
實際開發(fā)中的使用場景
1旺芽、在其他線程想操作UI
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
// 把消息發(fā)送到主線程的Looper中沪猴,因此 handlerMessage()方法也會在主線程中執(zhí)行
Handler handler = new Handler(Loop.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
Looper.loop();
}
}).start();
- 消息發(fā)送到當前線程的Looper中處理辐啄,因此為了方便使用,系統(tǒng)提供了HandlerThread類簡化代碼运嗜,方便開發(fā)者壶辜。
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
Looper.loop();
}
}).start();
-
UI線程延時更新等
如果在主線程中通過new Handler()方式創(chuàng)建Handler的對象handler,那么handler對象中持有的Looper是主線程的Looper因此handler對象中持有的MessageQueue隊列也是主線程的消息隊列担租,因此handler對象發(fā)送的消息也都是發(fā)送到主線程的消息隊列中了砸民。
Q: 如何保證誰(handler)發(fā)送的消息誰來處理?
A:Handler發(fā)送消息到MessageQueue之后會把Message的target屬性設(shè)置為Handler自身奋救,正如Looper取出消息處理消息的處理一致
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);
}
}
Q: 為什么在非UI線程中直接創(chuàng)建Handler對象會崩潰阱洪?
public Handler(Callback callback, boolean async) {
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());
}
}
// 獲取當前線程的Looper, 如果是非UI線程默認是沒有Looper對象的因此, 直接創(chuàng)建Handler必然會崩潰, 所以需要主動調(diào)用Looper.prepare(), Looper.loop();
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;
}