BroadcastReceiver详解
使用方式
在Manifest.xml文件中注册
<receiver
android:name=".test.broadcast.MyTestReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
<action android:name="android.intent.action.SCREEN_ON" />
<action android:name="android.intent.action.SCREEN_OFF" />
<action android:name="android.intent.action.INPUT_METHOD_CHANGED" />
<action android:name="android.intent.action.CONFIGURATION_CHANGED" />
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
<action android:name="android.intent.action.USER_PRESENT" />
</intent-filter>
</receiver>
在Manifesh.xml文件中注册自定义的BroadcastReceiver,当意图过滤器中的动作发生时,会回调BroadcastReceiver中的onRecevie方法.
public class MyTestReceiver extends BroadcastReceiver {
private static final String TAG = "MyTestReceiver";
@Override
public void onReceive(Context context, Intent intent) {
if (intent != null) {
String action = intent.getAction();
Log.d(TAG, "onReceive: receive action is " + action);
}
}
}
代码中动态注册
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent == null ? "" : intent.getAction();
if (Intent.ACTION_SCREEN_OFF.equalsIgnoreCase(action)) {
Log.i(TAG, "onReceive: 屏幕关闭");
}
context.unregisterReceiver(this);
}
}, intentFilter);
在Activity上下文中调用registerReceiver方法,动态注册一个广播接收者,同时指定了意图过滤器。
这里要注意注册的BroadcastReceiver最好是静态类,防止内存泄漏。否则要及时取消注册。
原理
注册通知
静态注册
通过PackageManagerService安装应用时,会解析安装包中的Manifest.xml文件,存储解析到Receiver标签对应的广播接收器。
通过Unix Socket接收需要安装的Apk文件
//android/os/FileBridge.java
public class FileBridge extends Thread {
private static final int MSG_LENGTH = 8;
private final FileDescriptor mServer = new FileDescriptor();
private final FileDescriptor mClient = new FileDescriptor();
private FileDescriptor mTarget;
//构造函数
//创建Unix Domain Socket的客户端和服务端,然后会将代表客户端的文件描述符发送给客户端进程
public FileBridge() {
try {
Os.socketpair(AF_UNIX, SOCK_STREAM, 0, mServer, mClient);
} catch (ErrnoException e) {
throw new RuntimeException("Failed to create bridge");
}
}
//mTarget文件描述符用来存储客户端进程发送过来的Apk文件数据
public void setTargetFile(FileDescriptor target) {
mTarget = target;
}
public FileDescriptor getClientSocket() {
return mClient;
}
//这是一个线程,不停监听Socket的Server端
@Override
public void run() {
final byte[] temp = new byte[8192];//读缓冲区
//读取Socket的Server端,如果没有数据会阻塞
//每次先读取8字节,前4个字节代表数据类型,后4四个字节代表数据长度
while (IoBridge.read(mServer, temp, 0, MSG_LENGTH) == MSG_LENGTH) {
//按照大端序读取前4字节
final int cmd = Memory.peekInt(temp, 0, ByteOrder.BIG_ENDIAN);
//数据写入,将apk文件数据写入到mTraget代表的文件描述中
if (cmd == CMD_WRITE) {
int len = Memory.peekInt(temp, 4, ByteOrder.BIG_ENDIAN);
while (len > 0) {
int n = IoBridge.read(mServer, temp, 0, Math.min(temp.length, len));
if (n == -1) {
throw new IOException(
"Unexpected EOF; still expected " + len + " bytes");
}
IoBridge.write(mTarget, temp, 0, n);
len -= n;
}
//数据同步,并回显给客户端确认
} else if (cmd == CMD_FSYNC) {
Os.fsync(mTarget);
IoBridge.write(mServer, temp, 0, MSG_LENGTH);
//关闭消息通道,并回显给客户端确认
} else if (cmd == CMD_CLOSE) {
Os.fsync(mTarget);
Os.close(mTarget);
mClosed = true;
IoBridge.write(mServer, temp, 0, MSG_LENGTH);
break;
}
}
}
}
验证APK合法性
- 包名、版本号
- 签名(V2+V1)sudo
- 其他应用验证package-verifier
- 应用申请的权限,需要用户确认
- 拷贝安装包到data/data/{包名}/目录下,并且复制Native库到lib目录下
解析Apk文件
- 添加apk文件路径到AssetManager管理中
- 解析Manifest.xml文件信息到Package类中,区分是多个apk文件安装,还是单个apk文件安装
- 验证该安装包是否只用于测试
- 为Package类填充证书和签名信息
- 是否覆盖安装,需要验证签名是否一致、包配置是否一致等。还要替换应用资源
//frameworks/base/core/java/android/content/pm/PackageParser.java
private boolean parseBaseApplication(Package owner, Resources res,
XmlResourceParser parser, int flags, String[] outError)
...
final int innerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth))
{
String tagName = parser.getName();
if (tagName.equals("activity")) {
Activity a = parseActivity(owner, res, parser, flags, outError,
false,owner.baseHardwareAccelerated);
owner.activities.add(a);
} else if (tagName.equals("receiver")) {
Activity a = parseActivity(owner, res, parser, flags, outError, true, false);
owner.receivers.add(a);
} else if (tagName.equals("service")) {
...
}
}
从上面解析过程可以看出,Android系统把Activity和BroadcastReceiver都当做了Activity类型来存储的。
parseActivity方法中,还会继续解析里面的intent-filter标签,以及其action/category/data等子标签。
所有从Manifest.xml文件中解析出来的数据,会存储到Package类中。
public final static class Package {
public String packageName;
public final ArrayList<Activity> activities = new ArrayList<Activity>(0);
public final ArrayList<Activity> receivers = new ArrayList<Activity>(0);
...
}
保存组件信息
将Package类中的四大组件信息以及整个Package信息保存到PackageManagerService中。同时还会保存到Settings类中,Settings类代表了系统的动态设置信息。
//PackageManagerService.java
//系统中所有程序信息
final ArrayMap<String, PackageParser.Package> mPackages =
new ArrayMap<String, PackageParser.Package>();
// 系统中所有程序的四大组件信息
final ActivityIntentResolver mActivities =
new ActivityIntentResolver();
final ActivityIntentResolver mReceivers =
new ActivityIntentResolver();
final ServiceIntentResolver mServices = new ServiceIntentResolver();
final ProviderIntentResolver mProviders = new ProviderIntentResolver();
//扫描包信息
private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg,
final int policyFlags, final int scanFlags, long currentTime, UserHandle user)
throws PackageManagerException {
// Add the new setting to mSettings
mSettings.insertPackageSettingLPw(pkgSetting, pkg);
// Add the new setting to mPackages
mPackages.put(pkg.applicationInfo.packageName, pkg);
...
//保存Package中的BroadcastReceiver信息
N = pkg.receivers.size();
r = null;
for (i=0; i<N; i++) {
//这里把Receiver当做Activity来处理
PackageParser.Activity a = pkg.receivers.get(i);
a.info.processName = fixProcessName(pkg.applicationInfo.processName,
a.info.processName, pkg.applicationInfo.uid);
mReceivers.addActivity(a, "receiver");
if ((policyFlags&PackageParser.PARSE_CHATTY) != 0) {
if (r == null) {
r = new StringBuilder(256);
} else {
r.append(' ');
}
r.append(a.info.name);
}
}
}
动态注册
通过ActivityManagerServer注册
//frameworks/base/core/java/android/app/ContextImpl.java
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 {
final Intent intent = ActivityManagerNative.getDefault().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName,
rd, filter, broadcastPermission, userId);
if (intent != null) {
intent.setExtrasClassLoader(getClassLoader());
intent.prepareToEnterProcess();
}
return intent;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
我们在Context上下文中调用registerReceiver(BroadcastReceiver, IntentFilter)方法动态注册一个广播时,会先保存到ActivityManagerServer中。
//frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>();
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
...
//如果调用者传入BroadcastReceiver是null,代表想要获得一个粘性广播
//粘性广播直接通过registerReceiver方法的返回值返回给调用者。
Intent sticky = allSticky != null ? allSticky.get(0) : null;
if (receiver == null) {
return sticky;
}
//将BroadcastReceiver存储到一个HashMap中
ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
if (rl == null) {
//ReceiverList是一个ArrayList子类,用来存储BroadcastReceiver和对应的过滤器
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);
}
//保存过滤器到IntentResolver中
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
permission, callingUid, userId);
rl.add(bf);
mReceiverResolver.addFilter(bf);
}
发送、接收通知
当应用程序或系统程序发出一个广播时,会调用到ActivityManagerService中,ActivityManagerService会从自己保存的BroadcastReceiver列表和PackageManagerService保存的BroadcastReceiver列表中找出符合意图过滤器的广播接收者,然后向其发送广播。
//ActivityManagerService.java
final int broadcastIntentLocked(ProcessRecord callerApp,
String callerPackage, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {
//1. 默认不给已经stopped停止的应用发送广播
//2. 如果没有完成开机操作,例如正在升级系统,只允许注册广播,不能发送
//3. 如果广播指定的目的用户,那么这个用户必须处于运行状态
//4. 验证是否是系统应用发出的受保护的广播。受保护的广播只能由系统应用发出。
//系统应用的判断标准是用户id:ROOT_UID/SYSTEM_UID/PHONE_UID/BLUETOOTH_UID/NFC_UID,或者是常驻应用
//5. 窗口小部件的配置和更新广播也不能由应用发出。由于历史原因,它们并不是受保护的广播,需要单独判断
//6. 对特殊广播的处理:由PackageManager发出的应用被删除或变更的广播,
//需要ActivityManagerService作出对应的操作,例如从最近使用应用中删除它们的Activity。
//特殊类型的广播处理完成后,就直接返回了
//7. 从PackageManagerService中查询符合过滤器条件的广播接受者List<ResolveInfo>,这里就是应用安装时,解析出来的静态注册的广播接受者。
List<ResolveInfo> newReceivers = AppGlobals.getPackageManager()
.queryIntentReceivers(intent, resolvedType, pmFlags,
user).getList();
//8. 从ActivityManagerService的ContentResolver中查询符合条件的广播接受者,这里代表通过代码动态注册的广播接受者
registeredReceivers = mReceiverResolver.queryIntent(intent,
resolvedType, false, userId);
//9. 将两个列表按照优先级合并成一个列表
//10. 构造一个BroadcastRecord,添加到队列BroadcastQueue中,然后开始调度执行。
}
//BroasdcastQueue.java
public final class BroadcastQueue {
final ArrayList<BroadcastRecord> mParallelBroadcasts = new ArrayList<>();
final ArrayList<BroadcastRecord> mOrderedBroadcasts = new ArrayList<>();
//11. BroadcastQueue中的广播被分为并发广播和顺序广播。并发广播会立即被执行,而不需要等待其他的广播执行完毕。顺序广播则会逐一执行。
//我们在注册广播时可以指定是否是顺序广播,或者粘性广播被认为是并发广播
final void processNextBroadcast(boolean fromMsg) {
BroadcastRecord r;
//12. 开始处理列表中的广播。先处理并发广播
//13. 如果处理广播的进程不存在,还需要等待进程的创建
//14. 处理动态注册的广播,在BroadcastQueue中以BroadcastFilter形式存储
performReceiveLocked(filter.receiverList.app,
filter.receiverList.receiver,
new Intent(r.intent), r.resultCode, r.resultData,
r.resultExtras, r.ordered, r.initialSticky, r.userId);
//15. 处理静态注册的广播,如果需要,还要提前唤起对应进程。如果目标进程开始运行后,再分发对应的广播
String targetProcess = info.activityInfo.processName;
ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
info.activityInfo.applicationInfo.uid, false);
app.addPackage(info.activityInfo.packageName,
info.activityInfo.applicationInfo.versionCode,
mService.mProcessStats);
processCurBroadcastLocked(r, app);
}
}
performReceiveLocked方法代码如下:
void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
Intent intent, int resultCode, String data, Bundle extras,
boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
if (app != null) {
app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
data, extras, ordered, sticky, sendingUser,
app.repProcState);
} else {
receiver.performReceive(intent, resultCode, data, extras, ordered,
sticky, sendingUser);
}
}
processCurBroadcastLocked方法如下:
private final void processCurBroadcastLocked(BroadcastRecord r,
ProcessRecord app) throws RemoteException {
app.thread.scheduleReceiver(new Intent(r.intent),
r.curReceiver,
mService.compatibilityInfoForPackageLocked(
r.curReceiver.applicationInfo),
r.resultCode,
r.resultData,
r.resultExtras,
r.ordered,
r.userId,
app.repProcState);
}
取消注册
取消动态注册的广播接收器
前面说过动态注册的广播接收器是存储在ActivityManagerService中的,那么取消注册的时候,也是通过跨进程通信,告知ActivityManagerService移除指定的广播接收器。
//ActivityManagerService.java
final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>();
public void unregisterReceiver(IIntentReceiver receiver) {
final long origId = Binder.clearCallingIdentity();
try {
boolean doTrim = false;
synchronized(this) {
ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
if (rl != null) {
final BroadcastRecord r = rl.curBroadcast;
if (r != null && r == r.queue.getMatchingOrderedReceiver(r)) {
final boolean doNext = r.queue.finishReceiverLocked(
r, r.resultCode, r.resultData, r.resultExtras,
r.resultAbort, false);
if (doNext) {
doTrim = true;
r.queue.processNextBroadcast(false);
}
}
if (rl.app != null) {
rl.app.receivers.remove(rl);
}
//从HashMap中移除BroadcastReceiver
//从IntentResolver中移除对应的过滤器
removeReceiverLocked(rl);
if (rl.linkedToDeath) {
rl.linkedToDeath = false;
rl.receiver.asBinder().unlinkToDeath(rl, 0);
}
}
}
// If we actually concluded any broadcasts, we might now be able
// to trim the recipients' apps from our working set
if (doTrim) {
trimApplications();
return;
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
void removeReceiverLocked(ReceiverList rl) {
mRegisteredReceivers.remove(rl.receiver.asBinder());
for (int i = rl.size() - 1; i >= 0; i--) {
mReceiverResolver.removeFilter(rl.get(i));
}
}
取消静态注册的广播接收器
静态注册的广播接收者理论上是无法取消注册的,因为它们是写死在Manifest.xml文件中,并在应用安装时就被记录到PackageManagerService中的,并且PackageManagerService类并没有提供移除Receiver信息的接口。
那么静态注册的广播接收器就不能取消了么?
当然不是,这里我们可以采用迂回的方式达到目的。
在从PackageManagerService中查询符合条件的广播接收器列表时,IntentResolver会检查每个过滤器对应的BroadcastReceiver是否被禁用了,如果被禁用了就会忽略掉这个BroadcastReceiver,从而达到和取消注册同样的效果。
下面是禁用或启动某个组件的代码:
//禁用xxx.class代表的组件,如果是广播接收器,则不会再接收到广播
ComponentName componentName = new ComponentName(this, xxx.class);
getPackageManager().setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
//启用xxx.class代表的组件
ComponentName componentName = new ComponentName(this, xxx.class);
getPackageManager().setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
当我们通过PackageManagerService禁用某个组件时,PackageManagerService做了两件事:
- 前面在静态注册流程中提高,从Manifest.xml解析出来的组件也会存储到Setting中一份,标识了组件的状态。所以第一个要先修改Setting中的组件状态。如下面代码所示:
//PackageManagerService.java
private void setEnabledSetting(final String packageName, String className, int newState,
final int flags, int userId, String callingPackage) {
PackageSetting pkgSetting;
pkgSetting = mSettings.mPackages.get(packageName);
pkgSetting.setEnabled(newState, userId, callingPackage);
}
将某个组件的状态改为禁用后,当再次向PackageManagerService查询静态注册的组件时就会把禁用掉的组件过滤掉,不会返回给ActivityManagerService。
//PackageManagerService.java
//ActivityIntentResolver类的方法
//根据ActivityIntentInfo创建一个ResolveInfo
@Override
protected ResolveInfo newResult(PackageParser.ActivityIntentInfo info,
int match, int userId) {
//向Setting类查询对应的组件是否被启用了
if (!mSettings.isEnabledAndMatchLPr(info.activity.info, mFlags, userId)) {
return null;
}
}
- 第二件事会调用通过ActivityManagerService发送一个Intent.ACTION_PACKAGE_CHANGED类型的广播,告知ActivityManagerService有个组件被禁用了,如果有正在排队发送的广播需要移除掉。
这个流程和普通应用发送广播的流程是一样的,区别是ActivityManagerService在检查到此类型的广播时,会自己处理掉,而不会真正的发送出去,为你Intent.ACTION_PACKAGE_CHANGED广播属于受保护的广播,只能系统代码能发送。
ActivityManagerService的处理逻辑如下:
//ActivityManagerService.java
private void cleanupDisabledPackageComponentsLocked(
String packageName, int userId, boolean killProcess, String[] changedClasses) {
//1. 遍历禁用的组件,从PackageManagerService中查询对应组件是否已经被禁用
//找出还没有被禁用的组件,如果已经禁用了就不管了
try {
enabled = pm.getComponentEnabledSetting(
new ComponentName(packageName, changedClass),
(userId != UserHandle.USER_ALL) ? userId : UserHandle.USER_SYSTEM);
} catch (Exception e) {
// As above, probably racing with uninstall.
return;
}
if (enabled != PackageManager.COMPONENT_ENABLED_STATE_ENABLED
&& enabled != PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
if (disabledClasses == null) {
disabledClasses = new ArraySet<>(changedClasses.length);
}
//disabledClasses代表了实际将要被禁用的组件
disabledClasses.add(changedClass);
}
//遍历ActivityManagerService存储的广播接收器,查看是否有需要禁用的
for (int i = mBroadcastQueues.length - 1; i >= 0; i--) {
mBroadcastQueues[i].cleanupDisabledPackageReceiversLocked(
packageName, disabledClasses, userId, true);
}
}
通过以上两个步骤就实现了禁用静态注册的广播接收器的目的。
与LocalBroadcastReceiver区别
LocalBroadcastReceiver就比较简单了,就是一个单例类+消息转发。不需要与ActivityManagerService或PackageManagerService服务进行跨进程通信。
LocalBroadcastReceiver是线程安全的,注册或发送广播时都是用Synchronized关键字包裹起来。
获取LocalBroadcastReceiver单例
@NonNull
public static LocalBroadcastManager getInstance(@NonNull Context context) {
synchronized (mLock) {
if (mInstance == null) {
mInstance = new LocalBroadcastManager(context.getApplicationContext());
}
return mInstance;
}
}
private LocalBroadcastManager(Context context) {
mAppContext = context;
mHandler = new Handler(context.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_EXEC_PENDING_BROADCASTS:
executePendingBroadcasts();
break;
default:
super.handleMessage(msg);
}
}
};
}
在构造LocalBroadcastReceiver实例时,会创建一个Handler,用于在主线程发送通知。
注册广播
private final HashMap<BroadcastReceiver, ArrayList<ReceiverRecord>> mReceivers
= new HashMap<>();
private final HashMap<String, ArrayList<ReceiverRecord>> mActions = new HashMap<>();
public void registerReceiver(@NonNull BroadcastReceiver receiver,
@NonNull IntentFilter filter) {
synchronized (mReceivers) {
//使用ReceiverRecord记录一个BroadcastReceiver和IntentFilter
//使用HashMap记录一个BroadcastReceiver与多个ReceiverRecord关系
//因为对同一个BroadcastReceiver允许注册多次
ReceiverRecord entry = new ReceiverRecord(filter, receiver);
ArrayList<ReceiverRecord> filters = mReceivers.get(receiver);
if (filters == null) {
filters = new ArrayList<>(1);
mReceivers.put(receiver, filters);
}
filters.add(entry);
for (int i=0; i<filter.countActions(); i++) {
String action = filter.getAction(i);
//使用HashMap记录每个Action和多个ReceiverRecord的关系
//便于发送广播时查找对应的Receiver
ArrayList<ReceiverRecord> entries = mActions.get(action);
if (entries == null) {
entries = new ArrayList<ReceiverRecord>(1);
mActions.put(action, entries);
}
entries.add(entry);
}
}
}
发送广播
在主线程接收广播
private final ArrayList<BroadcastRecord> mPendingBroadcasts = new ArrayList<>();
public boolean sendBroadcast(@NonNull Intent intent) {
...
//找出接受者,存储到列表mPendingBroadcasts中,等待被发送
mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
//向主线程发送一个消息,开始分发mPendingBroadcasts中的广播
if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
}
}
在当前调用线程接收广播
public void sendBroadcastSync(@NonNull Intent intent) {
if (sendBroadcast(intent)) {
//找到符合条件的接受者后,立即在当前线程去执行分发逻辑,而不等待主线程分发
//也就是此方法会BroadcastReceiver的onReceive方法执行后,才会返回
executePendingBroadcasts();
}
}