-
Handler 機(jī)制中,存在哪些角色?各自承擔(dān)了什么功能?
Handler:消息輔助類 & 對(duì)外的接口 & 向 MQ 投遞消息 & 消息的目標(biāo)處理者;
Message:消息的載體 & 被 Handler 投遞 & 自帶 Handler 處理 & 自帶消息池;
Looper:循環(huán)器 & 持有 MQ & 循環(huán)從 MQ 中獲取消息 & TLS 線程唯一;
MessageQueue:基于時(shí)間的優(yōu)先級(jí)隊(duì)列 & 鏈表結(jié)構(gòu) & Java 與 C++ 層的紐帶;
-
Looper死循環(huán)為什么不會(huì)導(dǎo)致應(yīng)用ANR
答: ANR的真正原因是單個(gè)消息處理時(shí)間過長(zhǎng),阻塞了隊(duì)列中其他關(guān)鍵消息(如界面渲染、手勢(shì)有操作)的執(zhí)行。因?yàn)楫?dāng)Looper它在空閑時(shí)會(huì)通過nativePollOnce()進(jìn)入休眠,不消耗CPU資源,當(dāng)有新的Message進(jìn)來的時(shí)候會(huì)打破阻塞繼續(xù)執(zhí)行,消息能得到繼續(xù)處理,不會(huì)有消息處理超時(shí)的情況。
Looper的死循環(huán),是循環(huán)執(zhí)行各種事務(wù),包括UI繪制事務(wù)。Looper死循環(huán)說明線程沒有死亡,如果Looper停止循環(huán),線程則結(jié)束退出了。Looper的死循環(huán)本身就是保證UI繪制任務(wù)可以被執(zhí)行的原因之一。
-
Handler的阻塞喚醒機(jī)制是怎么回事?
Message next() {
for (;;) {
// 這個(gè)native調(diào)用是關(guān)鍵:讓線程進(jìn)入TIMED_WAITING狀態(tài)
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
...
}
}
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
// ... 消息入隊(duì)邏輯
// 判斷是否需要喚醒:隊(duì)列為空、是屏障消息、或者新消息在隊(duì)首
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
當(dāng)消息不可用或者沒有消息的時(shí)候就會(huì)阻塞在next方法,會(huì)進(jìn)入nativePollOnce()方法,而阻塞的辦法是通過pipe/epoll機(jī)制,Android利用Linux的epoll機(jī)制實(shí)現(xiàn)高效的多路復(fù)用I/O,對(duì)I/O進(jìn)行了阻塞。
在enqueueMessage方法中,如果有新的消息進(jìn)入,會(huì)根據(jù)needWeak字段,調(diào)用nativeWake()方法進(jìn)行喚醒。
https://blog.csdn.net/chuyouyinghe/article/details/128198098
-
可以在子線程直接new一個(gè)Handler嗎
可以在子線程直接new一個(gè)Handler,不過需要在一個(gè)線程里需要先調(diào)用Looper.prepare()和Looper.loop()方法。
Thread {
Looper.prepare()
object : Handler() {
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
}
}
Looper.loop()
}.start()
在主線程中為什么沒看到Looper.prepare()?因?yàn)橄到y(tǒng)已經(jīng)在應(yīng)用啟動(dòng)的main方法里面調(diào)用Looper.prepareMainLooper()。Looper啟動(dòng)了handler的機(jī)制才能夠正常運(yùn)行,啟動(dòng)之前有需要prepare去創(chuàng)建Looper。如果不去調(diào)用loop()方法開啟循環(huán)讀取消息,你就算用handler傳遞了消息,沒有去取消息的呀。
public static void main(String[] args) {
Looper.prepareMainLooper();
Looper.loop();
}
handler.post(Runnable {})會(huì)做什么
將一個(gè) Runnable 對(duì)象(即一段代碼)發(fā)送到與該 Handler 關(guān)聯(lián)的線程的消息隊(duì)列中,并在該線程中異步執(zhí)行。如果目標(biāo)線程空閑:消息會(huì)被立即處理。
如果目標(biāo)線程正在處理其他消息:你的 Runnable 必須等待前面所有的消息都處理完畢后,才會(huì)輪到它執(zhí)行。
-
MessageQueue內(nèi)部是什么樣的數(shù)據(jù)結(jié)構(gòu)?
MessageQueue內(nèi)部存有Message對(duì)象,Message內(nèi)存存有一個(gè)next的Message對(duì)象,就形成了message->next->message->next ...。
那么就形成了一個(gè)單鏈表。
public final class MessageQueue {
Message mMessages;
Message next() {
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());
}
}
}
public final class Message implements Parcelable {
@UnsupportedAppUsage
/*package*/ Message next;
}
再看將消息入列的enqueueMessage方法:
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
}
}
enqueueMessage()方法里面,通過延遲時(shí)間when,去遍歷消息隊(duì)列的延遲時(shí)間,依次作比較,如果值小于某個(gè)消息的值,就會(huì)將該消息插入到這個(gè)消息前一個(gè)位置,實(shí)現(xiàn)一個(gè)節(jié)點(diǎn)的插入。為什么這里是一個(gè)隊(duì)列呢?因?yàn)槿∠⒌臅r(shí)候永遠(yuǎn)是從一個(gè)方向去取,永遠(yuǎn)是從頭部開始取,那這不就是一個(gè)隊(duì)列嗎?
又因?yàn)榘l(fā)送消息最終是sendMessageAtTime(),可以控制傳送的時(shí)間先后,將時(shí)間短的插入到先發(fā)送但是延遲時(shí)間長(zhǎng)的節(jié)點(diǎn)前面去,所以MessageQueue是一個(gè)單鏈表的優(yōu)先級(jí)隊(duì)列。所以在loop方法中queue.next()能依次取出下一個(gè)消息,就是因?yàn)殒湵淼慕Y(jié)構(gòu)。

