做Android系统定制的人,应该都接过这种需求:车机开机必须直接进自家桌面,不允许用户切到别的Launcher;政企平板点开教学链接必须走内置浏览器,弹窗选择器一出来就算事故;智能盒子装完系统后,所有视频流地址都只能由指定播放器拉起。这类需求落到技术层面,几乎都会撞上同一个组件——ResolverActivity。这个组件就是Android里最常被忽视、又最让人头疼的那个“应用选择器”。它平时隐身在系统里,只有多个应用同时声明能处理同一个Intent、并且系统没有默认值时才会跳出来,让用户选一次。而我们要做的定制化,本质就是研究清楚它的决策链路,再把“弹窗让用户选”变成“系统自动确定并直接拉起”,也就是标题里说的自动设置机制。
这篇文章我打算从ResolverActivity的触发原理讲起,把默认启动器、PreferredActivity、RoleManager这几个容易混淆的概念捋顺,然后给出一套可落地的系统定制方案,最后把我在实际项目里踩过的坑和排查方法整理成速查手册。适合正在做ROM定制、Framework开发、企业设备管控的工程师参考,搞App开发但被“默认应用跳转”折磨过的朋友也能从中找到答案。
1. 需求拆解:为什么默认启动器定制要动ResolverActivity
1.1 从隐式Intent到ResolverActivity的完整链路
先把最底层的机制跑一遍。Android里启动一个界面,大多数时候用的是隐式Intent,只声明“我要看一个http链接”或者“我要打开一张图片”,而不指定具体由哪个包来处理。系统接到这个请求后,PackageManagerService会拿Intent里的action、category、data scheme、MIME type去跟所有已安装应用的intent-filter做匹配,返回一组ResolveInfo。
这里有个关键分叉点:如果匹配结果只有一个,系统直接启动那一个,全程无感;如果匹配到多个,并且系统里没有记录过“默认用谁”,ActivityTaskManager就会拉起ResolverActivity,把候选列表渲染出来,让用户点一次。用户点完并且勾选了“始终使用”之后,这次选择会被持久化,下次同样的Intent就不再弹窗。
我之所以先讲这个链路,是因为很多人做定制时上来就改Launcher的Manifest,加一堆priority,结果还是弹窗,根本原因就是没搞清楚:ResolverActivity的弹窗与否,不取决于你的priority有多高,而取决于解析阶段有没有产生“唯一解”或者“已有默认值”。后面实战部分,所有方案的终极目标都是让系统在resolve阶段直接拿到唯一答案。
1.2 默认启动器到底是什么
标题里的“默认启动器”,要区分两种含义,不然代码写到一半容易自己把自己绕晕。
第一种是桌面Launcher,也就是开机后显示图标的那一层。Android定义了一个HOME角色,任何应用只要在Manifest里声明了ACTION_MAIN加CATEGORY_HOME的intent-filter,就有资格成为桌面。系统设置里的“默认桌面”实际上操控的就是这个角色归属。Launcher的切换看起来和ResolverActivity无关,因为它用的是RoleManager下唯一角色的机制:有且只能有一个home角色持有者,新桌面设置生效,老的自动被顶掉。
第二种是“某个Intent类型的默认处理器”,比如默认浏览器、默认拨号器、默认短信应用。这类机制同样由角色体系管理,但也保留着老版的PreferredActivity兼容逻辑。ResolverActivity弹出的“始终使用”,写进去的就是这类默认项。
搞系统定制时这两条线经常交织在一起。比如车机项目里,既要把三方Launcher按角色锁死,又要把内置浏览器设成所有http链接的唯一处理者。前者走RoleManager,后者除了角色设置,必要时还得配合ResolverActivity的候选过滤。
1.3 两种典型定制场景的诉求对比
场景A是系统层锁定。设备是单用途的,不允许用户有选择权。这种定制通常发生在系统预置阶段,厂商直接把不需要的候选应用从匹配结果里剔除,或者把自家应用设成默认。需求方关心的是“稳定”“不可被用户改变”“恢复出厂后依然生效”。
场景B是应用层接管。比如你的App想在自己内部处理特定链接,不希望跳到系统浏览器,或者希望发给你的推送链接自动在你的App里打开。这种定制主要靠Manifest声明和代码处理,用户侧看到的效果是“点链接直接进App”,但如果还有其他App也声明了同样的action,系统照样弹ResolverActivity。
搞清楚自己属于哪个场景,后面方案选型才有依据。场景A必须走系统源码、系统签名或者设备管理通道,普通App权限根本不够;场景B则可以用优先级方案或者自带常用链接解析来降低弹窗概率。我下面的实战方案会按这两种场景分别给路线。
2. ResolverActivity的决策机制剖析
2.1 弹窗的触发条件与ResolveInfo排序规则
要控制ResolverActivity,先得知道它在什么条件下才真正出现。系统源码里,ActivityTaskManager在启动Activity前会调用resolveActivity去查询匹配项,根据查询结果决定走正常启动还是ResolverActivity流程。
5万+




被折叠的 条评论
为什么被折叠?



