幾個(gè)關(guān)于Handler 的思考問(wèn)題
Handler 消息管理機(jī)制,如何管理事務(wù)?
Handler在線程間如何通信嫌术?
Handler 內(nèi)存共享方案
架構(gòu)簡(jiǎn)圖
->「主線程,消費(fèi)者」queue.next
[Message] <--------[Looper] ===>[dispatchMessage]===>[handleMessage]
[Message]
[Message]
[Message]
[Message]
.
.
.
[Message] <-----queue.enqueueMesage-----[Handler]
->「子線程栅盲,生產(chǎn)者」
我們使用時(shí)候的過(guò)程
子線程(bean)-> 主線程(顯示)
涉及到的知識(shí)點(diǎn)
ActiviyThread->AMS 流程
ActivitThead 里面的main()方法
我們的應(yīng)用啟動(dòng)過(guò)程
Launcher(app):zygote->jvm->ActivityThread.main()==>[Looper.prepareMainLooper() ------looper.loop]
設(shè)計(jì)思路迄本,handler 的message消息隊(duì)列為什么不阻塞拇囊?
這是因?yàn)檎麄€(gè)系統(tǒng)都再用handler 的message機(jī)制。如果阻塞會(huì)產(chǎn)生手機(jī)卡死的情況炭序。
設(shè)計(jì)模式,是一種生產(chǎn)折消費(fèi)者常用模式扫俺,生產(chǎn)--->緩存池----->消費(fèi)
生產(chǎn)者,通過(guò)enqueueMessage ----->[MessageQueue 倉(cāng)庫(kù)]-----> next() 取出消息柠贤,消費(fèi)者速缆。
異步消息,同步消息
看完這個(gè)鏈接朗鸠,或者看完本文
消息屏障/handlerThread IntentService
看完這個(gè)鏈接蹬竖,或者看完本文
參考掘金: https://juejin.im/post/6844903910113705998
MessageQueue特殊情況
兩個(gè)方面的阻塞:
- 消息沒(méi)到執(zhí)行時(shí)刻,會(huì)根據(jù)when 阻塞流酬,計(jì)算nextpollTimeout---nativePollOnce
自動(dòng)喚醒
2.消息隊(duì)列為空的時(shí)候 nativePollOnce---ptr -nativeInt()
無(wú)限等待狀態(tài)芽腾。
當(dāng)其他消息進(jìn)來(lái)就會(huì)喚醒
源碼分析
開(kāi)始
使用的入口
Hndler=>
「無(wú)論調(diào)用那個(gè)postAtTime 最后會(huì)執(zhí)行」
「無(wú)論調(diào)用那個(gè)sendMessage最后會(huì)執(zhí)行」
=>sendMessageDelayed(Message msg, long delayMillis)
=> sendMessageAtTime(Message msg, long uptimeMillis)
「uptimeMills====SystemClock.uptimeMillis() + delayMillis 系統(tǒng)時(shí)間+我們傳入的延時(shí)時(shí)間 呻袭,在那個(gè)時(shí)間點(diǎn)執(zhí)行」
=>enqueueMessage(queue, msg, uptimeMillis)
public boolean sendMessageAtTime(@NonNull 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);
}
傳送帶功能
MessageQueue跟隨Looper 順帶創(chuàng)建的
MessageQueue數(shù)據(jù)結(jié)構(gòu)
單鏈表實(shí)現(xiàn)的優(yōu)先級(jí)隊(duì)列(根據(jù)時(shí)間條件優(yōu)先級(jí)判斷)
根據(jù)時(shí)間優(yōu)先級(jí),是一個(gè)插入排序算法,優(yōu)先級(jí)隊(duì)列是取消息是從隊(duì)列頭開(kāi)始弹沽,
MessageQueue 持有 (Message mMessages;)
鏈表 Message->next->Message->next
Message => 持有(Handler target;)注意這個(gè)是Message 持有target相當(dāng)于Handler自身this檀夹,由于消息發(fā)送可能延時(shí),我們Activity聲明周期結(jié)束策橘,但是內(nèi)部類持有外部引用炸渡,造成內(nèi)存泄露的原因之一
MessageQueue 的 mQuitting默認(rèn)false調(diào)用quit ---true
quit清空消息,調(diào)用本地nativeWake喚醒
下面的Looper
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);
}
}
MessageQueue 的 synchronized (this)
內(nèi)置鎖保證安全
一個(gè)線程只有一個(gè)MessageQueue丽已,而且MessageQueue還有內(nèi)置對(duì)象鎖所以保證唯一
boolean enqueueMessage(Message msg, long when) {
->「注意這里target」
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
->「注意這里mQuitting」
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
->「如果消息隊(duì)列有消息蚌堵,判斷節(jié)點(diǎn)p,根據(jù)我要加入消息when比p時(shí)間早,輪詢判斷進(jìn)行插入」
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 {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
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;
}
->「當(dāng)消息為空的時(shí)候吼畏,再次來(lái)其他消息督赤,需要喚醒」
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
輪詢?nèi)∠?/p>
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
->「nativePollOnce 承接上文喚醒隊(duì)列」
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
->「 如果target==null,那么它就是屏障宫仗,需要循環(huán)遍歷,一直往后找到第一個(gè)異步的消息
」
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());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
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);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
我們有很多native方法旁仿。系統(tǒng)實(shí)現(xiàn)了很多JNI層
nativePollOnce(睡眠)/nativeWake 喚醒操作
底層調(diào)用epoll方法藕夫,使線程阻塞
private native static long nativeInit();
private native static void nativeDestroy(long ptr);
@UnsupportedAppUsage
private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
private native static void nativeWake(long ptr);
private native static boolean nativeIsPolling(long ptr);
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
動(dòng)力
Looper 相關(guān)
-> Looper 的初始化
構(gòu)造函數(shù)是私有的,不能隨便創(chuàng)建
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
持有一個(gè)引用final MessageQueue mQueue;
我們的MessageQueue 就是在這里順帶創(chuàng)建的
在prepare里面進(jìn)行的初始化
private static void prepare(boolean quitAllowed) {
->「注意承接上面枯冈,保證唯一行threadLocal get 檢查」
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
所有的當(dāng)前線程的Looper myLooper方法毅贮,唯一性
/**
* 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();
}
Looper 的 ThreadLocal
初始化中
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
上下文存儲(chǔ)變量,內(nèi)部有一個(gè)ThreadLocalMap k 就是threadlocal 本身,而他是final的 ,每次set<k,value>先get(k)如果存在直接返回
是一個(gè)常量保證了唯一性
ThreadLocal 維護(hù)類自己的Map
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
...
}
一個(gè)線程----》只有一個(gè)Looper 不能改
一個(gè)線程----》ThreadLocalMap---》<唯一的ThreadLocal,value>
內(nèi)部維護(hù)原子性操作
private static AtomicInteger nextHashCode =
new AtomicInteger();
prepare 的時(shí)候先get 判斷是否存在
Looper loop()
執(zhí)行流程
looper.loop()->messageQueue.next()->handler.dispatchMessage->handler.handlerMessage()
承接Message
「msg.target 相當(dāng)于====handler===dispatchMessage 回調(diào)」
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;
// 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();
// Allow overriding a threshold with a system prop. e.g.
// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
final int thresholdOverride =
SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
+ ".slow", 0);
boolean slowDeliveryDetected = false;
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);
}
// Make sure the observer won't change while processing a transaction.
final Observer observer = sObserver;
final long traceTag = me.mTraceTag;
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
if (thresholdOverride > 0) {
slowDispatchThresholdMs = thresholdOverride;
slowDeliveryThresholdMs = thresholdOverride;
}
final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
final boolean needStartTime = logSlowDelivery || logSlowDispatch;
final boolean needEndTime = logSlowDispatch;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
Object token = null;
if (observer != null) {
token = observer.messageDispatchStarting();
}
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
try {
->注意留意「msg.target」
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logSlowDelivery) {
if (slowDeliveryDetected) {
if ((dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
slowDeliveryDetected = false;
}
} else {
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
msg)) {
// Once we write a slow delivery log, suppress until the queue drains.
slowDeliveryDetected = true;
}
}
}
if (logSlowDispatch) {
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
}
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();
}
}
結(jié)束
Handler 的Callback
Handler->handlerMessage()
共享內(nèi)存
整個(gè)過(guò)程是Message在動(dòng)的過(guò)程
Message產(chǎn)生尘奏,new Message() 或者 obtain過(guò)來(lái)滩褥,所以就體現(xiàn)子線程到主線程的內(nèi)存共享
內(nèi)存不分線程,所以子線程能用炫加,主線程也能用瑰煎。
也是為什么不能不斷new Message的原因
退出和空消息
如果子線程調(diào)用 消息為空如何處理
需要主動(dòng)調(diào)用quite,Looper 的loop 是個(gè)無(wú)限循環(huán)俗孝,msg == null
quit-》喚醒-》messagequeue = null -> 退出Looper
quit函數(shù)---》nativeWake()
Handler 內(nèi)存泄露的原因和解決
- Activity 使用錯(cuò)誤,使用了匿名內(nèi)部類酒甸。默認(rèn)會(huì)持有 this,handler 持有外部類Activity。-----------解決改成static 靜態(tài)內(nèi)部類
Handler handler = new Handler(){
......
}
- 其他的內(nèi)部類持有為什么沒(méi)有這種問(wèn)題赋铝,recyclerview 的Adapter 的viewholder插勤?聲明周期的原因
sendMessageAtTime--->enqueueMessage{
msg.target = this;=====handler
}
整個(gè)message傳遞過(guò)程,需要延遲處理革骨,message持有handler农尖,handler持有activity。GC無(wú)法回收良哲,可達(dá)性算法不能回收
------------------解決 在 onDestroy的時(shí)候 主動(dòng)調(diào)用removeCallbacksAndMessages 釋放
HandlerThread是什么?為什么它會(huì)存在?
HandlerThread是Thread的子類盛卡,嚴(yán)格意義上來(lái)說(shuō)就是一個(gè)線程,只是它在自己的線程里面幫我們創(chuàng)建了Looper HandlerThread 存在的意義如下:
- 方便使用:a. 方便初始化筑凫,b窟扑,方便獲取線程looper 2)保證了線程安全
我們一般在Thread里面 線程Looper進(jìn)行初始化的代碼里面,必須要對(duì)Looper.prepare(),同時(shí)要調(diào)用Loop漏健。 loop();
@Override
public void run() { Looper.prepare(); Looper.loop();
}
而我們要使用子線程中的Looper的方式是怎樣的呢?看下面的代碼
Thread thread = new Thread(new Runnable() {
Looper looper;
@Override
public void run() {
// Log.d(TAG, "click2: " + Thread.currentThread().getName());
Looper.prepare();
looper =Looper.myLooper(); Looper.loop();
}
public Looper getLooper() {
return looper;
} });
thread.start();
Handler handler = new Handler(thread.getLooper());
上面這段代碼有沒(méi)有問(wèn)題呢? 肯定有
1)在初始化子線程的handler的時(shí)候嚎货,我們無(wú)法將子線程的looper傳值給Handler,解決辦法有如下辦法:
a. 可以將Handler的初始化放到 Thread里面進(jìn)行
b. 可以創(chuàng)建一個(gè)獨(dú)立的類繼承Thread,然后蔫浆,通過(guò)類的對(duì)象獲取殖属。
這兩種辦法都可以,但是瓦盛,這個(gè)工作 HandlerThread幫我們完成了
2)依據(jù)多線程的工作原理洗显,我們?cè)谏厦娴拇a中外潜,調(diào)用 thread.getLooper()的時(shí)候,此時(shí)的looper可能還沒(méi)有初 始化挠唆,此時(shí)是不是可能會(huì)掛掉呢?
以上問(wèn)題
HandlerThread 已經(jīng)幫我們完美的解決了处窥,這就是 handlerThread存在的必要性了。
我們?cè)倏?HandlerThread源碼
public void run() {
mTid = Process.myTid(); Looper.prepare(); synchronized (this) {
mLooper = Looper.myLooper();
notifyAll(); //此時(shí)喚醒其他等待的鎖玄组,但是 }
Process.setThreadPriority(mPriority); onLooperPrepared();
Looper.loop();
mTid = -1;
}
它的優(yōu)點(diǎn)就在于它的多線程操作滔驾,可以幫我們保證使用Thread的handler時(shí)一定是安全的。
IntentService 默認(rèn)創(chuàng)建HandlerThread 參見(jiàn) http://www.reibang.com/p/ad5613d3955e
應(yīng)用場(chǎng)景1:
IntentService 應(yīng)用:本身繼承service: 處理后臺(tái)耗時(shí)任務(wù)
處理完》 IntentService 自動(dòng)停止:內(nèi)存釋放
應(yīng)用需求:一項(xiàng)任務(wù)分成幾個(gè)子任務(wù)俄讹,子任務(wù)按順序執(zhí)行哆致,子任務(wù)全部執(zhí)行完成后,這項(xiàng)任務(wù)才算成功
這個(gè)需求可以用多個(gè)線程來(lái)處理患膛,一個(gè)線程處理完 -> 下一個(gè)線程 -> 下一個(gè)線程
IntentService就可以幫助我們完成這個(gè)工作摊阀,而且,能夠很好的管理線程踪蹬,保證只有一個(gè)線程處理工作胞此,而且是一個(gè)一個(gè)的完成任務(wù),有條不紊的進(jìn)行
同一個(gè)線程-》順序執(zhí)行1 2 3 4 : 對(duì)線程的控制么
原因:
IntentService 每個(gè)HandlerThread 維護(hù)一個(gè)Looper 保證looper綁定
每個(gè)任務(wù)是一個(gè)消息跃捣,維護(hù)了一個(gè)隊(duì)列messageQueue
先來(lái)后到 保證線程消息一個(gè)一個(gè)執(zhí)行豌鹤。
類似的場(chǎng)景2:緩存操作參見(jiàn)
Messagequeue 隊(duì)列處理機(jī)制在fragment生命周期管理中的應(yīng)用,glide
http://www.reibang.com/p/317b2d6bde1b
題外話:我們?cè)贕lide里面handler的場(chǎng)景
Glide.with(context).from(url).into(iamgeView)
我們通常context 縮小范圍枝缔,如果默認(rèn)用子線程他會(huì)默認(rèn)給你轉(zhuǎn)成applicationContext容易造成泄露
context:? fragment.getAppalicationContext
RequestManagerFragment getRequestManagerFragment(final android.app.FragmentManager fm) {
pendingRequestManagerFragments = new HashMap<Fragment>();
//嘗試根據(jù)id去找到此前創(chuàng)建的RequestManagerFragment
RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
//如果沒(méi)有找到布疙,那么從臨時(shí)存儲(chǔ)中尋找
current = pendingRequestManagerFragments.get(fm);
if (current == null) {
//如果仍然沒(méi)有找到,那么新建一個(gè)RequestManagerFragment愿卸,并添加到臨時(shí)存儲(chǔ)中灵临。
//然后開(kāi)啟事務(wù)綁定fragment并使用handler發(fā)送消息來(lái)將臨時(shí)存儲(chǔ)的fragment移除。
current = new RequestManagerFragment();
pendingRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
場(chǎng)景3:Fragment的聲明周期管理趴荸,也用到了MessageQueue和handler相關(guān)東西儒溉。
android View中的Looper Handler message
Choreographer 屏幕的點(diǎn)擊 「翻譯過(guò)來(lái)是舞者」
同步屏障,保證不掉幀 vsync
我們現(xiàn)在手機(jī)最低60hz 发钝,也就是刷新率1000ms/60幀率=16幀/ms
消息機(jī)制之同步屏障
線程的消息都是放到同一個(gè)MessageQueue里面
顿涣,取消息的時(shí)候是互斥取消息
,而 且 酝豪,而添加消息是按照消息的執(zhí)行的先后順序
進(jìn)行的排序涛碑,那么問(wèn)題來(lái)了,同一個(gè)時(shí)間范圍內(nèi)的消 息孵淘,如果它是需要立刻執(zhí)行
的蒲障,那我們?cè)趺崔k,按照常規(guī)的辦法,我們需要等到隊(duì)列輪詢到我自己的時(shí)候才能執(zhí)行 哦揉阎,那豈不是黃花菜都涼了庄撮。所以,我們需要給緊急需要執(zhí)行的消息一個(gè)綠色通道毙籽,這個(gè)綠色通道就是同步屏障的概 念
洞斯。
同步屏障是什么?
屏障的意思即為阻礙,顧名思義坑赡,同步屏障就是阻礙同步消息烙如,只讓異步消息通過(guò)
。如何開(kāi)啟同步屏障呢?如下而 已:
MessageQueue#postSyncBarrier()
源代碼是這樣
/**
* Posts a synchronization barrier to the Looper's message queue.
*
* Message processing occurs as usual until the message queue encounters the
* synchronization barrier that has been posted. When the barrier is encountered,
* later synchronous messages in the queue are stalled (prevented from being executed)
* until the barrier is released by calling {@link #removeSyncBarrier} and specifying
* the token that identifies the synchronization barrier.
*
* This method is used to immediately postpone execution of all subsequently posted
* synchronous messages until a condition is met that releases the barrier.
* Asynchronous messages (see {@link Message#isAsynchronous} are exempt from the barrier
* and continue to be processed as usual.
*
* This call must be always matched by a call to {@link #removeSyncBarrier} with
* the same token to ensure that the message queue resumes normal operation.
* Otherwise the application will probably hang!
*
* @return A token that uniquely identifies the barrier. This token must be
* passed to {@link #removeSyncBarrier} to release the barrier.
*
* @hide
*/
@TestApi
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;
}
}
可以看到垮衷,Message 對(duì)象初始化的時(shí)候并沒(méi)有給 target 賦值厅翔,因此乖坠, target == null 的 來(lái)源就找到了搀突。上面消 息的插入也做了相應(yīng)的注釋。這樣熊泵,一條target == null 的消息就進(jìn)入了消息隊(duì)列仰迁。
那么,開(kāi)啟同步屏障后顽分,所謂的異步消息又是如何被處理的呢? 如果對(duì)消息機(jī)制有所了解的話徐许,應(yīng)該知道消息的最終處理是在消息輪詢器 Looper#loop() 中,而 loop() 循環(huán)中會(huì)
調(diào)用 MessageQueue#next() 從消息隊(duì)列中進(jìn)行取消息卒蘸。
->上面的「?jìng)魉蛶А筂essage next() 查看源碼
從上面可以看出雌隅,當(dāng)消息隊(duì)列開(kāi)啟同步屏障的時(shí)候(即標(biāo)識(shí)為msg.target == null),消息機(jī)制在處理消息的時(shí) 候缸沃,優(yōu)先處理異步消息恰起。這樣,同步屏障就起到了一種過(guò)濾和優(yōu)先級(jí)的作用趾牧。
同步消息的應(yīng)用場(chǎng)景
似乎在日常的應(yīng)用開(kāi)發(fā)中检盼,很少會(huì)用到同步屏障。那么翘单,同步屏障在系統(tǒng)源碼中有哪些使用場(chǎng)景呢?Android 系統(tǒng)中
的 UI 更新相關(guān)的消息即為異步消息吨枉,需要優(yōu)先處理。
比如哄芜,在 View 更新時(shí)貌亭,draw、requestLayout认臊、invalidate 等很多地方都調(diào)用了
ViewRootImpl#scheduleTraversals() 属提,如下:
//ViewRootImpl.java
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
-》【開(kāi)啟同步屏障】
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
-》【發(fā)送異步消息】
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
postCallback() 最終走到了
Choreographer-》postCallbackDelayedInternal() :
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
if (DEBUG_FRAMES) {
Log.d(TAG, "PostCallback: type=" + callbackType
+ ", action=" + action + ", token=" + token
+ ", delayMillis=" + delayMillis);
}
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
if (dueTime <= now) {
scheduleFrameLocked(now);
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
-》【異步消息】
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
這里就開(kāi)啟了同步屏障,并發(fā)送異步消息,由于 UI 更新相關(guān)的消息是優(yōu)先級(jí)最高的冤议,這樣系統(tǒng)就會(huì)優(yōu)先處理這些異 步消息斟薇。
最后,當(dāng)要移除同步屏障的時(shí)候需要調(diào)用 ViewRootImpl#unscheduleTraversals() 恕酸。
void unscheduleTraversals() {
if (mTraversalScheduled) {
} }
小結(jié)
同步屏障的設(shè)置可以方便地處理那些優(yōu)先級(jí)較高的異步消息堪滨。當(dāng)我們調(diào)用
Handler.getLooper().getQueue().postSyncBarrier() 并設(shè)置消息的 setAsynchronous(true) 時(shí),target 即 為 null 蕊温,也就開(kāi)啟了同步屏障袱箱。當(dāng)在消息輪詢器 Looper 在 loop() 中循環(huán)處理消息時(shí),如若開(kāi)啟了同步屏障义矛,會(huì)優(yōu) 先處理其中的異步消息发笔,而阻礙同步消息。
享元設(shè)計(jì)模式(內(nèi)存復(fù)用)
Message
obtain() 用的一個(gè)對(duì)象池
public static final Object sPoolSync = new Object();
message 用的是對(duì)象池
當(dāng)結(jié)束后凉翻,會(huì)回收
點(diǎn)擊事件會(huì)轉(zhuǎn)換成message發(fā)送出去
Looper 的 loop死循環(huán) 跟ANR 沒(méi)有關(guān)系了讨,只是ANR發(fā)生 5s handler會(huì)發(fā)出提醒
ActivityThread的 main 方法的主要作用就是做消息循環(huán),一旦退出消息循環(huán)制轰,主線程運(yùn)行完畢前计,那么你的應(yīng)用也就退出了。Android是事件驅(qū)動(dòng)的垃杖,在Looper.loop()中不斷接收事件男杈、處理事件,而Activity的生命周期都依靠于主線程的 Loop.loop() 來(lái)調(diào)度调俘,所以可想而知它的存活周期和 Activity 也是一致的伶棒。當(dāng)沒(méi)有事件需要處理時(shí),主線程就會(huì)阻塞彩库;當(dāng)子線程往消息隊(duì)列發(fā)送消息肤无,并且往管道文件寫(xiě)數(shù)據(jù)時(shí),主線程就被喚醒侧巨。真正會(huì)卡死主線程的操作是在執(zhí)行回調(diào)方法 onCreate/onStart/onResume 等操作的時(shí)間過(guò)長(zhǎng)舅锄,導(dǎo)致掉幀,甚至發(fā)生ANR司忱,looper.loop() 本身不會(huì)導(dǎo)致應(yīng)用卡死皇忿。
主線程Looper從消息隊(duì)列讀取消息,當(dāng)讀完所有消息時(shí)坦仍,主線程阻塞鳍烁。子線程往消息隊(duì)列發(fā)送消息,并且往管道文件寫(xiě)數(shù)據(jù)繁扎,主線程即被喚醒幔荒,從管道文件讀取數(shù)據(jù)糊闽,主線程被喚醒只是為了讀取消息,當(dāng)消息讀取完畢爹梁,再次睡眠右犹。因此loop的循環(huán)并不會(huì)對(duì)CPU性能有過(guò)多的消耗。