您的位置:首页 > 移动开发 > Android开发

Android BroadcastReceiver的注册

2017-08-02 17:12 363 查看

广播接收器的注册

广播接收器,是用来接收系统和应用发出的广播,常见的是开机广播,可以用于实现开机启动服务的功能,还有网络变化,电池电量变化等等均会发出相应的广播。Android系统中的广播设计的很好,对于开发者而言非常容易上手。

静态注册

不管该应用程序是否处于活动状态,都会进行监听,比如某个程序是监听内存的使用情况的,当在手机上安装好后,不管该应用程序是处于什么状态,都会执行该监听方法中的内容。

静态注册即在AndroidManifest中注册广播接收器

//TODO 解释各个属性的意义

<receiver android:enabled=["true" | "false"]
android:exported=["true" | "false"]
android:icon="drawable resource"
android:label="string resource"
android:name="string"
android:permission="string"
android:process="string" >
. . .
</receiver>


实例:

<!-- AndroidManifest.xml -->
<receiver
android:name=".MyReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="MLY" />
</intent-filter>
</receiver>


动态注册

在代码中进行注册后,当应用程序关闭后,就不再进行监听,因此一般在Activity创建的时候注册,在Activity销毁的时候取消注册。

注册

IntentFilter filter = new IntentFilter("");
BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
}
};

registerReceiver(receiver, filter);


取消注册

unregisterReceiver(receiver);


静态广播接收器的注册流程

Android系统启动后,PackageManagerService会扫描各个APK的AndroidManifest.xml,并解析其中的Receiver标签,最后将Receiver信息保存在PackageManagerService的receivers中。AMS就可以调用PMS的queryIntentReceivers函数获取到ResolveInfo列表,一个ResolveInfo代表一个BroadcastReceiver,AMS就可以通过ResolveInfo启动未启动的BroadcastReceiver所在的进程,然后将广播分发给静态注册的BroadcastReceiver。

动态广播接收器注册流程



从序列图上看,动态广播的注册流程还是很简单的,在Activity里调用registerReceiver实际是调用了ComtextImpl的registerReceiver函数,该函数会调用registerReceiverInternal函数

Step 1

ComtextImpl.registerReceiverInternal

该函数定义在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 {
......
}
try {
//调用了ActivityManagerNative的registerReceiver函数
return ActivityManagerNative.getDefault().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName,
rd, filter, broadcastPermission, userId);
} catch (RemoteException e) {
return null;
}
}


该函数主要是获取一个IIntentReceiver对象,最后AMS在分发广播的时候会通过这个对象回调到当前这个进程,一般情况下Receiver,mPackageInfo和context不为空,所以该对象是通过mPackageInfo.getReceiverDispatcher来获取的。同时scheduler指定了最后回调的函数运行的Handler,如果未指定,则是在主线程中。最后调用ActivityManagerNative的registerReceiver函数,该函数通过binder调用到AMS的registerReceiver。

Step 2

LoadedApk.getReceiverDispatcher函数

该函数定义在frameworks/base/core/java/android/app/LoadedApk.java函数中

public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,
Context context, Handler handler,
Instrumentation instrumentation, boolean registered) {
synchronized (mReceivers) {
LoadedApk.ReceiverDispatcher rd = null;
ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;
if (registered) {
map = mReceivers.get(context);
if (map != null) {
rd = map.get(r);
}
}
//一个BroadcastReceiver可以注册多次,如果是多次注册,这里的rd不为空,返回的IIntentReceiver也是相同的
if (rd == null) {
rd = new ReceiverDispatcher(r, context, handler,
instrumentation, registered);
if (registered) {
if (map == null) {
map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
mReceivers.put(context, map);
}
map.put(r, rd);
}
} else {
rd.validate(context, handler);
}
rd.mForgotten = false;
return rd.getIIntentReceiver();
}
}


registered参数表示是否是注册receiver函数调用。mReceivers是一个ArrayMap类型,key是Context,value也是ArrayMap类型,其中key是BroadcastReceiver,value是ReceiverDispatcher。

