相信广播大家都有用过,也知道安卓广播的一些基础知识,如静态广播、动态广播、粘性广播等等,但相信很多人都不知道系统层面是怎样实现这些广播特性的,这篇文章就让我们来聊一聊安卓广播机制的系统实现原理.

静态广播的注册

静态广播是通过PackageManagerService在启动的时候扫描已安装的应用去注册的.

在PackageManagerService的构造方法中,会去扫描应用安装目录,顺序是先扫描系统应用安装目录再扫描第三方应用安装目录.

PackageManagerService.scanDirLI就是用于扫描目录的方法,由于代码比较少,这里我们直接把它贴了上来:

private void scanDirLI(File dir, int flags, int scanMode, long currentTime) {

String[] files = dir.list();

if (files == null) {

return;

}

int i;

for (i=0; i

File file = new File(dir, files[i]);

if (!isPackageFilename(files[i])) {

continue;

}

PackageParser.Package pkg = scanPackageLI(file,

flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null);

if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 &&

mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) {

file.delete();

}

}

}

private static final boolean isPackageFilename(String name) {

return name != null && name.endsWith(".apk");

}

可以看到,它通过File.list方法列出目录下的所有后缀为".apk"的文件传给scanPackageLI去处理.

而scanPackageLI(File scanFile,int parseFlags, int scanMode, long currentTime, UserHandle user)内部会调用它的重载方法scanPackageLI(PackageParser.Package pkg,int parseFlags, int scanMode, long currentTime, UserHandle user):

private PackageParser.Package scanPackageLI(File scanFile,int parseFlags, int scanMode, long currentTime, UserHandle user) {

...

final PackageParser.Package pkg = pp.parsePackage(scanFile,scanPath, mMetrics, parseFlags);

...

PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE, currentTime, user);

...

}

在这个scanPackageLIl里面会解析Package并且将AndroidManifest.xml中注册的BroadcastReceiver保存下来:

...

N = pkg.receivers.size();

r = null;

for (i=0; i

PackageParser.Activity a = pkg.receivers.get(i);

a.info.processName = fixProcessName(pkg.applicationInfo.processName,

a.info.processName, pkg.applicationInfo.uid);

mReceivers.addActivity(a, "receiver");

...

}

...

所以从上面获取静态广播的流程可以看出来:系统应用的广播先于第三方应用的广播注册,而安装在同一个目录下的应用的静态广播的注册顺序是按照File.list列出来的apk的顺序注册的.他们的注册顺序就决定了它们接收广播的顺序.

通过静态广播的注册流程,我们已经将静态广播注册到了PackageManagerService的mReceivers中,而我们可以使用PackageManagerService.queryIntentReceivers方法查询intent对应的静态广播

public List queryIntentReceivers(Intent intent, String resolvedType, int flags, int userId) {

if (!sUserManager.exists(userId)) return Collections.emptyList();

ComponentName comp = intent.getComponent();

if (comp == null) {

if (intent.getSelector() != null) {

intent = intent.getSelector();

comp = intent.getComponent();

}

}

if (comp != null) {

List list = new ArrayList(1);

ActivityInfo ai = getReceiverInfo(comp, flags, userId);

if (ai != null) {

ResolveInfo ri = new ResolveInfo();

ri.activityInfo = ai;

list.add(ri);

}

return list;

}

synchronized (mPackages) {

String pkgName = intent.getPackage();

if (pkgName == null) {

return mReceivers.queryIntent(intent, resolvedType, flags, userId);

}

final PackageParser.Package pkg = mPackages.get(pkgName);

if (pkg != null) {

return mReceivers.queryIntentForPackage(intent, resolvedType, flags, pkg.receivers,

userId);

}

return null;

}

}

动态广播的注册

我们调用Context.registerReceiver最后会调到ActivityManagerService.registerReceiver:

public Intent registerReceiver(IApplicationThread caller, String callerPackage, IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {

...

ReceiverList rl = (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());

...

BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, permission, callingUid, userId);

...

mReceiverResolver.addFilter(bf);

...

}

所以通过mReceiverResolver.queryIntent就能获得intent对应的动态广播了.

发送广播

ContextImpl.sendBroadcast中会调用ActivityManagerNative.getDefault().broadcastIntent()

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, false, false,getUserId());

} catch (RemoteException e) {

}

}

实际是调用ActivityManagerService.broadcastIntent:

public final int broadcastIntent(IApplicationThread caller,

Intent intent, String resolvedType, IIntentReceiver resultTo,

int resultCode, String resultData, Bundle map,

String requiredPermission, int appOp, 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, map, requiredPermission, appOp, serialized, sticky,

callingPid, callingUid, userId);

Binder.restoreCallingIdentity(origId);

return res;

}

}

