2020-04-19-Android-前台广播和后台广播
前面介绍全局广播的时候,提到过根据intent的flag不同,广播会被加入到不同的队列中。
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;
}
前台广播
默认情况下,Intent是不带FLAG_RECEIVER_FOREGROUND的flag的,所以我们默认使用的都是后台广播。
如果要使用前台广播,也很简单,只需要加上flag即可。过程中遇到一个问题,在android新版本上,隐式调用的广播,不能通过静态注册接收了。
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
接下来看看前后台广播队列的构造有什么区别
// 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);
构造函数有5个参数,后面两个参数不同,第一个参数BroadcastConstants主要设置广播参数,比如超时时间等。我们可以看到前台广播超时时间是10秒,后台是60秒。不过,只有串行广播,也就是有序广播才需要考虑超时。还有一点需要注意的是,静态注册的广播实际上都是一种串行的方式。
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);
}
}
实际上超时的机制在前面介绍全局广播的时候有提到过,在processNextBroadcastLocked方法中,有一处调用了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方法中发送了一个延时消息,提醒ANR问题。
final void setBroadcastTimeoutLocked(long timeoutTime) {
if (! mPendingBroadcastTimeoutMessage) {
Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
mHandler.sendMessageAtTime(msg, timeoutTime);
mPendingBroadcastTimeoutMessage = true;
}
}
超时时间到达之后,会调用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处会重新判断一次超时时间,如果广播已经分发给下一个receiver,则timeoutTime 大于now,return,不会发生ANR。
注释2处调用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处,如果当前的广播接收者和下一个广播接收者不处于同一个进程,且当前有正在启动的后台服务(用户维度)那么BroadcastQueue进入WAITING_SERVICES状态。
该状态对processNextBroadcast内对有序广播的处理以及广播超时方法broadcastTimeoutLocked都有一定的影响。
这可能就是我们常说前台广播比后台快的原因,因为前台广播不需要等待后台服务启动。
参考
说说Android的广播(4) - 前台广播为什么比后台广播快?
Broadcast之前/后台广播队列
Android Broadcast广播机制分析
Android广播的超时机制
Android中有序广播的基本使用方法
Android Q上broadcast 的新特性:广播延时方案