如果registered为true,函数首先会检查mReceivers是否已经注册了相同的BroadcastReceiver对象,如果是则直接返回该对象对应的ReceiverDispatcher中的IIntentReceiver。如果没有注册,则实例化一个ReceiverDispatcher对象,并将其加入到mReceivers中,然后其中的IIntentReceiver对象。

从这里可以看到一个BroadcastReceiver是可以注册多次的,这里可以指定不同的IntentFilter,但是每个BroadcastReceiver只能指定一个Handler。

如果registered为false,则直接实例化一个ReceiverDispatcher并返回,这里并不会将ReceiverDispatcher对象保存在mReceivers里。

Step 3

ActivityManagerService.registerReceiver

该函数定义在frameworks/base/core/java/com/android/server/am/ActivityManagerService.java

synchronized (this) {
ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
//同一个BroadcastReceiver第一次注册时,rl为null
if (rl == null) {
rl = new ReceiverList(this, callerApp, callingPid, callingUid,
userId, receiver);
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);
mReceiverResolver.addFilter(bf);
}


这里只截取主要的代码

1. 如果一个BroadcastReceiver是第一次注册,则rl为空,会实例化一个ReceiverList,该类型为ArrayList,用于保存同一个BroadcastReceiver注册的多个IntentFilter,然后将实例化ReceiverList和IIntentReceiver的binder对象保存mRegisteredReceivers Map中。

2. 如果非第一次注册,则会检查pid,uid,userId是否一致,若不一致则抛出异常

3. 实例化一个BroadcastFilter,并加入ReceiverList,同时会保存在mReceiverResolver中

至此,动态广播的注册结束。AMS中mReceiverResolver里包含了BroadcastFilter,这个与IntentFilter一一对应,BroadcastFilter里包含了IntentFilter和IIntentReceiver。 AMS中mRegisteredReceivers中,Key是IIntentReceiver的binder对象,Value是一个列表,包含了该对象对应的多个BroadcastFilter(与IntentFilter一一对应)。

动态广播接收器取消注册流程



Activity调用unregisterReceiver实际调用了ContextImpl的unregisterReceiver函数,该函数中调用LoadedApk的forgetReceiverDispatcher函数以及调用ActivityManagerNative的unregisterReceiver

Step 1

LoadedApk.forgetReceiverDispatcher

该函数定义在frameworks/base/core/java/android/app/LoadedApk.java函数中

public IIntentReceiver forgetReceiverDispatcher(Context context,
BroadcastReceiver r) {
synchronized (mReceivers) {
ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = mReceivers.get(context);
LoadedApk.ReceiverDispatcher rd = null;
if (map != null) {
rd = map.get(r);
if (rd != null) {
map.remove(r);
if (map.size() == 0) {
mReceivers.remove(context);
}
rd.mForgotten = true;
return rd.getIIntentReceiver();
}
}
}
}


该函数主要是将mReceivers中保存的BroadcastReceiver删除,并返回IIntentReceiver对象

Step 2

ActivityManagerService.unregisterReceiver

该函数定义在frameworks/base/core/java/com/android/server/am/ActivityManagerService.java

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);
}
removeReceiverLocked(rl);
if (rl.linkedToDeath) {
rl.linkedToDeath = false;
rl.receiver.asBinder().unlinkToDeath(rl, 0);
}
}
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}


该函数首先判断接收器当前是否正在接收广播,若正在接收广播,需要处理finishReceiverLocked处理。然后调用removeReceiverLocked函数移除IIntentReceiver

Step 3

ActivityManagerService.removeReceiverLocked

该函数定义在frameworks/base/core/java/com/android/server/am/ActivityManagerService.java

void removeReceiverLocked(ReceiverList rl) {
mRegisteredReceivers.remove(rl.receiver.asBinder());
for (int i = rl.size() - 1; i >= 0; i--) {
mReceiverResolver.removeFilter(rl.get(i));
}
}


从前面动态广播的注册流程来看,当一个IIntentReceiver注册后,mRegisteredReceivers保存了IIntentReceiver对象及其所对应的BroadcastFilter列表。 mReceiverResolver会保存BroadcastFilter。因此这里取消注册时候,需要对应删除两个列表。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息