ActivityManagerService.broadcastIntent中又会调用ActivityManagerService.broadcastIntentLocked,而broadcastIntentLocked中的关键代码如下:

// 静态广播

List receivers = null;

// 动态广播

List registeredReceivers = null;

if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)

== 0) {

// 查询静态广播

receivers = collectReceiverComponents(intent, resolvedType, users);

}

if (intent.getComponent() == null) {

// 查询动态广播

registeredReceivers = mReceiverResolver.queryIntent(intent,

resolvedType, false, userId);

}

final boolean replacePending =

(intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;

int NR = registeredReceivers != null ? registeredReceivers.size() : 0;

if (!ordered && NR > 0) {

final BroadcastQueue queue = broadcastQueueForIntent(intent);

BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,

callerPackage, callingPid, callingUid, resolvedType, requiredPermission,

appOp, registeredReceivers, resultTo, resultCode, resultData, map,

ordered, sticky, false, userId);

final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);

if (!replaced) {

// 发送动态广播

queue.enqueueParallelBroadcastLocked(r);

queue.scheduleBroadcastsLocked();

}

registeredReceivers = null;

NR = 0;

}

...

if ((receivers != null && receivers.size() > 0)

|| resultTo != null) {

BroadcastQueue queue = broadcastQueueForIntent(intent);

BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,

callerPackage, callingPid, callingUid, resolvedType,

requiredPermission, appOp, receivers, resultTo, resultCode,

resultData, map, ordered, sticky, false, userId);

boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);

if (!replaced) {

// 发送静态广播

queue.enqueueOrderedBroadcastLocked(r);

queue.scheduleBroadcastsLocked();

}

}

大家应该都有听说过动态广播会优先于静态广播,从上面的代码我们可以看到,这实际是因为安卓的源代码就是按这个顺序写的...

最后我们来看一下ActivityManagerService.collectReceiverComponents方法,实际上静态广播静态就是从PackageManagerService中查询的:

private List collectReceiverComponents(Intent intent, String resolvedType,

int[] users) {

...

List newReceivers = AppGlobals.getPackageManager()

.queryIntentReceivers(intent, resolvedType, STOCK_PM_FLAGS, user);

...

粘性广播的实现原理

ActivityManagerService.broadcastIntentLocked有下面这样一段代码,它将粘性广播存到了mStickyBroadcasts中。

if (sticky) {

...

ArrayMap> stickies = mStickyBroadcasts.get(userId);

if (stickies == null) {

stickies = new ArrayMap>();

mStickyBroadcasts.put(userId, stickies);

}

ArrayList list = stickies.get(intent.getAction());

if (list == null) {

list = new ArrayList();

stickies.put(intent.getAction(), list);

}

int N = list.size();

int i;

for (i=0; i

if (intent.filterEquals(list.get(i))) {

// This sticky already exists, replace it.

list.set(i, new Intent(intent));

break;

}

}

if (i >= N) {

list.add(new Intent(intent));

}

}

而ManagerService.registerReceiver会获取之前发送的粘性广播,再次发送给刚刚注册的receiver:

...

List allSticky = null;

// 获取符合的粘性广播

Iterator actions = filter.actionsIterator();

if (actions != null) {

while (actions.hasNext()) {

String action = (String)actions.next();

allSticky = getStickiesLocked(action, filter, allSticky,

UserHandle.USER_ALL);

allSticky = getStickiesLocked(action, filter, allSticky,

UserHandle.getUserId(callingUid));

}

} else {

allSticky = getStickiesLocked(null, filter, allSticky,

UserHandle.USER_ALL);

allSticky = getStickiesLocked(null, filter, allSticky,

UserHandle.getUserId(callingUid));

}

...

//向新注册的receiver发送粘性广播

if (allSticky != null) {

ArrayList receivers = new ArrayList();

receivers.add(bf);

int N = allSticky.size();

for (int i=0; i

Intent intent = (Intent)allSticky.get(i);

BroadcastQueue queue = broadcastQueueForIntent(intent);

BroadcastRecord r = new BroadcastRecord(queue, intent, null,

null, -1, -1, null, null, AppOpsManager.OP_NONE, receivers, null, 0,

null, null, false, true, true, -1);

queue.enqueueParallelBroadcastLocked(r);

queue.scheduleBroadcastsLocked();

}

}

...

getStickiesLocked即从mStickyBroadcasts中查询之前发送过的粘性广播

private final List getStickiesLocked(String action, IntentFilter filter,

List cur, int userId) {

final ContentResolver resolver = mContext.getContentResolver();

ArrayMap> stickies = mStickyBroadcasts.get(userId);

if (stickies == null) {

return cur;

}

final ArrayList list = stickies.get(action);

if (list == null) {

return cur;

}

int N = list.size();

for (int i=0; i

Intent intent = list.get(i);

if (filter.match(resolver, intent, true, TAG) >= 0) {

if (cur == null) {

cur = new ArrayList();

}

cur.add(intent);

}

}

return cur;

}

广播队列

从ActivityManagerService.broadcastIntentLocked中我们可以看到,实际上它不是直接将广播发送到BroadcastReceiver中的.

而是将他包装到BroadcastRecord中,再放进BroadcastQueue:

BroadcastQueue queue = broadcastQueueForIntent(intent);

BroadcastRecord r = new BroadcastRecord(queue, intent, null,

null, -1, -1, null, null, AppOpsManager.OP_NONE, receivers, null, 0,

null, null, false, true, true, -1);

queue.enqueueParallelBroadcastLocked(r);

queue.scheduleBroadcastsLocked();

enqueueParallelBroadcastLocked方法用于并发执行广播的发送.它很简单,就是将BroadcastRecord放到了mParallelBroadcasts中:

public void enqueueParallelBroadcastLocked(BroadcastRecord r) {

mParallelBroadcasts.add(r);

}

scheduleBroadcastsLocked方法同样很简单,就是向mHandler发送了个BROADCAST_INTENT_MSG消息:

public void scheduleBroadcastsLocked() {

if (mBroadcastsScheduled) {

return;

}

mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));

