☰
Android 10 屏蔽 Launcher HOME 属性报错修复:原理与工程实践
2026/10/21 8:24:36 网站建设 项目流程

前阵子帮某方案商调一批 Android 10 的行业定制机,需求听起来不复杂:设备开机之后只能停留在自研的那套点餐/广告界面,用户怎么按都回不到原生 Launcher。开发同学上手很快,直接把原生 Launcher 的 HOME 属性屏蔽了。结果屏蔽完不到十分钟,系统里到处都在报ActivityNotFoundException,Settings 崩、SystemUI 黑屏、按 HOME 键没反应,连开机动画都能卡住。这篇文章就把从“屏蔽原生 Launcher 的 HOME 属性”到“修复由此引发的一串调用报错”的完整过程写下来,包括原理、改法、修法和验证,给同样在搞行业定制、电视盒子、车载中控或广告机的朋友一个参考。

1. 定制设备为什么非要跟 HOME 属性较劲

1.1 封闭场景下“不让人回到原生桌面”意味着什么

餐厅点餐机、酒店自助机、医疗器械、车载后装屏这类设备,产品经理的诉求往往是“整台机器看起来就是一个应用”。从用户体验上讲,无论用户怎么点,最终都要落在自研的全屏界面里,系统桌面不应该出现,也不应该允许用户通过某种方式跳回原生 Launcher。

这种封闭诉求落到 Android 系统层,第一个要解决的就是“谁是默认桌面”的问题。Android 系统本身并不知道你的自研界面是桌面,它只知道一个规则:找Intent.ACTION_MAIN + Intent.CATEGORY_HOME的 Activity。只要原生 Launcher 还声明着这个组合,它就有资格在开机启动、按 HOME 键、语音助手调用等场景里被系统拉起来。也就是说,只要 HOME 属性还在,原生 Launcher 就永远是“合法桌面候选”,哪怕你把它从桌面上隐藏了,系统机制还是会找到它。

所以很多方案商的开发同学上来就想把原生 Launcher 的 HOME 属性摘掉,让系统彻底不认为它还能当桌面。这个思路本身没错,但摘掉之后引发的连锁反应,往往被严重低估了。

1.2 直接禁用 Launcher 包为什么会“伤敌一千自损八百”

第一反应通常是“直接把原生 Launcher 包 disable 掉不就行了?”实际项目里我基本不建议这么干,原因有几个:

  • 系统开机时依赖一个“客户端侧 Home”来结束启动流程。如果没有可用的 HOME 候选,开机动画会卡住,反复起不来。
  • 原生 Launcher 不只是桌面,还承担着壁纸绑定、Shortcut 管理、QuickStep 代理、无障碍焦点等一堆隐式功能。禁用整个包,等于把这些服务也一起禁掉了。
  • 系统 UI 里很多逻辑都默认“设备里一定存在某个 Launcher”,直接禁用包的后果是bindService失败、查询到空列表,出现一堆跟 Launcher 名字有关的间接崩溃。
  • 从工程回滚的角度看,禁用包是一次运行时状态修改,如果你想验证是不是它引起的,恢复倒是简单,但如果要交付量产版本,这种状态很容易被恢复出厂设置或者其他用户操作破坏。

所以我更倾向于“让系统不再认为原生 Launcher 是 Home 候选”,而不是把 Launcher 整个从设备里抹掉。

1.3 屏蔽 HOME 属性和禁用组件到底有什么区别

这里有一个很多新手容易混淆的概念:屏蔽 HOME 属性,和禁用 Launcher 组件,虽然最终效果都是“原生 Launcher 不再被当作默认桌面”,但作用位置完全不同。

