前面介紹全局廣播的時(shí)候猫态,提到過(guò)根據(jù)intent的flag不同该园,廣播會(huì)被加入到不同的隊(duì)列中伐弹。
BroadcastQueue broadcastQueueForIntent(Intent intent) {
if (isOnOffloadQueue(intent.getFlags())) {
if (DEBUG_BROADCAST_BACKGROUND) {
Slog.i(TAG_BROADCAST,
"Broadcast intent " + intent + " on offload queue");
}
return mOffloadBroadcastQueue;
}
final boolean isFg = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
if (DEBUG_BROADCAST_BACKGROUND) Slog.i(TAG_BROADCAST,
"Broadcast intent " + intent + " on "
+ (isFg ? "foreground" : "background") + " queue");
return (isFg) ? mFgBroadcastQueue : mBgBroadcastQueue;
}
前臺(tái)廣播
默認(rèn)情況下拉馋,Intent是不帶FLAG_RECEIVER_FOREGROUND的flag的,所以我們默認(rèn)使用的都是后臺(tái)廣播。
如果要使用前臺(tái)廣播煌茴,也很簡(jiǎn)單随闺,只需要加上flag即可。過(guò)程中遇到一個(gè)問題蔓腐,在android新版本上矩乐,隱式調(diào)用的廣播,不能通過(guò)靜態(tài)注冊(cè)接收了回论。
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
接下來(lái)看看前后臺(tái)廣播隊(duì)列的構(gòu)造有什么區(qū)別
// Broadcast policy parameters
final BroadcastConstants foreConstants = new BroadcastConstants(
Settings.Global.BROADCAST_FG_CONSTANTS);
foreConstants.TIMEOUT = BROADCAST_FG_TIMEOUT;
final BroadcastConstants backConstants = new BroadcastConstants(
Settings.Global.BROADCAST_BG_CONSTANTS);
backConstants.TIMEOUT = BROADCAST_BG_TIMEOUT;
mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
"foreground", foreConstants, false);
mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
"background", backConstants, true);
構(gòu)造函數(shù)有5個(gè)參數(shù)散罕,后面兩個(gè)參數(shù)不同,第一個(gè)參數(shù)BroadcastConstants主要設(shè)置廣播參數(shù)傀蓉,比如超時(shí)時(shí)間等欧漱。我們可以看到前臺(tái)廣播超時(shí)時(shí)間是10秒,后臺(tái)是60秒僚害。不過(guò)硫椰,只有串行廣播,也就是有序廣播才需要考慮超時(shí)萨蚕。還有一點(diǎn)需要注意的是靶草,靜態(tài)注冊(cè)的廣播實(shí)際上都是一種串行的方式。
BroadcastQueue(ActivityManagerService service, Handler handler,
String name, BroadcastConstants constants, boolean allowDelayBehindServices) {
mService = service;
mHandler = new BroadcastHandler(handler.getLooper());
mQueueName = name;
mDelayBehindServices = allowDelayBehindServices;
mConstants = constants;
mDispatcher = new BroadcastDispatcher(this, mConstants, mHandler, mService);
}
private void updateConstants() {
synchronized (mParser) {
try {
mParser.setString(Settings.Global.getString(mResolver, mSettingsKey));
} catch (IllegalArgumentException e) {
Slog.e(TAG, "Bad broadcast settings in key '" + mSettingsKey + "'", e);
return;
}
// Unspecified fields retain their current value rather than revert to default
TIMEOUT = mParser.getLong(KEY_TIMEOUT, TIMEOUT);
SLOW_TIME = mParser.getLong(KEY_SLOW_TIME, SLOW_TIME);
DEFERRAL = mParser.getLong(KEY_DEFERRAL, DEFERRAL);
DEFERRAL_DECAY_FACTOR = mParser.getFloat(KEY_DEFERRAL_DECAY_FACTOR,
DEFERRAL_DECAY_FACTOR);
DEFERRAL_FLOOR = mParser.getLong(KEY_DEFERRAL_FLOOR, DEFERRAL_FLOOR);
ALLOW_BG_ACTIVITY_START_TIMEOUT = mParser.getLong(KEY_ALLOW_BG_ACTIVITY_START_TIMEOUT,
ALLOW_BG_ACTIVITY_START_TIMEOUT);
}
}
實(shí)際上超時(shí)的機(jī)制在前面介紹全局廣播的時(shí)候有提到過(guò)岳遥,在processNextBroadcastLocked方法中奕翔,有一處調(diào)用了setBroadcastTimeoutLocked。
if (! mPendingBroadcastTimeoutMessage) {
long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Submitting BROADCAST_TIMEOUT_MSG ["
+ mQueueName + "] for " + r + " at " + timeoutTime);
setBroadcastTimeoutLocked(timeoutTime);
}
在setBroadcastTimeoutLocked方法中發(fā)送了一個(gè)延時(shí)消息浩蓉,提醒ANR問題派继。
final void setBroadcastTimeoutLocked(long timeoutTime) {
if (! mPendingBroadcastTimeoutMessage) {
Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
mHandler.sendMessageAtTime(msg, timeoutTime);
mPendingBroadcastTimeoutMessage = true;
}
}
超時(shí)時(shí)間到達(dá)之后,會(huì)調(diào)用broadcastTimeoutLocked方法捻艳。
final void broadcastTimeoutLocked(boolean fromMsg) {
if (fromMsg) {
mPendingBroadcastTimeoutMessage = false;
}
//……
long now = SystemClock.uptimeMillis();
BroadcastRecord r = mDispatcher.getActiveBroadcastLocked();
if (fromMsg) {
if (!mService.mProcessesReady) {
// Only process broadcast timeouts if the system is ready; some early
// broadcasts do heavy work setting up system facilities
return;
}
//……
long timeoutTime = r.receiverTime + mConstants.TIMEOUT;//1
if (timeoutTime > now) {
// We can observe premature timeouts because we do not cancel and reset the
// broadcast timeout message after each receiver finishes. Instead, we set up
// an initial timeout then kick it down the road a little further as needed
// when it expires.
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Premature timeout ["
+ mQueueName + "] @ " + now + ": resetting BROADCAST_TIMEOUT_MSG for "
+ timeoutTime);
setBroadcastTimeoutLocked(timeoutTime);
return;
}
}
//……
// Move on to the next receiver.
finishReceiverLocked(r, r.resultCode, r.resultData,
r.resultExtras, r.resultAbort, false);//2
scheduleBroadcastsLocked();
//……
}
注釋1處會(huì)重新判斷一次超時(shí)時(shí)間驾窟,如果廣播已經(jīng)分發(fā)給下一個(gè)receiver,則timeoutTime 大于now认轨,return绅络,不會(huì)發(fā)生ANR。
注釋2處調(diào)用finishReceiverLocked方法嘁字。
public boolean finishReceiverLocked(BroadcastRecord r, int resultCode,
String resultData, Bundle resultExtras, boolean resultAbort, boolean waitForServices) {
//……
// If we want to wait behind services *AND* we're finishing the head/
// active broadcast on its queue
if (waitForServices && r.curComponent != null && r.queue.mDelayBehindServices
&& r.queue.mDispatcher.getActiveBroadcastLocked() == r) {
//……
// Don't do this if the next receive is in the same process as the current one.
if (receiver == null || nextReceiver == null
|| receiver.applicationInfo.uid != nextReceiver.applicationInfo.uid
|| !receiver.processName.equals(nextReceiver.processName)) {
// In this case, we are ready to process the next receiver for the current broadcast,
// but are on a queue that would like to wait for services to finish before moving
// on. If there are background services currently starting, then we will go into a
// special state where we hold off on continuing this broadcast until they are done.
if (mService.mServices.hasBackgroundServicesLocked(r.userId)) {
Slog.i(TAG, "Delay finish: " + r.curComponent.flattenToShortString());
r.state = BroadcastRecord.WAITING_SERVICES;//1
return false;
}
}
}
//……
return state == BroadcastRecord.APP_RECEIVE
|| state == BroadcastRecord.CALL_DONE_RECEIVE;
}
注釋1處恩急,如果當(dāng)前的廣播接收者和下一個(gè)廣播接收者不處于同一個(gè)進(jìn)程,且當(dāng)前有正在啟動(dòng)的后臺(tái)服務(wù)(用戶維度)那么BroadcastQueue進(jìn)入WAITING_SERVICES狀態(tài)纪蜒。
該狀態(tài)對(duì)processNextBroadcast內(nèi)對(duì)有序廣播的處理以及廣播超時(shí)方法broadcastTimeoutLocked都有一定的影響衷恭。
這可能就是我們常說(shuō)前臺(tái)廣播比后臺(tái)快的原因,因?yàn)榍芭_(tái)廣播不需要等待后臺(tái)服務(wù)啟動(dòng)纯续。
參考
說(shuō)說(shuō)Android的廣播(4) - 前臺(tái)廣播為什么比后臺(tái)廣播快随珠?
Broadcast之前/后臺(tái)廣播隊(duì)列
Android Broadcast廣播機(jī)制分析
Android廣播的超時(shí)機(jī)制
Android中有序廣播的基本使用方法
Android Q上broadcast 的新特性:廣播延時(shí)方案