mBroadcastsScheduled = true;

}

这个时候我们就需要再去看看mHandler在接收到BROADCAST_INTENT_MSG消息的时候会做些什么:

final Handler mHandler = new Handler() {

public void handleMessage(Message msg) {

switch (msg.what) {

case BROADCAST_INTENT_MSG: {

processNextBroadcast(true);

} break;

case BROADCAST_TIMEOUT_MSG: {

synchronized (mService) {

broadcastTimeoutLocked(true);

}

} break;

}

}

};

processNextBroadcast方法用于从队列中获取广播消息并发送给BroadcastReceiver,它内部有两个分支,并行处理和串行处理.

并行处理

例如动态注册的非有序广播等就是使用并行处理,我们先看看并行处理的分支:

final void processNextBroadcast(boolean fromMsg) {

synchronized(mService) {

BroadcastRecord r;

mService.updateCpuStats();

if (fromMsg) {

mBroadcastsScheduled = false;

}

while (mParallelBroadcasts.size() > 0) {

r = mParallelBroadcasts.remove(0);

r.dispatchTime = SystemClock.uptimeMillis();

r.dispatchClockTime = System.currentTimeMillis();

final int N = r.receivers.size();

for (int i=0; i

Object target = r.receivers.get(i);

// 发送消息给Receiver

deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);

}

addBroadcastToHistoryLocked(r);

}

...

}

...

}

private final void deliverToRegisteredReceiverLocked(BroadcastRecord r,

BroadcastFilter filter, boolean ordered) {

...

// 获取BroadcastReceiver的Binder

r.receiver = filter.receiverList.receiver.asBinder();

...

// 使用Binder机制将消息传递给BroadcastReceiver

performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,

new Intent(r.intent), r.resultCode, r.resultData,

r.resultExtras, r.ordered, r.initialSticky, r.userId);

...

}

void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,

Intent intent, int resultCode, String data, Bundle extras,

boolean ordered, boolean sticky, int sendingUser) throws RemoteException {

......

//通过Binder将消息处理传到应用进程,应用进程内部再使用Handler机制,将消息处理放到主线程中

app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,

data, extras, ordered, sticky, sendingUser, app.repProcState);

......

}

}

串行处理

例如有序广播和静态广播等,会通过enqueueOrderedBroadcastLocked传给BroadcastQueue:

public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {

mOrderedBroadcasts.add(r);

}

然后在processNextBroadcast里面会对mOrderedBroadcasts进行特殊处理,但是恕我愚钝,这部分代码比较复杂,我现在还没有搞懂它实际的怎么运行的.这块就留下来之后再讲了.

总结

广播队列传送广播给Receiver的原理其实就是将BroadcastReceiver和消息都放到BroadcastRecord里面,然后通过Handler机制遍历BroadcastQueue里面的BroadcastRecord,将消息发送给BroadcastReceiver:

1.png

所以整个广播的机制可以总结成下面这张图:

2.png

