说说Android的广播
对于Activity的启动流程,我们已经有了几个版本的分析了。这里我们分析一个更容易一些的,四大组件中最简单的Broadcast Receiver。
关于Broadcast,有几点需要了解。首先是广播的类型,然后是广播的发送方法,最后是广播是如何被接收的。这三者相辅相承的,比如普通广播和有序广播只有在详细了解了广播的接收过程了之后,才能真正明白它的含义。
广播的类型:普通广播,有序广播和粘性广播
普通的广播是不在意顺序的,最简单的理解是同时可以收到这个广播。如果应用是动态注册这个广播的,且广播发送时这个进程还活着,那么当然可以并发的把广播尽快地传送出去是最好的。
但是,如果是通过AndroidManifest.xml静态注册的情况,也就是说这个广播首先要把一个进程启动起来,这时并发启动很多进程就是个问题了。Android目前的做法是,对这种静态的广播接收者,自动按有序广播的方式来串行处理。但是这对应用是透明的,应用不能假设系统已经把静态的无序广播当成有序广播来处理。
这个时候讲粘性广播有福了,因为从Android 5.0(API 21)开始,因为安全性的问题,官方已经正式废弃了粘性广播。
普通广播的发送
Context类提供两个方法可以用于发送普通广播:
- sendBroadcast(Intent intent);
- sendBroadcast(Intent intent, String receiverPermission);
差别是第二个设置权限。
发给特定的用户:
- sendBroadcastAsUser(Intent intent, UserHandle user);
- sendBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission);
有序广播的发送
有序广播因为要处理消息的处理结果,所以要复杂一些。
- sendOrderedBroadcast(Intent intent, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras);
如果只是想让广播可以按优先级来收取,并不在意处理的结果,可以用下面的版本:
- sendOrderedBroadcast(Intent intent, String receiverPermission);
同样,在多用户环境下,也可以选择给哪个用户发广播:
- sendOrderedBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras);
过时的粘性广播
不管是普通的还是有序的广播都对应有粘性的版本:
- sendStickyBroadcast(Intent intent);
- sendStickyBroadcastAsUser(Intent intent, UserHandle user);
- sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras);
- sendStickyOrderedBroadcastAsUser(Intent intent, UserHandle user, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras);
以上的API都是定义于Context类中:https://developer.android.com/reference/android/content/Context.html
首先我们先看看发送端是如何发送的。
我们首先先放一个大图,让大家先有一个直观的印象,不管普通广播、有序广播、粘性广播如何组合,最终都汇集到一个大方法中。
应用发送广播的过程
普通广播的发送
我们先看应用发送普通广播的一个简单的例子:
Intent intent = new Intent("android.intent.action.TestAction");
intent.putExtra(EXTRA_TIME, System.currentTimeMillis());
MainActivity.this.sendBroadcast(intent);
非常简单,调用ContentWrapper的sendBroadcast方法就可以了。
然后我们顺藤摸瓜就好了。
Activity中的sendBroadcast,实际上调用的是:
void android.content.ContextWrapper.sendBroadcast(Intent intent);
ContextWrapper.sendBroadcast
我们来看frameworks/base/core/java/android/content/ContextWrapper.java中对sendBroadcast的定义:
@Override
public void sendBroadcast(Intent intent) {
mBase.sendBroadcast(intent);
}
ContextWrapper只是一个包装,真正的实现在ContextImpl中
ContextImpl.sendBroadcast
我们来看/frameworks/base/core/java/android/app/ContextImpl.java中真正实现sendBroadcast的功能:
@Override
public void sendBroadcast(Intent intent) {
...
try {
...
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);
}
}
它会通过IPC去调用AMS的broadcastIntent。由于我们这个普通的广播的方法参数最少,所以好多都是传null。
ActivityManagerService.broadcastIntent
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的真正逻辑放一下,先看看有序广播是如何发送的。
ContextWrapper.sendOrderedBroadcast
Context是abstract方法,调用的是ContextWrapper的实现:
@Override
public void sendOrderedBroadcast(Intent intent,
String receiverPermission) {
mBase.sendOrderedBroadcast(intent, receiverPermission);
}
跟普通广播一样,还是会调用到ContextImpl.sendOrderedBroadcast
public void sendOrderedBroadcast(Intent intent, String receiverPermission) {
warnIfCallingFromSystemProcess();
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
String[] receiverPermissions = receiverPermission == null ? null
: new String[] {receiverPermission};
try {
intent.prepareToLeaveProcess();
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
null, true, false, getUserId());
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
}
有序广播调用broadcastIntent的区别在于serialized参数,普通广播为false,有序广播为true.
原型为:
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) {
多参数的版本
前面讲过带有回调的版本,我们看看它是如何实现的:
@Override
public void sendOrderedBroadcast(
Intent intent, String receiverPermission, BroadcastReceiver resultReceiver,
Handler scheduler, int initialCode, String initialData,
Bundle initialExtras) {
mBase.sendOrderedBroadcast(intent, receiverPermission,
resultReceiver, scheduler, initialCode,
initialData, initialExtras);
}
当然还是调用ContextImpl.sendOrderedBroadcast
@Override
public void sendOrderedBroadcast(Intent intent,
String receiverPermission, BroadcastReceiver resultReceiver,
Handler scheduler, int initialCode, String initialData,
Bundle initialExtras) {
sendOrderedBroadcast(intent, receiverPermission, AppOpsManager.OP_NONE,
resultReceiver, scheduler, initialCode, initialData, initialExtras, null);
}
这次变成只是一个封装了,它会调用一个更多参数的版本:
void sendOrderedBroadcast(Intent intent,
String receiverPermission, int appOp, BroadcastReceiver resultReceiver,
Handler scheduler, int initialCode, String initialData,
Bundle initialExtras, Bundle options) {
warnIfCallingFromSystemProcess();
IIntentReceiver rd = null;
if (resultReceiver != null) {
if (mPackageInfo != null) {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
rd = mPackageInfo.getReceiverDispatcher(
resultReceiver, getOuterContext(), scheduler,
mMainThread.getInstrumentation(), false);
} else {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
rd = new LoadedApk.ReceiverDispatcher(
resultReceiver, getOuterContext(), scheduler, null, false).getIIntentReceiver();
}
}
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
String[] receiverPermissions = receiverPermission == null ? null
: new String[] {receiverPermission};
try {
intent.prepareToLeaveProcess();
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, rd,
initialCode, initialData, initialExtras, receiverPermissions, appOp,
options, true, false, getUserId());
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
}
这次是一个全参数调用broadcastIntent的版本了,除了sticky就齐了
粘性广播
我们也不绕圈子了,直接看ContextImpl.sendStickyBroadcast.
@Override
@Deprecated
public void sendStickyBroadcast(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, true,
getUserId());
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
}