Android源码分析 BroadcastReceiver 注册过程
Posted 小图包
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android源码分析 BroadcastReceiver 注册过程相关的知识,希望对你有一定的参考价值。
广播的注册过程
首先,想要使用广播就得通过 registerReceiver 方法注册一个广播,最终也是在 Activity 的父类 ContextWrapper 中实现,代码如下:
//ContextWrapper.java
/**
* 应用调用注册广播的函数
* @param receiver The BroadcastReceiver to handle the broadcast.
* @param filter Selects the Intent broadcasts to be received.
*
* @return
*/
@Override
public Intent registerReceiver(
BroadcastReceiver receiver, IntentFilter filter)
//mBase 是 Context的引用
return mBase.registerReceiver(receiver, filter);
直接看 Context 的实现类 ContextImpl 中的实现,代码如下:
/**
* Context 调用
* @param receiver The BroadcastReceiver to handle the broadcast.
* @param filter Selects the Intent broadcasts to be received.
*
* @return
*/
@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(), 0);
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
IntentFilter filter, String broadcastPermission,
Handler scheduler, Context context, int flags)
IIntentReceiver rd = null;
if (receiver != null)
1
if (mPackageInfo != null && context != null)
if (scheduler == null)
//获取 ActivityThread H
scheduler = mMainThread.getHandler();
//获取 IIntentReceiver 对象,通过它与 AMS 交互,并且通过 Handler 传递消息
rd = mPackageInfo.getReceiverDispatcher(
receiver, context, scheduler,
2
mMainThread.getInstrumentation(), true);
else
if (scheduler == null)
scheduler = mMainThread.getHandler();
3
rd = new LoadedApk.ReceiverDispatcher(
receiver, context, scheduler, null, true).getIIntentReceiver();
try
//调用 AMS 的 registerReceiver
4
final Intent intent = ActivityManager.getService().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName, rd, filter,
broadcastPermission, userId, flags);
if (intent != null)
intent.setExtrasClassLoader(getClassLoader());
intent.prepareToEnterProcess();
return intent;
catch (RemoteException e)
throw e.rethrowFromSystemServer();
总结如上步骤
1 判断 mPackageInfo 与 context 是否为空
2 通过 mPackageInfo 方法调用 getReceiverDispatch 方法获取 rd 对象
3 处的代码来创建 rd 对象
2 步代码主要就是来获取 IIntentReceiver 对象,它是一个 Binder 接口,用于广播的跨进程通信,它在在 LoadedApk.ReceiverDispatcher.InnerReceiver 中实现,代码如下所示:
//LoadedApk.java
static final class ReceiverDispatcher
final static class InnerReceiver extends IIntentReceiver.Stub
final WeakReference<LoadedApk.ReceiverDispatcher> mDispatcher;
final LoadedApk.ReceiverDispatcher mStrongRef;
InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong)
mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd);
mStrongRef = strong ? rd : null;
....
回到 registerReceiverInternal 方法,在注释 4 处调用 AMS 代理类 IActivityManager 的 registerReceiver 方法,通过进程间通信最后在 AMS 类的 registerReceiver 方法,并将 IIntentReceiver 类型的 rd 引用也一并传递进 AMS 中,这里之所以不直接传入客户端的 BroadcastReceiver 而是传入 IIntetnReceiver ,因为注册广播是一个跨进程的过程,需要具有跨进程通信功能的 IIntentReceiver ,你也可以把它理解为一个中间者,registerReceiver 方法内部比较多,这里分为两部分
我们先看
registerReceiver 方法的第一部分
//AMS.java
/**
* 这里是通过 Binder 通知调用
* @return Intent
*/
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission, int userId,
int flags)
enforceNotIsolatedCaller("registerReceiver");
ArrayList<Intent> stickyIntents = null;
ProcessRecord callerApp = null;
final boolean visibleToInstantApps
= (flags & Context.RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0;
int callingUid;
int callingPid;
boolean instantApp;
synchronized(this)
if (caller != null)
1
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 != 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();
instantApp = isInstantApp(callerApp, callerPackage, callingUid);
userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
ALLOW_FULL_ONLY, "registerReceiver", callerPackage);
2
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)
//根据actions 列表和 userIds 得到所有的粘性广播的 intent,并在注释 3 处传入到 stickyIntents 中
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>();
3
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);
// Don't provided intents that aren't available to instant apps.
if (instantApp &&
(intent.getFlags() & Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS) == 0)
continue;
//开始匹配
if (filter.match(resolver, intent, true, TAG) >= 0)
if (allSticky == null)
allSticky = new ArrayList<Intent>();
4
allSticky.add(intent);
....//后面代码 第二部分 讲解
return sticky;
总结如上
1 通过 getRecordForAppLocked 方法得到 ProcessRecord 类型的 callerApp 对象,它用于描述请求 AMS 注册广播接收者的 Activity 所在的应用程序进程
2 处根据传入的 IntentFilter 类型的 filter 得到一个 actions 列表,根据 actions 列表和 userIds 得到所有的粘性广播的 intent
3 处传入到 stickyIntents 中。接下来从 stickyIntent 中匹配传入的参数 filter 的粘性广播的 intent
4 处将这些 intent 存入到 allSticky 列表中,从这里可以看出粘性广播是存储在 AMS 中的。
registerReceiver 方法的 第二部分
接下来看 AMS 的 registerReceiver 方法剩余内容,代码如下所示:
//AMS.java
/**
* 这里是通过 Binder 通知调用
* @return Intent
*/
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission, int userId,
int flags)
...
synchronized (this)
...
1
ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
if (rl == null) //如果为空调用注释 2
2
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)
...
3
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
permission, callingUid, userId, instantApp, visibleToInstantApps);
4
rl.add(bf);
if (!bf.debugCheck())
Slog.w(TAG, "==> For Dynamic broadcast");
5
mReceiverResolver.addFilter(bf);
...
总结 如上代码
1. 获取 ReceiverList
2. 创建一个 ReceiverList 对象,它继承自 ArrayList,用于存放广播接收者
3 构建 BroadcastFilter 对象,并且将 广播接收者列表 ReceiverList 添加到进去 * 用于描述注册的广播接收者
4. 通过 add 函数将自身添加到广播接收者列表中
5. 将 BroadcastFilter 添加到 IntentResolver 类型的 mReceiverResolver 中
在注释 1 处获取到了 ReceiverList 列表,如果为空则在注释 2 处创建一个新的接收列表 ReceiverList 对象,它继承自 ArrayList,用来存储广播接收者。在注释 3 处创建 BroadcastFilter 并传入此前创建的 ReceiverList ,BroadcastFilter 用来描述注册的广播接收者,并在注释 4 处通过 add 方法将自身添加到 ReceiverList 中。在注释 5 处将 BroadcastFilter 添加到 IntentResolver 类型的 mReceiverResolver 中,这样当 AMS 接收到广播时就可以从 mReceiverResolver 中找到对应的广播接收者了。从而达到注册广播的目的。
以上是关于Android源码分析 BroadcastReceiver 注册过程的主要内容,如果未能解决你的问题,请参考以下文章
Android 音频源码分析——AudioTrack设备选择