-
Hander機(jī)制采用的是什么設(shè)計(jì)模式?

采用的是生產(chǎn)者/消費(fèi)者的設(shè)計(jì)模式。子線程生產(chǎn)消息,主線程消費(fèi)消息,MessageQueue為生產(chǎn)倉(cāng)庫(kù)。
-
Handler是怎么實(shí)現(xiàn)切換線程的?
var handler = object: Handler() {
override fun handleMessage(msg: Message) {
Log.i("minfo", "從子線程收到消息 ${msg.what}")
}
}
object : Thread() {
override fun run() {
handler.sendEmptyMessage(200)
}
}
在一個(gè)線程中創(chuàng)建handler,然后在另一個(gè)創(chuàng)建的線程中調(diào)用該handler的發(fā)送消息,該handler中能接收到消息,即實(shí)現(xiàn)了線程間通信,那么handler是如何實(shí)現(xiàn)線程切換的呢?
當(dāng)在A線程中創(chuàng)建handler的時(shí)候,同時(shí)創(chuàng)建了Looper與MessageQueue,Looper在A線程中調(diào)用loop進(jìn)入一個(gè)無限的for循環(huán)從MessageQueue中取消息。當(dāng)B線程調(diào)用handler發(fā)送一個(gè)message的時(shí)候,會(huì)通過msg.target.dispatchMessage(msg);將message插入到handler對(duì)應(yīng)的MessageQueue中,Looper發(fā)現(xiàn)有message插入到MessageQueue中,便取出message執(zhí)行相應(yīng)的邏輯,因?yàn)長(zhǎng)ooper.loop()是在A線程中啟動(dòng)的,所以則回到了A線程,達(dá)到了從B線程切換到A線程的目的。
-
handler.sendMessage()與handler.post()的區(qū)別?
1、如果msg.callback不為空,也就是通過post方法發(fā)送消息的時(shí)候,會(huì)把消息交給這個(gè)msg.callback進(jìn)行處理,然后就沒有后續(xù)了。
2、如果msg.callback為空,也就是通過sendMessage發(fā)送消息的時(shí)候,會(huì)判斷Handler當(dāng)前的mCallback是否為空,如果不為空就交給Handler.Callback.handleMessage處理。
所以post(Runnable) 與 sendMessage的區(qū)別就在于后續(xù)消息的處理方式,是交給msg.callback還是 Handler.Callback或者Handler.handleMessage。
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
-
MessageQueue是怎么增刪消息的?
msg.next = p; // invariant: p == prev.next
prev.next = msg;
添加消息:在enqueMessage方法中,將該條msg按照時(shí)間大小,遍歷鏈表到正確的位置,將該msg插入到該節(jié)點(diǎn)中。
msg.next = null;
msg.markInUse();
return msg;
void markInUse() {
flags |= FLAG_IN_USE;
}
刪除消息:在next方法中,將該msg對(duì)象的next指向null,并將flags標(biāo)識(shí)為FLAG_IN_USE。
-
一個(gè)線程可以有幾個(gè)Handler?幾個(gè)Looper?幾個(gè)MessageQueue?
一個(gè)線程可以有幾個(gè)Handler?
可以創(chuàng)建無數(shù)個(gè)Handler,但是他們使用的消息隊(duì)列都是同一個(gè),也就是同一個(gè)Looper。Handler在哪個(gè)線程創(chuàng)建的,就跟哪個(gè)線程的Looper關(guān)聯(lián),也可以在Handler的構(gòu)造方法中傳入指定的Looper,Looper.loop()循環(huán)讀取消息。
那同一個(gè)Looper是怎么區(qū)分不同的Handler的?因?yàn)樵趍sg入隊(duì)列時(shí),會(huì)將msg.target設(shè)置一個(gè)handler,處理消息的時(shí)候,也會(huì)調(diào)用msg對(duì)象的target去處理消息。
一個(gè)線程有幾個(gè)looper?如何保證的?
Looper類中部分代碼:
final MessageQueue mQueue;
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<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));
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
從Looper的源碼中可以看到,在一個(gè)線程中初始化Looper,是用ThreadLocal存儲(chǔ)Looper對(duì)象。同時(shí)保證一個(gè)線程只擁有一個(gè)Looper,存入looper之前,從sThreadLocal.get()中取看是否存過looper, sThreadLocal中如果存在則會(huì)拋出異常,保證一個(gè)線程中的looper實(shí)例唯一,且一旦存入,不可修改不可新增。
幾個(gè)MessageQueue
一個(gè)線程的looper只會(huì)創(chuàng)建一次,只有一個(gè)looper對(duì)象,一個(gè)looper下只有一個(gè)MessageQueue屬性,所以一個(gè)線程只有一個(gè)MessageQueue。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
-
A Handler發(fā)送的消息為什么不會(huì)跑到B Handler的handleMessage()方法中?
一個(gè)線程中包含多個(gè)Handler對(duì)象時(shí),在消息的添加分發(fā)時(shí),通過Message的target(Handler)標(biāo)注,處理消息會(huì)去除對(duì)應(yīng)的target去調(diào)用handleMessage,所以不會(huì)錯(cuò)亂。
-
ThreadLoacal的原理?
這里見我的另一篇ThreadLoacal的原理
-
在UI中創(chuàng)建的Handler,通過post方式發(fā)送的消息在run方法中可以進(jìn)行UI更新嗎?
在消息處理結(jié)束后,其回調(diào)至Runnable 的run方法中,需要注意的是這里的仍然是在UI線程中,因?yàn)槲覀儎?chuàng)建的Handler是在UI線程中,且Handler將Runnable內(nèi)部封住成Message的形式,所以最終調(diào)用的是Runable中的run()方法,因此還是回到UI線程中,可以更新UI界面。
-
主線程為什么不用初始化Looper?
Android程序的入口在ActivityThread的main方法中,在main方法中已經(jīng)調(diào)用了prepareMainLooper()去創(chuàng)建looper對(duì)象了,并且調(diào)用了 Looper.loop()。如果是自己創(chuàng)建的子線程中,需要自己初始化looper并調(diào)用loop方法。ActivityThread 不繼承自 Thread,它只是一個(gè)運(yùn)行在主線程上的對(duì)象。
public static void main(String[] args) {
...
// 初始化主線程Looper
Looper.prepareMainLooper();
...
// 新建一個(gè)ActivityThread對(duì)象
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
// 獲取ActivityThread的Handler,也是他的內(nèi)部類H
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
...
Looper.loop();
}
-
Handler如何保證MessageQueue并發(fā)訪問安全?
各個(gè)線程都往一個(gè)messageQueue中存取msg,在存取數(shù)據(jù)的時(shí)候,是通過synchronized來保證了線程的安全性,使用messageQueue作為對(duì)象鎖。各個(gè)子線程和主線程都是往用一個(gè)messageQueue存取消息,對(duì)調(diào)用同一個(gè)MessageQueue對(duì)象的線程來說,它們都是互斥的,所以保證了并發(fā)訪問安全。
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
......
}
}
Message next() {
synchronized (this) {
......
}
}
-
能不能讓一個(gè)Message加急被處理?
可以 / 一種使得異步消息可以被更快處理的機(jī)制。
而看了前面 MessageQueue::next 的代碼我們知道,當(dāng) MessageQueue 中遇到了一個(gè)同步屏障,則它會(huì)不斷地忽略后面的同步消息直到遇到一個(gè)異步的消息,這樣設(shè)計(jì)的目的其實(shí)是為了使得當(dāng)隊(duì)列中遇到同步屏障時(shí),則會(huì)使得異步的消息優(yōu)先執(zhí)行,這樣就可以使得一些消息優(yōu)先執(zhí)行。
Message next() {
for (;;) {
if (msg != null && msg.target == null) {
// 同步屏障,找到下一個(gè)異步消息
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
}
}
-
Message 的同步屏障有什么用?有什么意義?如何發(fā)送一個(gè)同步屏障?
在 Handler 中還存在了一種特殊的消息,它的 target 為 null,并不會(huì)被消費(fèi),僅僅是作為一個(gè)標(biāo)識(shí)處于 MessageQueue 中。它就是 SyncBarrier (同步屏障)這種特殊的消息。
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
移除同步屏障
public void removeSyncBarrier(int token) {
...
}
比如 View 的繪制過程中的 TraversalRunnable 消息就是異步消息,在放入隊(duì)列之前先放入了一個(gè)消息屏障,從而使得界面繪制的消息會(huì)比其他消息優(yōu)先執(zhí)行,避免了因?yàn)?MessageQueue 中消息太多導(dǎo)致繪制消息被阻塞導(dǎo)致畫面卡頓,當(dāng)繪制完成后,就會(huì)將消息屏障移除。
-
同步屏障的使用場(chǎng)景
貌似在開發(fā)的過程中,我們很少用到同步屏障,那么源碼中在哪里用到了?
Android中的UI消息就是異步消息,需要優(yōu)先處理 比如View更新,調(diào)用onDraw、requestLayout、invalidate等view最終都會(huì)調(diào)用到ViewRootImpl類中的scheduleTraversals方法。
void scheduleTraversals(){
if(!mTraversalScheduled){
mTraversalScheduled=true;
//開啟同步屏障
mTraversalBarrier=mHandler.getLooper().getQueue().postSyncBarrier();
//發(fā)送異步消息
mChoreographer.postCallback();
...
}
}
這里開啟了同步屏障,并發(fā)送了異步消息,由于UI更顯相關(guān)的消息是優(yōu)先級(jí)最高的,這樣系統(tǒng)就會(huì)優(yōu)先處理這些異步消息了。
當(dāng)然處理完消息后要移除同步屏障,這個(gè)時(shí)候就調(diào)用到了ViewRootImpl#unscheduleTraversals()。
-
什么是異步消息?如何發(fā)送
意義:需配合同步屏障使用,否者與同步消息無區(qū)別;
異步消息:setAsynchronous(true) → 向 flags 添加 FLAG_ASYNCHRONOUS 標(biāo)記
發(fā)送方式 通過異步 Handler 發(fā)送 → 構(gòu)造 Handler 時(shí),async 傳遞 true 發(fā)送消息前,主動(dòng)調(diào)用 setAsynchronous(true)
安全起見,Android 9.0 普通開發(fā)者無法使用異步消息,所有發(fā)送方式被標(biāo)記為 @hide
將handler標(biāo)識(shí)標(biāo)位異步handler,該handler就發(fā)送異步消息。
public void setAsynchronous(boolean async) {
if (async) {
flags |= FLAG_ASYNCHRONOUS;
} else {
flags &= ~FLAG_ASYNCHRONOUS;
}
}
-
Handler 里藏著的 Callback 能干什么?
Handler.Callback 有優(yōu)先處理消息的權(quán)利 ,當(dāng)一條消息被 Callback 處理并攔截
(返回 true),那么 Handler 的 handleMessage(msg) 方法就不會(huì)被調(diào)用了;
如果 Callback 處理了消息,但是并沒有攔截,那么就意味著一個(gè)消息可以同時(shí)
被 Callback 以及 Handler 處理。
-
什么是IdleHandler?
當(dāng)MessageQueue沒有消息的時(shí)候,就會(huì)阻塞在next方法中,其實(shí)在阻塞之前,MessageQueue還會(huì)做一件事,就是檢查是否存在IdleHandler,如果有,就會(huì)去執(zhí)行它的queueIdle方法。
當(dāng)沒有消息處理的時(shí)候,就會(huì)去處理這個(gè)mIdleHandlers集合里面的每個(gè)IdleHandler對(duì)象,并調(diào)用其queueIdle方法。 最后根據(jù)queueIdle返回值判斷是否用完刪除當(dāng)前的IdleHandler。
private IdleHandler[] mPendingIdleHandlers;
Message next() {
int pendingIdleHandlerCount = -1;
for (;;) {
synchronized (this) {
//當(dāng)消息執(zhí)行完畢,就設(shè)置pendingIdleHandlerCount
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
//初始化mPendingIdleHandlers
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
//mIdleHandlers轉(zhuǎn)為數(shù)組
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 遍歷數(shù)組,處理每個(gè)IdleHandler
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);
}
//如果queueIdle方法返回false,則處理完就刪除這個(gè)IdleHandler
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
}
}
-
IntentService是啥?有什么使用場(chǎng)景?
public abstract class IntentService extends Service {
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
@Override
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
這就是一個(gè)可以在子線程進(jìn)行耗時(shí)任務(wù),并且在任務(wù)執(zhí)行后調(diào)用stopSelf()自動(dòng)停止的Service。
-
HandlerThread是啥?有什么使用場(chǎng)景?
public class HandlerThread extends Thread {
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
}
HandlerThread就是一個(gè)封裝了Looper的Thread類。就是為了讓我們?cè)谧泳€程里面更方便的使用Handler。
-
Handler 分發(fā)事件優(yōu)先級(jí),是否可攔截?攔截的優(yōu)先級(jí)如何?
可以統(tǒng)一攔截消息,但無法攔截通過Runnable通過getPostMessage(Runnable r)生成的Message。
因?yàn)樗黰sg.callback不為空會(huì)優(yōu)先處理msg.callback,不會(huì)經(jīng)過統(tǒng)一的Hanlder的mCallback。
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
// mCallback 處理完如果返回 false,還是會(huì)繼續(xù)往下走,再交給 Handler.handleMessage 處理的
// 所以這邊可以通過反射去 hook 一個(gè) Handler ,可以監(jiān)聽 Handler 處理的每個(gè)消息,也可以改 msg 里面的值
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
-
主線程 Looper 何時(shí)運(yùn)行?
App 啟動(dòng)時(shí),會(huì)調(diào)用到 ActivityThread 中,Looper 就在其 main() 方法中被啟動(dòng);main() 中會(huì)主動(dòng)調(diào)用 Looper.prepareMainLooper() 和 Looper.loop()
-
Handler 的 Message 可以分為那 3 類?分別有什么標(biāo)識(shí)?
1.發(fā)送普通消息
2.發(fā)送異步消息
給msg對(duì)象加入設(shè)置異步的標(biāo)識(shí)。
public void setAsynchronous(boolean async) {
if (async) {
flags |= FLAG_ASYNCHRONOUS;
} else {
flags &= ~FLAG_ASYNCHRONOUS;
}
}
3.同步屏障
使用postSyncBarrier方法發(fā)送同步屏障,使msg.target == null。
private int postSyncBarrier(long when) {
...
}
-
點(diǎn)擊頁(yè)面上的按鈕后更新TextView的內(nèi)容,談?wù)勀愕睦斫猓?/h4>
點(diǎn)擊按鈕的時(shí)候會(huì)發(fā)送消息到Handler,但是為了保證優(yōu)先執(zhí)行,會(huì)加一個(gè)標(biāo)記異步,同時(shí)會(huì)發(fā)送一個(gè)target為null的消息,這樣在使用消息隊(duì)列的next獲取消息的時(shí)候,如果發(fā)現(xiàn)消息的target為null,那么會(huì)遍歷消息隊(duì)列將有異步標(biāo)記的消息獲取出來優(yōu)先執(zhí)行,執(zhí)行完之后會(huì)將target為null的消息移除。(同步屏障)
-
同一個(gè) Message 對(duì)象能否重復(fù) send?
關(guān)鍵在于如何定義同一個(gè) Message。
角度一:Java 對(duì)象層面,可被復(fù)用;
原因:Message 由消息池維護(hù),即同一個(gè)對(duì)象被回收后會(huì)被再次復(fù)用;| new Message & Message.obtain()
角度二:業(yè)務(wù)層面,不能復(fù)用;
原因:Message 通過 enqueueMessage() 入隊(duì)時(shí),會(huì)通過 markInUse() 標(biāo)記,再次入隊(duì)無法通過 isInUse() 檢查,則拋出異常;
-
Looper.loop 中,如果沒有待處理的消息,為什么不會(huì)阻塞 UI?
主線程在 MessageQueue 沒有消息時(shí),會(huì)阻塞在 loop 的 queue.next() 方法中的 nativePollOnce()方法里。
此時(shí)主線程會(huì)釋放 CPU 資源進(jìn)入休眠狀態(tài),直到下一個(gè)消息到達(dá)或者有事務(wù)發(fā)生,通過往 pipe 管道寫端寫入數(shù)據(jù)的方式,來喚醒主線程。這里采用的是 epoll 機(jī)制。
epoll 機(jī)制是一種 IO 多路復(fù)用機(jī)制,可以同時(shí)監(jiān)控多個(gè)描述符,在有事件發(fā)生的時(shí)候,立即通知相應(yīng)程序進(jìn)行讀或?qū)懖僮?,類似一種 callback 的回調(diào)機(jī)制。主線程在大多數(shù)時(shí)候是處于休眠狀態(tài),并不會(huì)消耗大量的 CPU 資源。當(dāng)有新的消息或事務(wù)到達(dá)時(shí),會(huì)立即喚醒主線程進(jìn)行處理,所以對(duì)用戶來說是無感知的。
-
Looper 的 Printer 輸出的日志,有什么其他用途?依靠的原理是什么?有什么缺點(diǎn)?
用途:性能監(jiān)控;
原理:通過篩選日志內(nèi)存,區(qū)分 Message 的開始執(zhí)行和結(jié)束執(zhí)行的時(shí)間點(diǎn),即可判斷處理 Message 的耗時(shí),即主線程卡頓耗時(shí);
缺點(diǎn):Printer 存在大量字符串拼接,在消息量大時(shí),會(huì)導(dǎo)致性能受損;| 實(shí)測(cè)數(shù)據(jù):存在 Printer 時(shí),在列表快速滑動(dòng)時(shí),平均幀率降低 5 幀;
-
Handler 可以 IPC 通信嗎?
不能;Handler是一種共享內(nèi)存的通信方式,Handler 只能用于共享內(nèi)存地址的 2 個(gè)線程通信,即同進(jìn)程的 2 個(gè)線程通信;
-
為什么系統(tǒng)不建議在子線程訪問UI?(為什么不能在子線程更新UI?)
如果采用多線程訪問UI會(huì)出現(xiàn)線程安全,那為什么不加鎖呢?
加鎖會(huì)降低UI訪問的效率。本身UI控件就是離用戶比較近的一個(gè)組件,加鎖之后自然會(huì)發(fā)生阻塞,那么UI訪問的效率會(huì)降低,最終反應(yīng)到用戶端就是這個(gè)手機(jī)有點(diǎn)卡。
所以,Android設(shè)計(jì)出了單線程模型來處理UI操作,再搭配上Handler,是一個(gè)比較合適的解決方案。
-
子線程訪問UI的 崩潰原因 和 解決辦法?
1.在ViewRootImpl創(chuàng)建之前進(jìn)行子線程的UI更新,比如onCreate方法中進(jìn)行子線程更新UI。
2.子線程切換到主線程進(jìn)行UI更新,比如Handler、view.post方法。
3.給操作的view設(shè)置大小為matchparent或者
-
Message可以如何創(chuàng)建?哪種效果更好,為什么?
Message.obtain來創(chuàng)建Message,這樣會(huì)復(fù)用之前的Message的內(nèi)存,不會(huì)頻繁的創(chuàng)建對(duì)象,導(dǎo)致內(nèi)存抖動(dòng)。
-
Message消息被分發(fā)之后會(huì)怎么處理?消息怎么復(fù)用的?
在一個(gè)message對(duì)象調(diào)用了dispatchMessage之后,會(huì)進(jìn)行回收操作。
在recycleUnchecked方法中,釋放了所有資源,然后將當(dāng)前的空消息插入到sPool表頭。
這里的sPool就是一個(gè)消息對(duì)象池,它也是一個(gè)鏈表結(jié)構(gòu)的消息,最大長(zhǎng)度為50。
使用obtain來獲取復(fù)用消息,直接復(fù)用消息池sPool中的第一條消息,然后sPool指向下一個(gè)節(jié)點(diǎn),消息池?cái)?shù)量減一。
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = UID_NONE;
workSourceUid = UID_NONE;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
-
主線程中能否調(diào)用quit()方法?
是不能的。它會(huì)拋出一個(gè)異常,讓程序掛掉。
-
修改了手機(jī)系統(tǒng)時(shí)間,handler的延時(shí)消息收到也會(huì)發(fā)生改變嗎
發(fā)送延時(shí)消息,會(huì)調(diào)用sendMessageDelayed,然后調(diào)用sendMessageAtTime,將一個(gè)時(shí)間點(diǎn)傳入,該時(shí)間為SystemClock.uptimeMillis()
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
/**
* Returns milliseconds since boot, not counting time spent in deep sleep.
*
* @return milliseconds of non-sleep uptime since boot.
*/
@CriticalNative
native public static long uptimeMillis();
SystemClock.uptimeMillis() 是一個(gè)表示當(dāng)前時(shí)間的一個(gè)相對(duì)時(shí)間,它代表的是 自系統(tǒng)啟動(dòng)開始從0開始的到調(diào)用該方法時(shí)相差的毫秒數(shù)
System.currentTimeMillis() 代表的是從 1970-01-01 00:00:00 到當(dāng)前時(shí)間的毫秒數(shù),我們可以通過修改系統(tǒng)時(shí)間達(dá)到修改該值的目的,所以該值是不可靠的值
-
handler怎么移除一個(gè)消息
通過調(diào)用:
Handler().removeMessages(what: Int)
調(diào)用了handler中:
public final void removeMessages(int what) {
mQueue.removeMessages(this, what, null);
}
進(jìn)入removeMessages方法:
void removeMessages(Handler h, Runnable r, Object object) {
if (h == null || r == null) {
return;
}
synchronized (this) {
Message p = mMessages;
// Remove all messages at front.
while (p != null && p.target == h && p.callback == r
&& (object == null || p.obj == object)) {
Message n = p.next;
mMessages = n;
p.recycleUnchecked();
p = n;
}
// Remove all messages after front.
while (p != null) {
Message n = p.next;
if (n != null) {
if (n.target == h && n.callback == r
&& (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
p.next = nn;
continue;
}
}
p = n;
}
}
}
removeMessages會(huì)將handler對(duì)應(yīng)message queue里的消息清空,如果帶了int參數(shù)則是對(duì)應(yīng)的消息清空。1、這個(gè)方法使用的前提是之前調(diào)用過sendEmptyMessageDelayed(0, time),意思是延遲time執(zhí)行handler中msg.what=0的方法;
2、在延遲時(shí)間未到的前提下,執(zhí)行removeMessages(0),則上面的handler中msg.what=0的方法取消執(zhí)行;
3、在延遲時(shí)間已到,handler中msg.what=0的方法已執(zhí)行,再執(zhí)行removeMessages(0),不起作用。
-
handler.removeCallbacksAndMessages(null)是什么意思
如果需要?jiǎng)h除handler所有的消息和回調(diào)函數(shù),那就需要使用handler.removeCallbacksAndMessages(null)。
這樣做的好處是在Acticity退出的時(shí)候,可以避免內(nèi)存泄露,因?yàn)橛醒舆t消息,msg持有handler,handler持有activity,activity退出需要移除所有消息。
會(huì)while循環(huán)遍歷msg消息鏈表,調(diào)用msg的recycleUnchecked()進(jìn)行回收,方法會(huì)將msg所有屬性置空和置0。
所以,防止handler內(nèi)存泄露,一種解決方案是static+弱引用,另一種方案就是在activity的onDestroy中調(diào)用handler.removeCallbacksAndMessages方法。
參考:
https://blog.csdn.net/javine/article/details/45953575
https://blog.csdn.net/bzlj2912009596/article/details/79736912
http://m.itdecent.cn/p/7f1c46fb55c8
https://blog.csdn.net/qq_39477770/article/details/109331658?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-1&spm=1001.2101.3001.4242
https://blog.csdn.net/u012165769/article/details/114681388?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-0&spm=1001.2101.3001.4242
https://blog.csdn.net/u012165769/article/details/113531570
https://blog.csdn.net/weixin_39952074/article/details/111249137
說一下Handler的同步屏障機(jī)制?