android 广播的实现,安卓广播的底层实现原理相关推荐

  1. 【BYM】Android 实现相机快门动画,hashmap底层实现原理

    6.重新计算 依旧是正弦函数,余弦函数,算出点在红线上的位置. float L1 = (float) Math.sqrt(Math.pow(mRadius, 2.0) - Math.pow(Math. ...

  2. 【Android 电量优化】JobScheduler 源码分析 ( JobServiceContext 源码分析 | 闭环操作总结 | 用户提交任务 | 广播接收者接受相关广播触发任务执行 )★

    文章目录 一.JobServiceContext 引入 二.JobServiceContext 源码分析 三.用户在应用层如何使用 JobScheduler 四.用户提交任务 五.广播接收者监听广播触 ...

  3. Android 全局大喇叭——详解广播机制

    Android 全局大喇叭--详解广播机制 一.广播机制简介 1. 标准广播(Normal broadcasts) 2. 有序广播(Ordered broadcasts) 二.接收系统广播 1. 动态 ...

  4. Android应用程序注冊广播接收器(registerReceiver)的过程分析

    前面我们介绍了Android系统的广播机制,从本质来说,它是一种消息订阅/公布机制,因此,使用这样的消息驱动模型的第一步便是订阅消息:而对Android应用程序来说,订阅消息事实上就是注冊广播接收器, ...

  5. 【Android 电量优化】电量优化 ( 充电状态获取 | 主动获取充电状态 | 广播接受者监听充电状态 | 被动获取充电状态 | 注册空广播接受者获取历史广播 )

    文章目录 一.获取充电状态 二.被动获取充电状态 三.主动获取充电状态 参考 Google 官方文档 : 优化电池续航时间 一.获取充电状态 在应用中执行某些操作 , 如软件云端备份 , 从服务器端获 ...

  6. Android 第二十课 广播机制(大喇叭)----发送自定义广播(包括发送标准广播和发送有序广播)

    广播分为两种类型:标准广播和有序广播 我们来看一下具体这两者的具体区别: 1.发送标准广播 我们需要先定义一个广播接收器来准备接收此广播才行,否则也是白发. 新建一个MyBroadcastReceiv ...

  7. Android之Broadcast, BroadcastReceiver(广播)

    在 Android 中使用 Activity, Service, Broadcast, BroadcastReceiver 活动(Activity) - 用于表现功能 服务(Service) - 相当 ...

  8. Android开发(2) | 广播 Broadcast 的应用——强制下线功能

    文章目录 功能简介 关闭所有活动 登陆界面 发送强制下线的广播 广播接收器 AndroidManifest.xml 运行结果 功能简介 强制下线功能只需要弹出一个对话框,让用户只能点击确定按钮,回到登 ...

  9. (Android开发辅助工具)动态广播注册解注工具

    平常我们开发的时候需要使用到动态注册广播,如果在一个类内注册很多的广播代码就会既冗余又乱糟糟的,就像这样: msgReceiver = new NewMessageBroadcastReceiver( ...

最新文章

  1. SqlServer2008查询性能优化_第一章
  2. Java编程入门(2.1):基础Java应用程序
  3. 图像处理(四)图像分割(2)测地距离Geodesic图割
  4. JavaWeb-综合案例(用户信息)-学习笔记06【复杂条件查询功能】
  5. C语言二分法查找数组
  6. 我的世界服务器物品展示框,我的世界怎么展示框物品 展示框攻略
  7. python自动登录百度空间
  8. 使用cv::findFundamentalMat要注意的几点
  9. 比尔盖茨:十条“金口玉言”-- 世界不会在意你的自尊
  10. linux后台执行命令与putty打开程序界面 screen
  11. w ndows7如何清理垃圾,Win7系统垃圾一键清理工具 一键清除windows7系统垃圾文件
  12. 深入RxJava2 源码解析(一)
  13. 迪赛智慧数——其他图表(平行坐标图):2001 VS 2020主要城市房价及涨幅
  14. 如何把一条条的微信语音合成一个mp3文件?
  15. 小学三年级计算机画图工具作品,小学三年级美术下册《电脑绘画—模板帮我们作画》教案...
  16. FineReport——登录不到决策系统
  17. mysql-索引和锁(何登成的分享转载)
  18. Python 学习7-魔方方法
  19. 一个pom文件中出现了两个相同的依赖_在IDEA里解决maven的pom引用jar包冲突
  20. 微型四轴DIY机架,轻巧稳固耐摔,通用720空心杯电机,9厘米轴距

热门文章

  1. 一、Flash Player的版本
  2. 验证码加减乘PHP,ThinkPHP 3.2.3实现加减乘除图片验证码
  3. 2019FME博客大赛——基于FME的地理国情监测 变化信息通用属性项检查
  4. matlab仿真插入损耗,均匀布拉格光栅的原理及MATLAB反射谱仿真
  5. C++加法运算符重载
  6. go报错dial tcp xx.xx.xx.xx:443: i/o timeout
  7. 简单的说一下人脸识别的过程及前端实现
  8. 在保研中脱颖而出的个人简历模板
  9. python实现AES加解密文档里英文字符串
  10. 3DMAX 3场景制作