BroadcastReceiver也就是“广播接收者”的意思,它就是用来接收来自系统和应用中的广播。
在Android系统中,广播体现在方方面面,例如当开机完成后系统会产生一条广播,接收到这条广播就能实现开机启动服务的功能;当网络状态改变时系统会产生一条广播,接收到这条广播就能及时地做出提示和保存数据等操作;当电池电量改变时,系统会产生一条广播,接收到这条广播就能在电量低时告知用户及时保存进度,等等。
一.在广播的使用中,分静态注册和动态注册:
静态注册:
静态注册是在AndroidManifest.xml文件中配置的,这种方式的注册是常驻型的,也就是说当应用关闭后,只要有广播发出来,就可以接收到。
在下面代码的中的onReceive方法内,我们可以使用intent获取数据。
public class MyStaticReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
String msg = intent.getStringExtra("msg");
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
}
}
<receiver android:name=".broadcastreceiverdemo.MyStaticReceiver">
<intent-filter>
<action android:name="android.intent.action.MY_STATIC_BROADCAST"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
Intent intent1 = new Intent("android.intent.action.MY_STATIC_BROADCAST");
intent1.putExtra("msg", "hello static receiver.");
sendBroadcast(intent1);
动态注册:动态注册需要在代码中动态的指定广播地址并注册,通常我们是在Activity或Service注册一个广播。当这个Activity或Service被销毁时如果没有解除注册,系统会报一个异常,所以我们应该onDestory的时候将广播解绑。
public class MyActivityReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String msg = intent.getStringExtra("msg");
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
}
}
<pre name="code" class="java">MyActivityReceiver receiver = new MyActivityReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction("android.intent.action.MY_BROADCAST1");
registerReceiver(receiver, filter);
@Override
protected void onDestroy() {
unregisterReceiver(receiver);
super.onDestroy();
}
这样我们就完成了动态注册。
二.
普通广播(Normal Broadcast)对于多个接收者来说是完全异步的,通常每个接收者都无需等待即可以接收到广播,接收者相互之间不会有影响。对于这种广播,接收者无法终止广播,即无法阻止其他接收者的接收动作。至于验证,大家可以去写三个广播接收者去接收一个广播即可。
有序广播(Ordered Broadcast),顾名思义,就是广播按照顺序依次的将广播发给广播接受者,先发送给优先级高的接收者,再到低的接收者;级别高的接收者可以终止广播不再向下传播,并且可以修改广播的内容。
粘性广播,我们可以使用sendStickyBroadcast()发出一个粘性广播,使用这个广播我们需要一个api权限:androi.permission.BROADCAST_STICKY,粘性广播的特点是intent会一直保留到广播事件结束,而这种广播也没有所谓的10秒限制,10秒限制是指普通广播的onReceive方法执行时间过长,超过10秒的时候系统会将这个广播置为可以干掉的candidate,一旦系统资源不够的时候,就会干掉这个广播而让它不执行;还有一点粘性广播会一直保存在系统里,知道有广播接受者调用context.removeStickyBroadcast(intent)才会将粘性广播移除掉。
public class FirstActivityReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String msg = intent.getStringExtra("msg");
Log.i("onReceive-First-", msg);
Bundle bundle = new Bundle();
bundle.putString("msg", msg + "@FirstReceiver");
setResultExtras(bundle);
}
}
public class SecondActivityReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String msg = intent.getStringExtra("msg");
Log.i("onReceive-Second-", msg);
String msg1 = getResultExtras(true).getString("msg");
Log.i("onReceive-Second-", msg1);
Bundle bundle = new Bundle();
bundle.putString("msg", msg1 + "@SecondReceiver");
setResultExtras(bundle);
}
}
public class ThirdActivityReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String msg = getResultExtras(true).getString("msg");
Log.i("onReceive-Second-", msg);
}
}
Intent intent3 = new Intent("android.intent.action.MY_BROADCAST2");
intent3.putExtra("msg", "hello receiver.");
sendOrderedBroadcast(intent3, "scott.permission.MY_BROADCAST_PERMISSION");
其中scott.permission.MY_BROADCAST_PERMISSION是声明的一个normal权限,因为发送有序广播需要设定一个权限
从log中可以看出,我们成功的修改了广播的内容,但在第二个广播中如果还是想要最原始广播的内容我们也是可以获取到的。
以上是我们介绍广播的发送和接收过程,下面介绍下BroadcastReceiver的工作过程:
1.广播的注册过程
静态注册的广播在应用安装时由系统自动完成注册,是由PackageManagerService来完成整个注册过程的,其实除了广播意外,其他三大组件都是在应用安装时由PackageManagerService完成注册的。这里我们只分析广播的动态注册。
在注册的时候调用的是ContextWrapper的方法
@Override
public Intent registerReceiver(
BroadcastReceiver receiver, IntentFilter filter) {
return mBase.registerReceiver(receiver, filter);
}
mBase也是ContextImpl类型,registerReceiver方法的实现如下:
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
return registerReceiver(receiver, filter, null, null);
}
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
String broadcastPermission, Handler scheduler) {
return registerReceiverInternal(receiver, getUserId(),
filter, broadcastPermission, scheduler, getOuterContext());
}
而后调用了内部的registerReceiverInternal方法:
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
IntentFilter filter, String broadcastPermission,
Handler scheduler, Context context) {
IIntentReceiver rd = null;
if (receiver != null) {
if (mPackageInfo != null && context != null) {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
rd = mPackageInfo.getReceiverDispatcher(
receiver, context, scheduler,
mMainThread.getInstrumentation(), true);
} else {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
rd = new LoadedApk.ReceiverDispatcher(
receiver, context, scheduler, null, true).getIIntentReceiver();
}
}
try {
return ActivityManagerNative.getDefault().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName,
rd, filter, broadcastPermission, userId);
} catch (RemoteException e) {
return null;
}
}
在这个方法里面我们看到了熟悉的ActivityManagerNative.getDefault(),还是交给了ActivityManagerService。但这里我们看到方法将传入的receiver转化成了IIntentReceiver,看其名字我们应该能猜到应该应该是传输用的,没错就是将BroadcastReceiver转换成了
IIntentReceiver,用于进程间通信。因为BroadcastReceiver作为android的一个组件是不能直接跨进程传递的;而IIntentReceiver也是一个Binder接口。在LoadedApk.ReceiverDispatcher.InnerReceiver中同事保存了BroadcastReceiver和InnerReceiver,这样在收到广播的时候ReceiverDispatcher就可以很好的调用BroadcastReceiver的onReceive方法了。其实在bindservice也有一个叫做ServiceDIspatcher的类和InnerConnection(也是binder接口)作用是相同的。
ActivityManagerService的注册方法如下:
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
enforceNotIsolatedCaller("registerReceiver");
ArrayList<Intent> stickyIntents = null;
ProcessRecord callerApp = null;
int callingUid;
int callingPid;
synchronized(this) {
if (caller != null) {
callerApp = getRecordForAppLocked(caller);
if (callerApp == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ ") when registering receiver " + receiver);
}
if (callerApp.info.uid != Process.SYSTEM_UID &&
!callerApp.pkgList.containsKey(callerPackage) &&
!"android".equals(callerPackage)) {
throw new SecurityException("Given caller package " + callerPackage
+ " is not running in process " + callerApp);
}
callingUid = callerApp.info.uid;
callingPid = callerApp.pid;
} else {
callerPackage = null;
callingUid = Binder.getCallingUid();
callingPid = Binder.getCallingPid();
}
userId = handleIncomingUser(callingPid, callingUid, userId,
true, ALLOW_FULL_ONLY, "registerReceiver", callerPackage);
Iterator<String> actions = filter.actionsIterator();
if (actions == null) {
ArrayList<String> noAction = new ArrayList<String>(1);
noAction.add(null);
actions = noAction.iterator();
}
// Collect stickies of users
int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
while (actions.hasNext()) {
String action = actions.next();
for (int id : userIds) {
ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id);
if (stickies != null) {
ArrayList<Intent> intents = stickies.get(action);
if (intents != null) {
if (stickyIntents == null) {
stickyIntents = new ArrayList<Intent>();
}
stickyIntents.addAll(intents);
}
}
}
}
}
ArrayList<Intent> allSticky = null;
if (stickyIntents != null) {
final ContentResolver resolver = mContext.getContentResolver();
// Look for any matching sticky broadcasts...
for (int i = 0, N = stickyIntents.size(); i < N; i++) {
Intent intent = stickyIntents.get(i);
// If intent has scheme "content", it will need to acccess
// provider that needs to lock mProviderMap in ActivityThread
// and also it may need to wait application response, so we
// cannot lock ActivityManagerService here.
if (filter.match(resolver, intent, true, TAG) >= 0) {
if (allSticky == null) {
allSticky = new ArrayList<Intent>();
}
allSticky.add(intent);
}
}
}
// The first sticky in the list is returned directly back to the client.
Intent sticky = allSticky != null ? allSticky.get(0) : null;
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Register receiver " + filter + ": " + sticky);
if (receiver == null) {
return sticky;
}
synchronized (this) {
if (callerApp != null && (callerApp.thread == null
|| callerApp.thread.asBinder() != caller.asBinder())) {
// Original caller already died
return null;
}
ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
if (rl == null) {
rl = new ReceiverList(this, callerApp, callingPid, callingUid,
userId, receiver);
if (rl.app != null) {
rl.app.receivers.add(rl);
} else {
try {
receiver.asBinder().linkToDeath(rl, 0);
} catch (RemoteException e) {
return sticky;
}
rl.linkedToDeath = true;
}
mRegisteredReceivers.put(receiver.asBinder(), rl);
} else if (rl.uid != callingUid) {
throw new IllegalArgumentException(
"Receiver requested to register for uid " + callingUid
+ " was previously registered for uid " + rl.uid);
} else if (rl.pid != callingPid) {
throw new IllegalArgumentException(
"Receiver requested to register for pid " + callingPid
+ " was previously registered for pid " + rl.pid);
} else if (rl.userId != userId) {
throw new IllegalArgumentException(
"Receiver requested to register for user " + userId
+ " was previously registered for user " + rl.userId);
}
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
permission, callingUid, userId);
rl.add(bf);
if (!bf.debugCheck()) {
Slog.w(TAG, "==> For Dynamic broadcast");
}
mReceiverResolver.addFilter(bf);
// Enqueue broadcasts for all existing stickies that match
// this filter.
if (allSticky != null) {
ArrayList receivers = new ArrayList();
receivers.add(bf);
final int stickyCount = allSticky.size();
for (int i = 0; i < stickyCount; i++) {
Intent intent = allSticky.get(i);
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, null,
null, -1, -1, null, null, AppOpsManager.OP_NONE, null, receivers,
null, 0, null, null, false, true, true, -1);
queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
}
return sticky;
}
}
代码看着很长,其实总结一下可以归纳以下这几句:
ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
mRegisteredReceivers.put(receiver.asBinder(), rl);
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,ermission, callingUid, userId);
rl.add(bf);
mReceiverResolver.addFilter(bf);这样将InnerReceiver和IntentFilter建立起来联系的,这样就完成了注册。
附:InnerReceiver是ReceiverDispatcher 的内部类,并且继承了IIntentReceiver.Stub。
2.广播的发送和接收过程(我们分析下普通广播的过程)
广播的发送和接收可以说是一个过程的两个阶段
广播的发送同样始于ContextWrapper的sendBroadcast方法
@Override
public void sendBroadcast(Intent intent) {
mBase.sendBroadcast(intent);
}
上面的方法什么都没有做,而是将工作交给了ContextImpl去处理了。
@Override
public void sendBroadcast(Intent intent) {
warnIfCallingFromSystemProcess();
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
intent.prepareToLeaveProcess();
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
getUserId());
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
}
又见到ActivityManagerNative.getDefault()了,是不是有点烦它了呢?
public final int broadcastIntent(IApplicationThread caller,
Intent intent, String resolvedType, IIntentReceiver resultTo,
int resultCode, String resultData, Bundle resultExtras,
String[] requiredPermissions, int appOp, Bundle options,
boolean serialized, boolean sticky, int userId) {
enforceNotIsolatedCaller("broadcastIntent");
synchronized(this) {
intent = verifyBroadcastLocked(intent);
final ProcessRecord callerApp = getRecordForAppLocked(caller);
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
int res = broadcastIntentLocked(callerApp,
callerApp != null ? callerApp.info.packageName : null,
intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
requiredPermissions, appOp, null, serialized, sticky,
callingPid, callingUid, userId);
Binder.restoreCallingIdentity(origId);
return res;
}
}
因为broadcastIntentLocked的代码比较多,这里就不贴出来了,但有必要提一下:
// By default broadcasts do not go to stopped apps.
intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
从Android3.1开始广播为Intent增加了两个标记:
1.FLAG_EXCLUDE_STOPPED_PACKAGES 表示不包含用户没有启动过或强制停止过的应用,这个时候广播不会发给应用
2.FLAG_INCLUDE_STOPPED_PACKAGES 表示包含用户没有启动过或强制停止过的应用,这个时候广播会发给应用
当这两种标记共存的时候以FLAG_INCLUDE_STOPPED_PACKAGES为准,当我们没有指定flags的时候系统默认发送的时候设置的是FLAG_EXCLUDE_STOPPED_PACKAGES,这样做是为了防止广播无意间或者在不必要的时候调起已经停止运行的应用,如果我们的确需要调起未启动过的应用,那么只能为广播的Intent添加FLAG_INCLUDE_STOPPED_PACKAGES,而这也只能是我们自己定义的广播,系统的广播我们也是没有办法修改的。
在方法的最后将符合的广播放到队列中,然后将广播发送出:
queue.enqueueOrderedBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
下面看一下scheduleBroadcastsLocked的实现:
public void scheduleBroadcastsLocked() {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["
+ mQueueName + "]: current="
+ mBroadcastsScheduled);
if (mBroadcastsScheduled) {
return;
}
mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
mBroadcastsScheduled = true;
}
在发送消息后,handler收到消息后,调用了processNextBroadcast(true);
在方法里面对无需广播的处理如下:
// First, deliver any non-serialized broadcasts right away.
while (mParallelBroadcasts.size() > 0) {
r = mParallelBroadcasts.remove(0);
r.dispatchTime = SystemClock.uptimeMillis();
r.dispatchClockTime = System.currentTimeMillis();
final int N = r.receivers.size();
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing parallel broadcast ["
+ mQueueName + "] " + r);
for (int i=0; i<N; i++) {
Object target = r.receivers.get(i);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Delivering non-ordered on [" + mQueueName + "] to registered "
+ target + ": " + r);
deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);
}
addBroadcastToHistoryLocked(r);
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast ["
+ mQueueName + "] " + r);
}
然后通过deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);将广播发送出去,
在这deliverToRegisteredReceiverLocked方法内,通过下面方法完成具体的发送:
performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
new Intent(r.intent), r.resultCode, r.resultData,
r.resultExtras, r.ordered, r.initialSticky, r.userId);
private static void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
Intent intent, int resultCode, String data, Bundle extras,
boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
// Send the intent to the receiver asynchronously using one-way binder calls.
if (app != null) {
if (app.thread != null) {
// If we have an app thread, do the call through that so it is
// correctly ordered with other one-way calls.
app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
data, extras, ordered, sticky, sendingUser, app.repProcState);
} else {
// Application has died. Receiver doesn't exist.
throw new RemoteException("app.thread must not be null");
}
} else {
receiver.performReceive(intent, resultCode, data, extras, ordered,
sticky, sendingUser);
}
}
这时就到ApplicationThread了,
public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
int resultCode, String dataStr, Bundle extras, boolean ordered,
boolean sticky, int sendingUser, int processState) throws RemoteException {
updateProcessState(processState, false);
receiver.performReceive(intent, resultCode, dataStr, extras, ordered,
sticky, sendingUser);
}
这里没有像activity和service那样直接sendmessage发送消息,而是调用了InnerReceiver的performReceive方法
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
LoadedApk.ReceiverDispatcher rd = mDispatcher.get();
if (ActivityThread.DEBUG_BROADCAST) {
int seq = intent.getIntExtra("seq", -1);
Slog.i(ActivityThread.TAG, "Receiving broadcast " + intent.getAction() + " seq=" + seq
+ " to " + (rd != null ? rd.mReceiver : null));
}
if (rd != null) {
rd.performReceive(intent, resultCode, data, extras,
ordered, sticky, sendingUser);
} else {
// The activity manager dispatched a broadcast to a registered
// receiver in this process, but before it could be delivered the
// receiver was unregistered. Acknowledge the broadcast on its
// behalf so that the system's broadcast sequence can continue.
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing broadcast to unregistered receiver");
IActivityManager mgr = ActivityManagerNative.getDefault();
try {
if (extras != null) {
extras.setAllowFds(false);
}
mgr.finishReceiver(this, resultCode, data, extras, false, intent.getFlags());
} catch (RemoteException e) {
Slog.w(ActivityThread.TAG, "Couldn't finish broadcast to unregistered receiver");
}
}
}
继续跟进ReceiverDispatcher的performReceive方法:
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
if (ActivityThread.DEBUG_BROADCAST) {
int seq = intent.getIntExtra("seq", -1);
Slog.i(ActivityThread.TAG, "Enqueueing broadcast " + intent.getAction() + " seq=" + seq
+ " to " + mReceiver);
}
Args args = new Args(intent, resultCode, data, extras, ordered,
sticky, sendingUser);
if (!mActivityThread.post(args)) {
if (mRegistered && ordered) {
IActivityManager mgr = ActivityManagerNative.getDefault();
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing sync broadcast to " + mReceiver);
args.sendFinished(mgr);
}
}
}
activityThread是一个handler,其实他就是ActivityThread中mH。
Args是ReceiverDispatcher的内部类,实现了Runnable接口。
public void run() {
final BroadcastReceiver receiver = mReceiver;
......
try {
......
receiver.setPendingResult(this);
receiver.onReceive(mContext, intent);
} catch (Exception e) {
......
}
......
}
可以看到在这里BroadcastReceiver的onReceive方法被执行了,这样就完成了广播的接收工作。