一.系統(tǒng)為什么不允許子線程訪問(wèn)UI線程:
這是因?yàn)閁I線程里面的控件都是非線程安全的孵户,如果在多線程并發(fā)訪問(wèn)可能會(huì)導(dǎo)致UI控件處于不可預(yù)期的狀態(tài)熟嫩。那么為什么不給控件訪問(wèn)加上鎖呢?首先,加鎖之后會(huì)導(dǎo)致訪問(wèn)邏輯變得復(fù)雜式矫,其次鎖機(jī)制會(huì)降低UI訪問(wèn)的效率乡摹,因?yàn)殒i機(jī)制會(huì)阻塞某些線程的執(zhí)行役耕。
二.消息機(jī)制的幾個(gè)重要對(duì)象:
Handler 發(fā)送和接收消息
Looper 用于輪詢消息隊(duì)列,一個(gè)線程只能有一個(gè)Looper
Message 消息實(shí)體
MessageQueue 消息隊(duì)列聪廉,存放消息實(shí)體的載體
三.創(chuàng)建Looper
在ActivityThread中的main方法中為我們prepare了
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
//其他代碼省略...
Looper.prepareMainLooper(); //初始化Looper以及MessageQueue
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(); //開(kāi)始輪循操作
throw new RuntimeException("Main thread loop unexpectedly exited");
}
Looper.prepareMainLooper();
public static void prepareMainLooper() {
prepare(false);//消息隊(duì)列不可以quit
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
prepare有兩個(gè)重載的方法瞬痘,主要看 prepare(boolean quitAllowed) quitAllowed的作用是在創(chuàng)建MessageQueue時(shí)標(biāo)識(shí)消息隊(duì)列是否可以銷(xiāo)毀, 主線程不可被銷(xiāo)毀 下面有介紹
public static void prepare() {
prepare(true);//消息隊(duì)列可以quit
}
//quitAllowed 主要
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {//不為空表示當(dāng)前線程已經(jīng)創(chuàng)建了Looper
throw new RuntimeException("Only one Looper may be created per thread");
//每個(gè)線程只能創(chuàng)建一個(gè)Looper
}
sThreadLocal.set(new Looper(quitAllowed));//創(chuàng)建Looper并設(shè)置給sThreadLocal板熊,這樣get的時(shí)候就不會(huì)為null了
}
創(chuàng)建MessageQueue以及Looper與當(dāng)前線程的綁定
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);//創(chuàng)建了MessageQueue
mThread = Thread.currentThread(); //當(dāng)前線程的綁定
}
MessageQueue的構(gòu)造方法
MessageQueue(boolean quitAllowed) {
//mQuitAllowed決定隊(duì)列是否可以銷(xiāo)毀 主線程的隊(duì)列不可以被銷(xiāo)毀需要傳入false, 在MessageQueue的quit()方法就不貼源碼了
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
Looper.loop()
同時(shí)是在main方法中 Looper.prepareMainLooper() 后Looper.loop(); 開(kāi)始輪詢
public static void loop() {
final Looper me = myLooper();//里面調(diào)用了sThreadLocal.get()獲得剛才創(chuàng)建的Looper對(duì)象
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}//如果Looper為空則會(huì)拋出異常
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 (;;) {
//這是一個(gè)死循環(huán)框全,從消息隊(duì)列不斷的取消息
Message msg = queue.next(); // might block
if (msg == null) {
//由于剛創(chuàng)建MessageQueue就開(kāi)始輪詢,隊(duì)列里是沒(méi)有消息的,等到Handler sendMessage enqueueMessage后
//隊(duì)列里才有消息
// 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
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);//msg.target就是綁定的Handler干签,詳見(jiàn)后面Message的部分津辩,Handler開(kāi)始
//后面代碼省略.....
msg.recycleUnchecked();
}
}
四.創(chuàng)建Handler
最常見(jiàn)的創(chuàng)建handler
Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
在內(nèi)部調(diào)用 this(null, false);
public Handler(Callback callback, boolean async) {
//前面省略
mLooper = Looper.myLooper();//獲取Looper,**注意不是創(chuàng)建Looper**容劳!
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;//獲得消息隊(duì)列MessageQueue
mCallback = callback; //初始化了回調(diào)接口
mAsynchronous = async;
}
Looper.myLooper()喘沿;
//這是Handler中定義的ThreadLocal ThreadLocal主要解多線程并發(fā)的問(wèn)題
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
五.Handler消息機(jī)制整個(gè)流程
1.handler內(nèi)部與Looper關(guān)聯(lián),handler->Looper->MessageQueue,handler發(fā)送消息就是向MessageQueue隊(duì)列發(fā)送消息竭贩。
2.Looper通過(guò)Looper.loop()不斷循環(huán)的方法并把消息(通過(guò)先進(jìn)先出的順序)通過(guò)dispatchMessage方法回傳給handler自己蚜印。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {//callback在message的構(gòu)造方法中初始化或者使用handler.post(Runnable)時(shí)候才不為空
handleCallback(msg);
} else {
if (mCallback != null) {//mCallback是一個(gè)Callback對(duì)象,通過(guò)無(wú)參的構(gòu)造方法創(chuàng)建出來(lái)的handler留量,該屬性為null窄赋,此段不執(zhí)行
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);//最終執(zhí)行handleMessage方法
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
注意:主線程中的Looper.loop()一直無(wú)限循環(huán)為什么不會(huì)造成ANR?
也許講到這里楼熄,很多人已經(jīng)知道原因了吧忆绰!不過(guò)習(xí)慣使然,我還是要總結(jié)一下可岂。主線程Looper從消息隊(duì)列讀取消息较木,當(dāng)讀完所有消息時(shí),主線程阻塞青柄。子線程往消息隊(duì)列發(fā)送消息伐债,并且往管道文件寫(xiě)數(shù)據(jù),主線程即被喚醒致开,從管道文件讀取數(shù)據(jù)峰锁,主線程被喚醒只是為了讀取消息,當(dāng)消息讀取完畢双戳,再次睡眠虹蒋。因此loop的循環(huán)并不會(huì)對(duì)CPU性能有過(guò)多的消耗。