說說Android的廣播(4) - 前臺廣播為什么比后臺廣播快袭异?
前臺廣播為什么比后臺廣播快
討論超時的細(xì)節(jié)之前钠龙,我們先講講對應(yīng)用開發(fā)有幫助的,為什么前臺隊列比后臺隊列要快?
應(yīng)用開發(fā)的同學(xué)在給系統(tǒng)團(tuán)隊提意見的時候講碴里,說以前我們都是靠通過將廣播消息設(shè)成前臺廣播的方式來做workaround來解決一些廣播的性能問題的沈矿,你們系統(tǒng)為什么不能將后臺廣播做得跟前臺廣播一樣快呢?這一定是設(shè)計上的問題咬腋。
其實羹膳,這種前臺廣播的設(shè)計,就是為了加速廣播的性能而設(shè)計的根竿。二者在設(shè)計思想上就有不同陵像。根據(jù)應(yīng)用層實際的需求,決定使用前臺廣播還是后臺廣播犀填,本來就是應(yīng)用設(shè)計時候應(yīng)該考慮的問題。
當(dāng)然嗓违,Android的這個設(shè)計對應(yīng)用開發(fā)的要求比較高九巡,我看了一些Android的教程,也沒有講到這么細(xì)節(jié)的東西蹂季。系統(tǒng)還是應(yīng)該更智能一些冕广。
這里面主要有三點(diǎn)原因:
- 前臺隊列相對比較空閑
- 前臺隊列的超時時間是10s,而后臺是60s. 后臺廣播的設(shè)計思想就是當(dāng)前應(yīng)用優(yōu)先偿洁,盡可能多讓收到廣播的應(yīng)用有充足的時間把事件做完撒汉。而前臺廣播的目的是緊急通知,設(shè)計上就傾向于當(dāng)前應(yīng)用趕快處理完涕滋,盡快傳給下一個睬辐。
- 前臺隊列不等后臺服務(wù),而后臺隊列要多等后臺服務(wù)一定的時間宾肺。這還是設(shè)計思想上的原因溯饵。
比如我們舉個關(guān)機(jī)廣播的例子,寫應(yīng)用的同學(xué)可以參照這個例子來寫發(fā)送前臺廣播哈:
Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendOrderedBroadcastAsUser(intent,
UserHandle.ALL, null, br, mHandler, 0, null, null);
比起普通廣播锨用,只是多加一個intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);就可以了丰刊。
不過,紙上得來終覺淺增拥,我們看下實際的代碼中是如何實現(xiàn)的吧啄巧。
廣播隊列的構(gòu)造
我們來看AMS中的構(gòu)造函數(shù)里,是如何為前臺隊列和后臺隊列配置參數(shù)的:
...
mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
"foreground", BROADCAST_FG_TIMEOUT, false);
mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
"background", BROADCAST_BG_TIMEOUT, true);
mBroadcastQueues[0] = mFgBroadcastQueue;
mBroadcastQueues[1] = mBgBroadcastQueue;
...
我們可以看到有兩點(diǎn)不同:一個是超時時間不同掌栅,另一個是allowDelayBehindServices參數(shù)不同秩仆,前臺是false,就是不等待猾封,而后臺是true逗概,要等待。
我們來看看BroadcastQueue的構(gòu)造函數(shù):
BroadcastQueue(ActivityManagerService service, Handler handler,
String name, long timeoutPeriod, boolean allowDelayBehindServices) {
mService = service;
mHandler = new BroadcastHandler(handler.getLooper());
mQueueName = name;
mTimeoutPeriod = timeoutPeriod;
mDelayBehindServices = allowDelayBehindServices;
}
timeoutPeriod是超時時間,我們來看看前臺和后臺的超時時間是如何定義的:
// How long we allow a receiver to run before giving up on it.
static final int BROADCAST_FG_TIMEOUT = 10*1000;
static final int BROADCAST_BG_TIMEOUT = 60*1000;
BroadcastRecord中的幾個時間點(diǎn)
既然是分析超時逾苫,我們先對BroadcastRecord中記錄的幾個時間點(diǎn)有個印象卿城。
long enqueueClockTime; // the clock time the broadcast was enqueued
long dispatchTime; // when dispatch started on this set of receivers
long dispatchClockTime; // the clock time the dispatch started
long receiverTime; // when current receiver started for timeouts.
long finishTime; // when we finished the broadcast.
其中最繞的是dispatchTime和dispatchClockTime,都是開始分發(fā)消息時的時間铅搓,它們有什么不同呢瑟押?
其實,它們的區(qū)別僅僅是計時方法不同:
r.dispatchTime = SystemClock.uptimeMillis();
r.dispatchClockTime = System.currentTimeMillis();
其他的時間也都一樣星掰,凡是叫ClockTime的都是System.currentTimeMillis()多望,只叫Time的,就是SystemClock.uptimeMillis().
我們看下它們的實際賦值氢烘,加深一下印象:
ClockTime就這兩個:
r.enqueueClockTime = System.currentTimeMillis();
r.dispatchClockTime = System.currentTimeMillis();
非Clock的Time有三個:
r.dispatchTime = SystemClock.uptimeMillis();
r.receiverTime = SystemClock.uptimeMillis();
r.finishTime = SystemClock.uptimeMillis();
入隊列的時間
入隊列的邏輯我們在第三講中已經(jīng)分析過了怀偷,我們再復(fù)習(xí)一下:
214 public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
215 mParallelBroadcasts.add(r);
216 r.enqueueClockTime = System.currentTimeMillis();
217 }
218
219 public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
220 mOrderedBroadcasts.add(r);
221 r.enqueueClockTime = System.currentTimeMillis();
222 }
派發(fā)的時間
這就是剛才我們看到的同時記兩個時間點(diǎn)的那個,在processNextBroadcast中播玖,我們下講會專門分析這個大派發(fā)函數(shù):
先看對于并發(fā)隊列的派發(fā):
...
654 // First, deliver any non-serialized broadcasts right away.
655 while (mParallelBroadcasts.size() > 0) {
656 r = mParallelBroadcasts.remove(0);
657 r.dispatchTime = SystemClock.uptimeMillis();
658 r.dispatchClockTime = System.currentTimeMillis();
659 final int N = r.receivers.size();
...
662 for (int i=0; i<N; i++) {
663 Object target = r.receivers.get(i);
...
667 deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);
668 }
669 addBroadcastToHistoryLocked(r);
...
672 }
...
后面還有針對串行隊列的椎工,也是兩個同時要賦值喲~
796 // Keep track of when this receiver started, and make sure there
797 // is a timeout message pending to kill it if need be.
798 r.receiverTime = SystemClock.uptimeMillis();
799 if (recIdx == 0) {
800 r.dispatchTime = r.receiverTime;
801 r.dispatchClockTime = System.currentTimeMillis();
...
receiver接收到的時間
這段邏輯也是在處理廣播消息的主循環(huán)processNextBroadcast函數(shù)中。
第一處就是剛才看到的位置
796 // Keep track of when this receiver started, and make sure there
797 // is a timeout message pending to kill it if need be.
798 r.receiverTime = SystemClock.uptimeMillis();
另一處是超時之后蜀踏,反正也不打算繼續(xù)等它了维蒙,就把超時那一刻的時間記錄成收到的時間吧。這段邏輯位于超時處理的函數(shù)broadcastTimeoutLocked中果覆。
1164 Slog.w(TAG, "Timeout of broadcast " + r + " - receiver=" + r. receiver
1165 + ", started " + (now - r.receiverTime) + "ms ago");
1166 r.receiverTime = now;
1167 r.anrCount++;
廣播處理結(jié)束的時間
這個省事了颅痊,只在一處出現(xiàn),就是修史書的時候局待,具體的函數(shù)是addBroadcastToHistoryLocked.
1221 private final void addBroadcastToHistoryLocked(BroadcastRecord r) {
...
1226 r.finishTime = SystemClock.uptimeMillis();
1227
1228 mBroadcastHistory[mHistoryNext] = r;
1229 mHistoryNext = ringAdvance(mHistoryNext, 1, MAX_BROADCAST_HISTORY);
1230
1231 mBroadcastSummaryHistory[mSummaryHistoryNext] = r.intent;
1232 mSummaryHistoryEnqueueTime[mSummaryHistoryNext] = r.enqueueClockTime;
1233 mSummaryHistoryDispatchTime[mSummaryHistoryNext] = r.dispatchClockTime;
1234 mSummaryHistoryFinishTime[mSummaryHistoryNext] = System.currentTimeMillis();
1235 mSummaryHistoryNext = ringAdvance(mSummaryHistoryNext, 1, MAX_BROADCAST_SUMMARY_HISTORY);
1236 }