维度屏蔽 HOME 属性禁用 Launcher 组件/包
修改位置AndroidManifest 里的 intent-filter 声明,或 PMS 查询结果过滤PMS 维护的组件 enabled 状态
原生 Launcher 本身包仍然可用,服务仍能正常调用包内组件被 disable,服务全部失效
对壁纸/Shortcut/QuickStep 等系统依赖基本不受影响容易引发系统 UI 连锁崩溃
恢复方式需要重新编译 system 镜像,或关掉 PMS 过滤开关pm enable即可恢复
典型适用场景整机 ROM 定制,需要稳定交付调试阶段快速验证,不建议量产用

理解了这层区别,后面看报错就顺了。屏蔽 HOME 属性,本质上只是把一个组件从“桌面候选名单”里摘掉,并没有把它杀死。系统其他服务如果还按旧逻辑去调用它,不一定会崩;真正崩的,是那些“按照系统里一定存在 Home 候选”来写代码的模块。

2. 屏蔽之后发生的连锁反应:先看报错再谈原理

2.1 最典型的 ActivityNotFoundException:开机和按 HOME 键都找不到“家”

我处理的那台项目 X 设备,当时情况是:系统里除了原生 Launcher,还塞了一个自研 Launcher(包名是com.example.kiosk),开发同学把原生 Launcher 的 HOME 属性屏蔽掉之后,以为一切正常。结果重启之后,开机动画停留了很久,然后屏幕黑了一阵,logcat 里反复刷这样的日志:

08-12 10:23:01.123 1234 1234 E ActivityTaskManager: startHomeActivityLocked: No home activity found 08-12 10:23:01.124 1234 1234 E ActivityTaskManager: Failed to start home activity 08-12 10:23:02.001 8765 8765 E AndroidRuntime: FATAL EXCEPTION: main Process: com.android.settings, PID: 8765 java.lang.RuntimeException: Unable to start activity ComponentInfo{com.android.settings/.SettingsActivity$DefaultHomeActivity} at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2450)

第一行日志是系统在开枪时尝试启动 Home Activity,结果resolveActivity返回为 null,系统直接放弃。第二行之后跟着的就是连锁反应:SystemUI、Settings 这些依赖“存在一个 Home”的模块开始各自崩溃。

