导读:本文说明系统是如何注册动态广播以及静态广播,这里主要注意其注册的顺序
这篇文章主要是针对我前两篇文章
之前只给出了结果,并没有给出代码分析,现在给出第一步分的分析
大家都知道,广播接收器分为动态注册和静态注册两种
静态接收,就是配置到manifest.xml文件中,PackageManagerService扫描后记录其信息……
动态接收,就是从代码中注册,通过调用下面的方法实现
Intent android.content.Context.registerReceiver(BroadcastReceiver receiver, IntentFilter filter)
(下面的流程图估计画的比较水,将就看一下吧,得补习一下UML了)
首先分析静态注册Receiver的流程
静态receiver的注册是由PackageManagerService开机的时候负责初始化
(PackageManagerService之后简称为PMS)
PMS在开机的时候会对系统一些目录逐个扫描,解析apk文件。静态广播接收器就是在PMS做这件事情的时候顺便处理的。
PMS会解析apk的manifest文件,查找这里注册的receiver,然后加载到内存中
下面看一下PMS是如何工作的
这部分内容没有什么难度,只要有耐心就行,我画了一个很简单流程图,从调用PMS的构造函数开始
注意,这里有几个同名函数,大家需要分清。并不是同一个函数调用了两次
这里只看几处
1.PMS初始化的时候干了些什么
当然,PMS会做很多很多事情,这里我们只看我们关注的,和这篇文章相关的部分
// Collect all system packages. mSystemAppDir = new File(Environment.getRootDirectory(), "app"); mSystemInstallObserver = new AppDirObserver( mSystemAppDir.getPath(), OBSERVER_EVENTS, true); mSystemInstallObserver.startWatching(); scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
扫描目录的顺序
system/framework
system/app
vendor/app
data/app
drm/app-private
这里以system/app目录为例
2.下一个要关注的地方是
void com.android.server.pm.PackageManagerService.scanDirLI(File dir, int flags, int scanMode, long currentTime)
private void scanDirLI(File dir, int flags, int scanMode, long currentTime { String[] files = dir.list(); …… int i; for (i=0; i<files.length; i++) { File file = new File(dir, files[i]); …… PackageParser.Package pkg = scanPackageLI(file, flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime); …… } }
注意
String[] files = dir.list();
以及之后的for循环
3.之后的部分比较无聊,我们直接跳到parseApplication函数部分
else if (tagName.equals("receiver")) { Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true, false); if (a == null) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; } owner.receivers.add(a); }
这部分就是解析manifest中的receiver部分,大家会很奇怪,receiver为什么会变成一个Activity
此Activity非彼Activity,这个Activity是PackageParser的一个内部类,结构也非常简单
public final static class Activity extends Component<ActivityIntentInfo> { public final ActivityInfo info; public Activity(final ParseComponentArgs args, final ActivityInfo _info) { super(args, _info); info = _info; info.applicationInfo = args.owner.applicationInfo; } public void setPackageName(String packageName) { super.setPackageName(packageName); info.packageName = packageName; } …… }
我们看到了PMS如何在初始化的时候如何解析manifest并把其中的element存放到内存中的
其中receiver保存到了owner的成员变量receivers中,owner的类型是android.content.pm.PackageParser.Package
也就是说,在上面的#2步中,scanPackageLI返回结果就是已经包含了manifest信息的Package对象
4.现在还没有结束,返回的结果最终交给了谁,我们还没有看到,在下面的这个函数中,我们终于发现了端倪
Package com.android.server.pm.PackageManagerService.scanPackageLI(Package pkg, int parseFlags, int scanMode, long currentTime)
N = pkg.receivers.size(); r = null; for (i=0; i<N; 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"); …… }
原来,最终是添加到了PMS中的成员变量mReceivers中
// All available receivers, for your resolving pleasure. final ActivityIntentResolver mReceivers = new ActivityIntentResolver();、
下面我们看看它是如何add的
void com.android.server.pm.PackageManagerService.ActivityIntentResolver.addActivity(Activity a, String type)
void com.android.server.IntentResolver.addFilter(ActivityIntentInfo f)
public void addFilter(F f) { ...... mFilters.add(f); int numS = register_intent_filter(f, f.schemesIterator(), mSchemeToFilter, " Scheme: "); int numT = register_mime_types(f, " Type: "); //根据我下面红色文字的假设,这里numS和numT应该都为0 if (numS == 0 && numT == 0) { register_intent_filter(f, f.actionsIterator(), mActionToFilter, " Action: "); } if (numT != 0) { register_intent_filter(f, f.actionsIterator(), mTypedActionToFilter, " TypedAction: "); } }
由于开机启动和接收短信并不涉及MIME Type、Scheme等因素。所有我们只考虑Intent中的Action,MIME Type、Scheme等均不考虑
最后看一下register_intent_filter函数,里面没有任何关于排序的代码,只是按顺序add到list中
private final int register_intent_filter(F filter, Iterator<String> i, HashMap<String, ArrayList<F>> dest, String prefix) { if (i == null) { return 0; } int num = 0; while (i.hasNext()) { String name = i.next(); num++; if (localLOGV) Slog.v(TAG, prefix + name); ArrayList<F> array = dest.get(name); if (array == null) { //Slog.v(TAG, "Creating new array for " + name); array = new ArrayList<F>(); dest.put(name, array); } array.add(filter); } return num; }
Action保存在mActionToFilter中记录,之后发送广播的时候,查找接收器还要靠mActionToFilter这个成员变量
每个action对应一个List,含有此action的filter将被保存到同一个List中
我们要注意一个事情,那就是mReceivers保存这些receiver的顺序
那就是一直与#2步的顺序保持一致,没有遭到破坏
甚至并没有根据优先级排序,只是一味的add
---------------------------------------------------------------
静态广播接收器的注册分析完了,之后就是系统发出广播,然后如何去分发给他们了
我们下篇文章再来分析
下面看看动态接收器的注册流程
我们也是画个简单的流程图,只看关键代码
最终会调用到AMS中的registerReceiver函数
其中关键部分如下
ReceiverList rl = (ReceiverList)mRegisteredReceivers.get(receiver.asBinder()); if (rl == null) { rl = new ReceiverList(this, callerApp, Binder.getCallingPid(), Binder.getCallingUid(), 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); } BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, permission); rl.add(bf); if (!bf.debugCheck()) { Slog.w(TAG, "==> For Dynamic broadast"); } mReceiverResolver.addFilter(bf);
mReceiverResolver的类型为IntentResolver<BroadcastFilter, BroadcastFilter>
mReceiverResolver.addFilter(bf);
在上面已经简述过了
最终所有动态注册的receiver都保存到AMS的成员变量mReceiverResolver中
/** * Resolver for broadcast intents to registered receivers. * Holds BroadcastFilter (subclass of IntentFilter). */ final IntentResolver<BroadcastFilter, BroadcastFilter> mReceiverResolver = new IntentResolver<BroadcastFilter, BroadcastFilter>() { @Override protected boolean allowFilterResult(BroadcastFilter filter, List<BroadcastFilter> dest) { IBinder target = filter.receiverList.receiver.asBinder(); for (int i=dest.size()-1; i>=0; i--) { if (dest.get(i).receiverList.receiver.asBinder() == target) { return false; } } return true; } @Override protected String packageForFilter(BroadcastFilter filter) { return filter.packageName; } };
总结:
静态广播和动态广播如何注册的,我们已经全部分析完了
静态广播是PackageManagerService负责,保存到其成员变量mReceivers中
动态广播是ActivityManagerService负责,保存到其成员变量mReceiverResolver中
注册的顺序:
动态广播与应用中调用的顺序一致
静态广播参考函数
void com.android.server.pm.PackageManagerService.scanDirLI(File dir, int flags, int scanMode, long currentTime)
注意
String[] files = dir.list();
以及之后的for循环
由于开机启动和接收短信并不涉及MIME Type、Scheme等因素。所有我们这里只考虑Intent中的Action,MIME Type、Scheme等均不考虑
保存的顺序参考函数
void com.android.server.IntentResolver.addFilter(ActivityIntentInfo f)
int com.android.server.IntentResolver.register_intent_filter(F filter, Iterator<String> i, HashMap<String, ArrayList<F>> dest, String prefix)
下篇文章将讲述发送广播之后,系统如何查找对应的receiver。在这个过程中,系统才开始考虑优先级。
下篇文章顺便看看隐式Intent是如何查找到目标组件的
转贴请保留以下链接
本人blog地址
相关推荐
为了矿井内因火灾防治工作更好更快的发展,结合矿井内因火灾形成机理,详细全面地介绍了开采技术措施、堵漏、均压、注浆、惰性气体、阻化剂、凝胶及三相泡沫防灭火技术进行矿井内因火灾防治的原理、技术材料及其工艺...
2022年下半年福建省安全工程师安全生产法:内因火灾的预防方法考试题定义.pdf
煤矿内因火灾影响着煤矿的安全高效生产,目前煤矿内因火灾评价准确度较低,评价方法过于陈旧。针对此问题,提出一种基于集对分析-区间三角模糊数的煤矿内因火灾危险性评价耦合模型,集对分析多元联系数可以兼顾煤矿内因...
通过对矿井内因火灾事故的条件与特性进行分析,运用预先危险性分析的方法辨别出矿井内因火灾的危险性类型、触发条件、隐患位置及可能造成的后果,结合矿井的实际安全条件建立矿井内因火灾的预先危险性分析表,并提出...
医药流通行业深度:内因外因逐步改善,底部精选核心资产-0806-安信证券-21页.pdf
毫不夸张的说,如果一台电脑没有安装杀毒软件就连接了互联网,则其很可能在几天内甚至数小时内因感染病毒而导计算机网络安全问题分析及对策全文共5页,当前为第3页。计算机网络安全问题分析及对策全文共5页,当前为...
第1章“轨道交通大发 展的时代”与第2章“轨道交通车辆的安全问题”与 城轨交通的发展 历史和当前形势接轨,搜集、调查了国际国内的若干 素材。在第3章,介绍了轨道车辆走行部常见故障。第4章 “轨道车辆走行部检测...
神东矿区煤层埋藏较浅,层间距较近,煤层近似水平,采动后易形成大面积多层复合采空区,导致各层均具有自然发火可能性,...针对不同类型内因火灾给出常规防治技术路线,以期可杜绝神东矿区内因火灾事故,保证矿井安全生产。
提示:从内因、外因两方面阐述产品开发背景,重点说明“为什么”要开发本产品。 (1)因方面着重考虑:开发方的短期、长期发展战略;开发方的当前实力。 (2)外因方面着重考虑:市场需求及发展趋势;技术状况及发展...
针对矿井内因火灾的突发性、不确定性和高危害性等特点,提出熵值法和突变理论相结合的矿井内因火灾安全评价方法。选取人、物、环境和管理4个方面的因素建立了4级共20个评价指标的矿井内因火灾安全评价指标体系,运用...
信息安全是综合性的、全方位的,它涉及技术、管理、法律、社会和国家,产生的原因也多样性的,归纳主要为两个方面:既有天然脆弱性的内因,也有人为复杂的外因,所以其保障必然会是综合性和全方位的。
煤矿工人安全认知是影响不安全行为的重要内因之一,为了探索安全认知与矿工不安全行为之间的关系,根据相关文献分析并结合煤矿作业环境,将安全认知划分为现场危险认知、工友风险行为认知、职业安全认知和规章制度认知4...
采用野外地质调查、测试分析和数据处理等综合手段,研究内蒙古乌达煤田煤层自然发火的内因条件,揭示了煤层自燃趋向性与煤层厚度、煤变质程度、灰分含量、发热量、硫含量、硫化物矿物以及有机显微组分含量等煤物质组成...
对煤层自燃机理的分析及其自燃存在的危害,结合七台河分公司的有效做法,对其在内因火灾防治方面的做法进行分析探讨,根据矿区的实际情况,分公司在防治自然发火措施方面按照安全、经济、效益的原则,做到分析全面、方向...
针对复杂通风网络中角联分支风流安全稳定性问题,从影响角联风流稳定性的内因、外因以及安全性因素方面建立了评价指标体系,运用层次分析法确定了各指标权重,提出角联风流安全稳定性的模糊优选评价法。从提高角联分支...
采煤工作面采空区不可避免地要有可燃煤的存在,采空区漏风流场供氧条件下,自燃破碎的煤极易形成内因火灾,严重影响着矿井的安全生产。采用温度观测法辅助预报手段,可以及时掌控采空区内部状况及采空区内煤体氧化规律,...
企业亏损的内因分析与治理对策.doc