按 HOME 键的场景也一样。Android 的 PhoneWindowManager 接收到 HOME 键之后,会通过ActivityTaskManager.startHomeActivity() 请求系统拉起桌面。如果当前没有 Home 候选,就会抛出ActivityNotFoundException`,表现出来就是黑屏几秒、无响应、甚至直接回到锁屏。

2.2 Settings 主屏幕应用入口异常:看似不致命但定制版会崩

这个报错很有代表性,因为很多同学是在“默认应用”页面里点“主屏幕应用”时才发现的。屏蔽掉原生 Launcher 的 HOME 属性之后,系统里只剩一个自研 Launcher 是 Home 候选,按理说 Settings 应该直接显示自研 Launcher。但问题是,原生 Launcher 之前已经被记录进Settings.Secure.HOME_CURRENT这类系统设置里了,现在对应的组件又匹配不上,于是 Settings 的DefaultHomePreferenceController在处理 ResolveInfo 时拿到 null,直接 NPE:

java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.pm.ActivityInfo android.content.pm.ResolveInfo.activityInfo' on a null object reference at com.android.settings.applications.defaultapps.DefaultHomePreferenceController.getDefaultHomeIntent

这不算最难修的,但它特别容易暴露“你在屏蔽属性的时候,没有把所有依赖旧默认桌面的状态清理掉”这个问题。

2.3 Recents 与 SystemUI 的间接故障:不全是 HOME 属性的锅

还有一类现象容易被误解,比如最近任务界面消失、上滑手势没法回到桌面、SystemUI 莫名重启。这些不一定都是 HOME 属性缺失直接造成的,但确实和 Launcher 的状态变化有关。

Android 10 的 QuickStep 手势导航,是通过Intent.ACTION_QUICKSTEP绑定到 Launcher 的代理服务来实现的。如果项目里自研 Launcher 没有实现对应的 QuickStep 契约,而原生 Launcher 又被“摘掉”了 Home 资格,SystemUI 在绑定OverviewProxyService时就会反复失败,甚至导致 SystemUI 整个进程崩溃重启。这个链路比较隐蔽,排查时不要只盯着“HOME 属性”这一点,还要看QuickStep绑定日志。毕竟屏蔽 HOME 属性的目标是“让原生 Launcher 不当默认桌面”,并不代表系统里其他依赖 Launcher 功能的服务都自动切换到自研实现。

3. “家”到底是怎么被找到的:HOME 属性与隐式 Intent 的解析链路

3.1 Activity 里的 intent-filter 是怎么声明“我是桌面”的

Android 判断一个应用是否桌面,完全看 AndroidManifest 中某个 Activity 的 intent-filter 内容。原生 Launcher 的配置一般长这样:

<activity android:name="com.android.launcher3.Launcher" android:launchMode="singleTask" ...> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.HOME" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>

这里的三个元素各有作用:

  • action MAIN:表示这是一个“入口 Activity”,没有它,系统不会把这个 Activity 当作顶级入口。
  • category HOME:表明这个 Activity 希望成为系统的“家”,也就是桌面。这是最关键的标记。
  • category DEFAULT:为了保证能够被隐式 Intent 解析到而存在的。系统在查 Home 候选时通常带MATCH_DEFAULT_ONLY,所以没有 DEFAULT 的话,即使有 MAIN+HOME 也不一定解析得到。

所以,如果只想屏蔽某组件的 HOME 属性,把<category android:name="android.intent.category.HOME" />这一行删掉即可。要是只留着 MAIN+HOME 但把 DEFAULT 删掉,你可能会看到系统查询出来结果里没有它,但那不是“屏蔽 HOME”的成功,而是“DEFAULT 缺失导致无法匹配”,容易被误判。

3.2 开机时系统按什么顺序挑选桌面

开机启动后,ActivityTaskManagerService会构造一个隐式 Intent:

Intent homeIntent = new Intent(Intent.ACTION_MAIN); homeIntent.addCategory(Intent.CATEGORY_HOME); homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

然后调用PackageManagerInternal.resolveHomeActivity()去查找当前用户下可用的 Home Activity。

这个过程大致是:

  1. 系统会先看Settings.Secure.HOME_CURRENT里有没有记录用户/厂商预设的默认桌面组件。
  2. 如果有,并且该组件仍然能匹配ACTION_MAIN + CATEGORY_HOME,就直接用它。
  3. 如果没有记录,或者记录失效,就查询所有匹配这个 Intent 的 Activity。
  4. 如果查询结果只有一个,直接启动;如果有多个,系统就会弹出一个“选择主屏幕应用”的对话框,让用户选。

屏蔽原生 Launcher 的 HOME 属性后,如果自研 Launcher 还带 HOME 属性,理论上系统应该直接解析到自研 Launcher。但如果你在屏蔽时,把系统里最后一个 Home 候选也弄没了,那resolveHomeActivity()就会返回 null,开机流程直接卡在“找不到桌面”这一步。

3.3 默认桌面弹窗和“主屏幕应用”设置的执行逻辑

Android 10 里,“默认桌面”这个状态本质上是一笔系统设置记录,不是单纯靠“哪个包排前面”决定的。Settings.Secure.HOME_CURRENT就是记录默认桌面组件的地方。你在设置里的“主屏幕应用”里切换,改的也是这个值。

所以在做屏蔽操作时,必须同步处理这个旧记录。只把原生 Launcher 的 HOME 属性摘掉,但没有把HOME_CURRENT里的旧分量清掉或改成自研 Launcher,系统就会在“记录指向的组件已经不再匹配”和“需要寻找新的 Home”之间反复横跳,报错就是这么来的。

调试阶段,可以直接用命令查看当前 Home 解析结果:

adb shell cmd package resolve-activity --brief -a android.intent.action.MAIN -c android.intent.category.HOME

如果输出为空或者是 null,说明系统当前状态下根本没有可用的 Home 候选。如果输出里还有原生 Launcher 的组件,说明你的屏蔽没有真正生效。

4. 屏蔽 HOME 属性的三种主流做法与工程取舍

4.1 方案一:改原生 Launcher 的 Manifest,从源头摘掉 HOME 属性

这是最直接的“物理屏蔽”。前提是你手里有 AOSP 源码,或者至少能改到预置 APK 的资源。定位到packages/apps/Launcher3/AndroidManifest.xml,在 Launcher Activity 的 intent-filter 里把 HOME category 注释掉或删掉。

需要注意,删掉 HOME 属性之后,这个 Activity 就不再是系统桌面候选了。但如果系统里没有其他带 HOME 属性的应用,开机就回不了家。所以这个方案必须搭配一个前提:自研 Launcher 的 Manifest 要同时保证action MAIN、category HOME、category DEFAULT三个条件都满足。

这种做法的好处是干干净净,在 Parser 层面就不认为原生 Launcher 是桌面,后续所有系统服务的查询结果自然不会有它。缺点是每一台机器都得重新预置镜像,如果产品线已经铺开了,改起来成本不小。

4.2 方案二:在 PackageManagerService 对 HOME 候选做包名过滤

另一种做法是在系统服务层过滤。项目上线后发现不想动 APK,或者原生 Launcher 的源码包不方便改,就可以在 PMS 的查询结果里把指定包名从 Home 候选列表里剔除。

以 Android 10 的PackageManagerService代码为参考,处理思路是在queryIntentActivitiesInternal返回结果前,做一次包名过滤。示意代码如下:

private List<ResolveInfo> maybeFilterHomeResolveList(List<ResolveInfo> resolveInfos, Intent intent) { // 只处理 ACTION_MAIN + CATEGORY_HOME 的查询 if (!Intent.ACTION_MAIN.equals(intent.getAction()) || !intent.hasCategory(Intent.CATEGORY_HOME)) { return resolveInfos; } String ignoredPkg = SystemProperties.get("ro.kiosk.ignore_home_pkg", ""); if (TextUtils.isEmpty(ignoredPkg)) { return resolveInfos; } List<ResolveInfo> filtered = new ArrayList<>(resolveInfos.size()); for (ResolveInfo info : resolveInfos) { if (!ignoredPkg.equals(info.activityInfo.packageName)) { filtered.add(info); } } return filtered; }

这里的核心思路,是用一个系统属性或者配置文件来控制“哪个包不再参与 Home 候选”。好处是:不用重新编译 Launcher APK,而且把条件做成开关之后,想恢复原生 Launcher 的桌面资格,改一行属性重启就行,非常适合产品版本迭代期间做 A/B 验证。

坏处也很明显:改的是 PMS 这个核心服务,影响面比改 APK 大得多。所有调用queryIntentActivities(ACTION_MAIN + CATEGORY_HOME)的模块都会受影响,包括 Settings 的“主屏幕应用”、LauncherAppsService、SystemUI。所以在过滤 PMS 结果的同时,必须同步处理上面说的HOME_CURRENT失效记录和 Settings 判空逻辑,否则过滤生效后照样会崩。

4.3 方案三:曲线思路——保留属性但让自研 Launcher 接管

严格来说这不算“屏蔽 HOME 属性”,而是利用 Android 已有的“默认 Home 绑定”机制,把系统对 Home 的解析结果固定到自研 Launcher。

在设备企业版或者设备所有者模式下,可以用DevicePolicyManager.addPersistentPreferredActivity(),把ACTION_MAIN + CATEGORY_HOME这个 Intent 持久化绑定到自研 Launcher 的某个 Activity:

DevicePolicyManager dpm = getSystemService(DevicePolicyManager.class); ComponentName admin = getAdminComponentName(); Intent homeIntent = new Intent(Intent.ACTION_MAIN); homeIntent.addCategory(Intent.CATEGORY_HOME); dpm.addPersistentPreferredActivity( admin, homeIntent, new ComponentName("com.example.kiosk", ".MainActivity"));

这样,系统在解析 Home 候选时,会优先且直接使用自研 Launcher,不会再弹“选择主屏幕应用”对话框,原生 Launcher 即使保留 HOME 属性也没有机会被默认选中。

这个方案的好处是改动最小,甚至不需要重新编译系统镜像,只要在设备初始化流程里设置一次 Device Owner 即可。缺点是它依赖 DPM 机制,设备必须进入 Device Owner 或 Profile Owner 状态,而且原生 Launcher 仍然保留着 HOME 属性,只是系统不再默认选它。如果后续有人通过设置把默认桌面切回去,原生 Launcher 还是可能被调起。

4.4 三个方案的对比与选择建议

维度源码改 ManifestPMS 过滤 + 属性开关DPM 持久化默认 Home
改动位置Launcher APK 源码framework 系统服务设备管理器配置
是否重新编译镜像是是否,可在运行时设置
是否影响 PMS 查询结果是是间接,解析时优先走预设组件
恢复默认原生 Launcher需要重新编译改开关属性重启移除持久化规则
风险等级中高低
量产稳定性高中高

我的建议是:如果你的产品线还没有量产,优先用方案三,用 DPM 把默认 Home 钉到自研 Launcher,成本最低,回滚最容易。如果项目不允许设备进入 Device Owner 模式,再考虑方案一,反正整机方案都会重新预置镜像,改 Manifest 最直观。方案二适合那种“东西已经铺出去了,只能在线升级 framework”的场景,但一定要把 PMS 过滤带来的连锁问题想清楚,否则改完还不如不改。

5. 报错修复逐个击破:稳住框架层再收拾应用层

5.1 开机黑屏、按 HOME 键没反应:startHomeActivity 路径上的兜底处理

先看现象。设备屏蔽原生 Launcher 的 HOME 属性且没有其他可解析的 Home 候选时,系统日志会出现类似这样的内容:

ActivityTaskManager E startHomeActivityLocked: No home activity found, device will stay in boot animation

这条日志说明系统尝试启动桌面时失败了,开机流程停在这里。修复的第一步不是去改 framework,而是先确认你到底需不需要保留一个 Home:

  • 如果产品形态是“开机必须进入自研全屏应用”,那自研 Launcher 必须带MAIN + HOME + DEFAULT三个条件,并用cmd package resolve-activity验证。
  • 如果产品形态是“开机直接进单一应用,确实不需要传统桌面”,那就得在 framework 里加一个兜底 Home Activity,否则开机流程没法结束。

给后一种情况打个比方:系统开机流程就像一个人深夜回家,如果发现门锁换掉了,他需要的是另一把能开门的钥匙,而不是在门口干等着。兜底 Home Activity 就是这把备用钥匙。

具体做法是,在系统源码里新增一个极简 Activity,比如FallbackHomeActivity,它不做任何界面,启动后立即把任务转到自研主界面。然后在ActivityTaskManagerService里,当resolveHomeActivity()返回 null 时,把启动目标切到FallbackHomeActivity。这里给一段示意逻辑:

private void startHomeActivityLocked(int userId, String reason) { ResolveInfo homeResolve = resolveHomeActivity(userId); if (homeResolve == null) { Slog.w(TAG, "No home found, starting FallbackHome"); Intent fallback = new Intent(); fallback.setComponent(new ComponentName("com.android.systemui", "com.android.systemui.FallbackHome")); mActivityStartController.startActivity( fallback, /* caller= */ null, /* ... */); return; } // 原有的正常启动 Home 逻辑 }

不同厂商的 AOSP 分支,这个函数的名字和位置可能不一样,但思路一致:在“找不到 Home”这个分支上给出降级方案,而不是让系统卡死在开机动画里。

排查这类问题时,我建议按这个顺序走:

  1. adb logcat抓开机日志,确认是不是startHomeActivityLocked在刷错误。
  2. 执行cmd package resolve-activity看当前 Home 候选到底有谁。
  3. 用dumpsys package查被屏蔽包的组件状态,确认是不是 PMS 或 manifest 层面确实没匹配上。
  4. 根据产品需求决定补自研 Launcher 的 HOME 属性,还是挂兜底 Activity。

5.2 Settings“主屏幕应用”为空:PreferenceController 判空与隐藏

Settings 崩溃的原因多半是旧默认桌面记录失效。排查链路如下:

先看崩溃日志里的调用栈,确认是DefaultHomePreferenceController在getDefaultHomeIntent()里拿到 null 后直接访问字段。再执行:

adb shell settings get secure home_current

如果输出里还是com.android.launcher3/.Launcher这种旧分量,而原生 Launcher 的 HOME 属性已经被屏蔽,那 Settings 在解析时必然拿不到结果。

修复分两步:

第一步,在 Settings 代码里给DefaultHomePreferenceController增加判空保护。大致逻辑是:

ResolveInfo resolveInfo = mPackageManager.resolveActivityAsUser( homeIntent, PackageManager.MATCH_DEFAULT_ONLY, userId); if (resolveInfo == null) { preference.setVisible(false); return; }

也就是当系统没有任何可用的 Home 候选时,干脆把“主屏幕应用”这个入口隐藏掉,而不是让用户点进去崩溃。如果你的产品本身就不需要用户去改默认桌面,这个方案最直接。

第二步,清理系统里失效的默认桌面记录。比较稳妥的做法是在 PMS 过滤逻辑或设备初始化脚本里,把home_current改为自研 Launcher:

adb shell settings put secure home_current com.example.kiosk/.MainActivity

或者直接删掉,让系统重新走一次 Home 解析:

adb shell settings delete secure home_current

在新机器量产时,应该在出厂镜像初始化脚本里就把这个值写好,不要等用户开机之后再处理。

5.3 隐式调用 HOME Intent 的第三方应用:统一容错与降级方案

屏蔽 HOME 属性之后,除了系统自身的模块,还可能被第三方应用炸到。典型场景是某个应用通过隐式 Intent 请求回到桌面:

Intent homeIntent = new Intent(Intent.ACTION_MAIN); homeIntent.addCategory(Intent.CATEGORY_HOME); startActivity(homeIntent);

在没有 Home 候选或者候选被过滤掉的情况下,startActivity就会抛ActivityNotFoundException。第三方应用如果不捕获异常,直接崩溃。

这类问题的修复原则是:不要指望所有第三方应用都做容错,你要么保证系统里永远有一个可用的 Home 候选,要么就得在调用链路里兜底。

对于自己可控的系统应用或系统服务,建议统一改成“先解析,再启动”,并在失败时降级到自研 Launcher 的显式 Intent:

Intent homeIntent = new Intent(Intent.ACTION_MAIN); homeIntent.addCategory(Intent.CATEGORY_HOME); ResolveInfo info = getPackageManager().resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY); if (info != null) { startActivity(homeIntent); } else { Intent fallback = new Intent(); fallback.setComponent(new ComponentName("com.example.kiosk", ".MainActivity")); startActivity(fallback); }

在整机源码里,可以通过搜索“CATEGORY_HOME”把所有调用方找出来,逐个评估:

grep -r "android.intent.category.HOME" packages/ vendor/ frameworks/base/ --include="*.java" | grep -v test

重点看两个地方:一是谁在构造 HOME Intent,二是有没有在startActivity附近加 try/catch。凡是这种隐式调用,一律补上异常捕获,避免某个入口因为找不到桌面而崩溃。

6. 部署、验证与一套管用的调试命令

6.1 修改 framework 后的正确替换与 oat 缓存处理

如果你采用的是 PMS 过滤或兜底 Home 这类 framework 改动,改完代码后替换文件这一步很容易踩坑。Android 10 上替换services.jar或者framework.jar后,如果不清除 oat 缓存,系统会继续加载旧的编译产物,你的代码改了等于没改。

比较稳的操作流程是:

# 进入 userdebug 版本,先禁用 dm-verity,否则 /system 只读 adb root adb disable-verity # 部分 Android 10 机器需要用新命令 adb shell avbctl disable-verification adb reboot # 重启后挂载可写 adb remount # 替换 framework 相关文件 adb push out/target/product/xxx/system/framework/services.jar /system/framework/ adb push out/target/product/xxx/system/framework/framework.jar /system/framework/ # 删除 oat 缓存,强制重新编译 adb shell "rm -rf /system/framework/oat /system/framework/arm /system/framework/arm64" adb reboot

这里有三个细节值得记住:

  • disable-verity和avbctl disable-verification的区别在于,部分 Android 10 机型使用 AVB,disable-verity已经不生效,优先用avbctl那条。
  • 如果只是替换 APK,不需要手动清 oat,pm install会自动处理。
  • 替换 framework 文件后第一次开机会比较慢,因为系统在重新做 AOT 编译,不要以为是卡死直接拔线。

6.2 回归用例清单:哪些项目必须测过才敢交付

屏蔽 HOME 属性不是改完代码拉起来看一眼就完事,必须跑回归。下面是我在一个项目里实际用过的用例清单,按照优先级排列:

序号测试项预期结果
1冷启动进入自研界面无“选择主屏幕应用”弹窗,不黑屏
2重启后按 HOME 键立即回到自研 Launcher,无延迟无崩溃
3无任何 Home 候选时系统有兜底页面,不卡开机
4Settings -> 默认应用 -> 主屏幕应用正常显示或隐藏,点击不崩溃
5最近任务/手势导航能切到自研 Launcher 或明确降级
6锁屏/壁纸/截屏正常,不依赖原生 Launcher
7第三方应用发 HOME 隐式 Intent不崩溃或降级到自研 Launcher
8恢复出厂设置默认桌面仍指向自研 Launcher

这里最容易翻车的是第 5 项。很多自研 Launcher 并没有实现 QuickStep 协议,屏蔽原生 Launcher 之后,用户从底部上滑呼出最近任务可能直接没反应或者闪一下。遇到这种情况,要么自研 Launcher 补齐 QuickStep 实现,要么在产品需求里明确“设备不支持手势导航,使用三键导航”,并做好对应配置。

6.3 再次强调的几点经验与注意事项

调试这类问题,我个人的习惯是先把“Home 解析结果”验证干净,再做其他事情。全部问题排查完,有几点经验是写在代码注释里也提醒不了的:

第一,不要一边屏蔽 HOME 属性,一边又不留任何 Home 候选。系统无论开机还是按 HOME 键,都需要一个地方去。如果你真的不需要传统桌面,就挂一个空的兜底 Home,至少让系统流程能走通。

第二,PMS 层的过滤要加上可逆开关。我见过太多团队直接把过滤逻辑写死在 PMS 里,后面又想恢复原生 Launcher 桌面资格,结果只能重新出镜像。用SystemProperties做黑名单,比写死包名灵活得多,出问题还能远程关掉。

第三,Settings.Secure.HOME_CURRENT这个过期状态一定要处理。很多崩溃的根源其实不在屏蔽本身,而在“系统还记着一个已经失效的默认桌面”。清理旧记录或者预置新记录,应该成为整个方案的一部分,而不是等崩了再修。

第四,只有带MAIN + HOME + DEFAULT的 Activity 才有资格当桌面。改 Manifest 的时候不要把 DEFAULT 删掉,否则你可能以为自己成功屏蔽了,实际上只是让解析结果为空,症状会特别难排查。

最后再补一句,这类定制问题没有捷径。每一次改完系统服务,都用adb logcat -b crash从头抓一遍日志,把每个ActivityNotFoundException和NullPointerException串起来看,你才能确定到底是哪一环漏了。把开机、HOME 键、设置入口这三个主要路径跑通,剩下的就是细节了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询