ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Unity手游动态更换App图标:双端原理与避坑实践

Unity手游动态更换App图标:双端原理与避坑实践 做手游的朋友应该都遇到过这种需求版本更新、节日活动、周年庆的时候产品拿着设计好的新图标跑过来问——“咱们能不能在活动当天自动把商店和桌面的图标换掉”如果只是换商店图标那很简单发版前上传就行但“桌面上的图标也跟着变”这就不再是运营问题而是双端原生能力的问题。这篇文章把我自己在 Unity 手游项目里实现动态更换 App 图标的全过程整理出来覆盖 Android 的 Activity Alias 切换方案和 iOS 的 setAlternateIconName 方案包括两端的配置、代码、资源要求以及在真机实测中遇到的桌面不刷新、厂商 ROM 缓存、系统版本差异等一堆坑。无论你是客户端主程、运营工具开发者还是想给项目加个“变图标”玩法的独立开发者这篇都值得参考。1. 运营活动、主题版本、AB测试动态图标的三类驱动场景与平台能力边界先聊清楚一件事动态换图标这个需求不是“炫技”它背后有非常明确的业务价值。我把它见过的需求归成三类。第一类是节点运营。春节、中秋、版本周年庆这类时间节点运营希望当天零点开始图标自动切换成活动版本活动结束后再自动换回默认图标。中间如果涉及商店审核和包体更新时效性完全不可控所以必须客户端本地实现定时或接收远端指令后切换。第二类是主题版本或联动版本。比如某游戏跟知名动漫联动联动期间把图标换成联动角色或者游戏推出了“暗黑模式”“周年限定”主题在某个时间段内让图标配合主题变化。这类需求往往有明确起止时间但具体哪天生效可能要等版权方确认所以开关最好由服务端动态下发。第三类是 AB 测试。图标对用户点击率的影响很大团队想测“默认图标 A”和“活动图标 B”哪个转化更好。这种场景下需要能做到用户维度精确控制一部分用户看 A一部分用户看 B下一次拉起时根据实验分组决定要不要换。动态换图标在技术上的成本比发两套包低太多。但是Android 和 iOS 对“动态换图标”的支持程度完全不同。很多人搜资料时会看到网上一堆“Android 可以iOS 不行”的说法这个说法一半对一半过时。实际上 iOS 从 10.3 开始提供了官方 API允许 App 在运行时切换到预设的备用图标不过有前提图标资源必须打包进安装包不能运行时从网络下载。而 Android 则通过 Activity Alias活动别名机制实现原理上更接近“让桌面上出现一个伪装成主入口的新组件”。下面是双端核心差异对比维度AndroidiOS核心技术Activity Alias PackageManager 组件开关setAlternateIconName 备用图标 API是否需要预置资源是图标需随包发布是图标需随包发布切换后应用是否重启视系统而定通常会短暂退出不重启图标稍后自动刷新系统限制程度较开放但厂商 ROM 有差异较严格无法动态拉取外部图片支持的最低版本Android 5.0 基本都能用iOS 10.3合规性正常使用无审核风险官方公开 API无审核风险这页表格基本就是整个方案的骨架。接下来我会分别把两端从原理到代码完整过一遍最后给出一个可落地的双端统一架构。2. Android 端用 Activity Alias 让桌面图标“分身”Android 端的实现思路简单说就是在 AndroidManifest 里给主 Activity 声明好几个“别名”每个别名可以有自己的图标和名称。默认情况下只启用其中一个需要切换时通过 PackageManager 把当前启用的别名禁用再启用另一个。桌面 Launcher 感知到组件状态变化后会重新读取图标并刷新显示。2.1 Activity Alias 的底层原理为什么它能骗过桌面要理解这个方案得先明白桌面图标到底是什么。Android 桌面上每个图标对应的是某个应用组件的一个入口通常是带MAIN和LAUNCHER过滤器条件的 Activity 或 Activity Alias。Launcher 通过 PackageManager 查询这些入口读取组件信息里的 icon 和 label 来绘制图标。正常情况下应用的主 Activity 本身带有LAUNCHER条件Launcher 找到这个 Activity把它的 icon 当作 App 图标。而 Activity Alias 相当于一个“组件替身”它指向同一个目标 Activity但可以拥有完全独立的组件名、图标、标签和启用状态。当 alias 被 Launcher 识别为入口后Launcher 显示的就是 alias 上的图标而不是目标 Activity 的图标。所以实现思路就清晰了主 Activity 不要直接带LAUNCHER条件把启动入口全部转移到 alias 上。声明多个 alias各自指向同一个主 Activity分别配置不同的图标。默认只启用其中一个 alias其余保持禁用状态。切换时先禁用当前 alias再启用目标 alias。这套机制的妙处在于Launcher 看到的始终是一个可启动的入口切换前后行为完全一致用户点图标进入的都是同一个 Activity。2.2 Manifest 配置多入口声明与主 Activity 的去图标化处理下面是核心的 Manifest 配置写法我以默认图标和节日图标两个入口为例application android:allowBackuptrue android:iconmipmap/ic_launcher_default android:roundIconmipmap/ic_launcher_default_round android:labelstring/app_name android:supportsRtltrue android:themestyle/AppTheme !-- 主 Activity 不带 LAUNCHER 条件仅作为目标 -- activity android:name.MainActivity android:exportedfalse /activity !-- 默认图标入口 -- activity-alias android:name.MainActivity_Alias_Default android:enabledtrue android:exportedtrue android:iconmipmap/ic_launcher_default android:roundIconmipmap/ic_launcher_default_round android:labelstring/app_name android:targetActivity.MainActivity intent-filter action android:nameandroid.intent.action.MAIN / category android:nameandroid.intent.category.LAUNCHER / /intent-filter /activity-alias !-- 节日图标入口 -- activity-alias android:name.MainActivity_Alias_Festival android:enabledfalse android:exportedtrue android:iconmipmap/ic_launcher_festival android:roundIconmipmap/ic_launcher_festival_round android:labelstring/app_name_festival android:targetActivity.MainActivity intent-filter action android:nameandroid.intent.action.MAIN / category android:nameandroid.intent.category.LAUNCHER / /intent-filter /activity-alias /application这里有两个非常关键的细节直接决定方案能不能跑通。第一个细节主 Activity 绝对不能有自己的LAUNCHER条件否则桌面上会出现两个图标。很多第一次做这个功能的人把 alias 加上了但忘了去掉主 Activity 原来带的 intent-filter结果切换完发现桌面出现重复图标。标准做法是让主 Activity 的exported保持false只作为 alias 的targetActivity存在。第二个细节每个 alias 的exported必须设置为true。因为 Launcher 需要能通过隐式 Intent 启动这个入口如果exported是false系统会因为权限问题拒绝 Launcher 的启动请求表现就是点了图标没反应或直接报“无法启动”。另外提醒一下android:enabled的初始值。默认图标那个 alias 保持true其他备用 alias 全部false。这样首次安装后桌面上只显示默认图标不会因为复用MAIN内容出现多个入口。2.3 切换代码的写法与 PackageManager 的调用细节配置写好之后核心就是运行时代码。Android 通过PackageManager.setComponentEnabledSetting来改变组件的启用状态这个方法会直接影响 Launcher 的图标读取结果。完整切换代码如下public class IconSwitcher { private static final String DEFAULT_ALIAS .MainActivity_Alias_Default; private static final String FESTIVAL_ALIAS .MainActivity_Alias_Festival; private static final String MAIN_ACTIVITY .MainActivity; public static void switchToFestivalIcon(Context context) { try { PackageManager pm context.getPackageManager(); String packageName context.getPackageName(); // Step 1: 先禁用当前的默认别名 pm.setComponentEnabledSetting( new ComponentName(packageName, packageName DEFAULT_ALIAS), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); // Step 2: 再启用目标别名 pm.setComponentEnabledSetting( new ComponentName(packageName, packageName FESTIVAL_ALIAS), PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP); // Step 3: 发送组件状态变化通知提示桌面刷新 sendPackageChangedBroadcast(context); } catch (Exception e) { Log.e(IconSwitcher, switchToFestivalIcon failed, e); } } private static void sendPackageChangedBroadcast(Context context) { try { Intent intent new Intent(Intent.ACTION_PACKAGE_CHANGED); intent.setData(Uri.parse(package: context.getPackageName())); intent.setPackage(context.getPackageName()); context.sendBroadcast(intent); } catch (Exception ignored) { // 部分系统版本对隐式广播限制较严发不出去就依赖桌面自行刷新 } } }这里我强烈建议按“先禁用当前别名再启用目标别名”的顺序来做。如果反过来先启用新别名再禁用旧别名在部分 ROM 上会出现一个极短暂的“双图标”窗口期截图或自动化测试时偶尔能抓到。关于DONT_KILL_APP标志它的作用是让组件状态变更不杀掉当前进程。但坦白说在真机上这个标志的效果并不总是符合预期。很多系统在禁用当前正在运行的组件时仍然会把 Activity 销毁掉。所以我在下面的踩坑章节会专门讲这个现象这里先记住一个结论切换完成后不要立刻依赖当前进程继续做繁重任务最好让用户回到桌面查看图标变化。2.4 资源文件与 Adaptive Icon 的配套处理如果只给 alias 配置android:icon在 Android 8.0 以上的设备上会遇到一个问题没有roundIcon或者 Adaptive Icon 图层配置不正确时桌面图标可能出现白底、变形、被裁切的情况。现在的国产 ROM 基本都是 Android 8.0 起步所以资源部分必须做全套。我的建议是每个图标版本都提供两套资源一套传统 PNG放在mipmap-mdpi/hdpi/xhdpi/xxhdpi/xxxhdpi一套 Adaptive Icon放在mipmap-anydpi-v26下面用 XML 定义前景和背景层。Adaptive Icon 的配置类似这样!-- res/mipmap-anydpi-v26/ic_launcher_festival.xml -- adaptive-icon xmlns:androidhttp://schemas.android.com/apk/res/android background android:drawabledrawable/ic_festival_background / foreground android:drawabledrawable/ic_festival_foreground / /adaptive-icon这里有个小经验背景层建议用纯色或简单的图层不要直接放完整的方形图片前景层要保证图标主体区域在安全范围内否则在部分桌面上会被裁掉一圈。对于没有适配 Adaptive Icon 的旧版本系统继续使用mipmap下的 PNG 即可系统会自动选择合适的一套。如果你用的是 Unity 项目资源处理要额外注意Unity 打包 Android 时res目录可能会被合并或重构建议直接通过 Android Library 工程管理图标资源和 ManifestUnity 主工程通过依赖方式接入避免打包工具的优先级把自定义 Manifest 节点覆盖掉。3. Android 端避坑桌面刷新、厂商 ROM 与进程状态的那些坑这一节是我个人认为整篇最有价值的部分。动态换图标的核心原理不复杂真正折磨人的是边界情况。我做这个功能时踩过的坑比写代码花的时间多得多。3.1 桌面图标不刷新三种典型场景与解决思路图标切换完成后桌面上的图标没变这是最常见的问题。我把它拆成三种场景。场景一切换后需要等待几秒。部分桌面特别是国产 ROM 自带桌面有图标缓存收到PACKAGE_CHANGED广播后不会立即重新读取而是等一个周期性同步。这种情况下让用户回桌面盯着看三到五秒一般会自己刷新。测试时不要刚切换完就去截图很容易误判为失败。场景二第三方桌面不响应。如果你装了 Nova Launcher、微软桌面这类第三方桌面它们对组件状态变化的响应逻辑各不相同。有的会监听PACKAGE_CHANGED并自动刷新有的则完全不监听。这时候最粗暴有效的办法是让用户长按桌面空白处进入“桌面设置”触发一次图标重建或者在代码里引导用户“重启桌面”或“返回桌面”。我实测下来无论第三方桌面还是系统桌面只要重新加载一次应用列表图标都会变成新状态。场景三部分 ROM 广播被限制。我前面写的sendPackageChangedBroadcast在 Android 8.0 之后隐式广播限制收紧这条广播在部分系统上可能发不出去。其实不用担心因为大多数桌面是自己通过LauncherApps或PackageMonitor监听组件变化的依赖的是系统服务层的回调而不是这个显式广播。所以发送广播是锦上添花不是雪中送炭。3.2 华为、小米、OPPO 等厂商 ROM 的差异化表现国产 ROM 是这个功能最大的不确定性来源。我无法给你一份“所有机型全部适配”的保证但可以把实测中的典型表现列出来。小米 MIUI 大部分情况表现优秀切换后桌面图标很快会更新但在少数版本上图标缓存比较顽固。如果遇到可以让用户在下拉通知栏里做一次“重新加载桌面”操作或者重启一次手机。另外 MIUI 的“图标重绘”机制会影响主题图标如果你的游戏接入了主题商店逻辑切换后图标可能被主题风格覆盖需要额外判断。华为 EMUI / HarmonyOS 的表现比较“慢热”图标刷新往往有延迟有时需要等十几秒甚至更久。我怀疑是系统图标加载服务有更强的缓存策略。实测中有一次等了接近三十秒才刷新差点以为失败了。华为设备上切换完成后建议不要立刻挂后台多保持应用在前台一两秒给系统广播足够时间下发。OPPO / vivo 的 ColorOS 和 OriginOS 表现中规中矩大多数情况下能刷新但偶尔也有失败案例。我没有找到完全通用的规律只能说“等待 触发重启桌面”是最后的兜底方案。三星 One UI 的适配度相对较好图标刷新速度和稳定性居中。我测试过的机型上基本都能在五秒内刷新成功。3.3 切换时应用被“杀掉”的机制说明与规避前面提到DONT_KILL_APP并不总是奏效。实际情况是当系统检测到你禁用的组件正是当前正在运行的组件或者组件状态变化影响到了包的整体可见性时系统会销毁你的进程。这在部分系统上会表现为切换图标后应用自动退出回到桌面然后新图标出现。从用户视角看这个行为其实“意外地合理”——用户看到图标变了应用回到桌面以为功能正常生效了。但从开发视角如果你在切换后还有未完成的任务比如上报埋点、弹窗提示进程被杀会导致这些逻辑丢失。规避方案有三个层次第一个层次切换动作尽量放在一个独立的、非关键的流程里。比如用户点击“应用节日图标”后先弹 Toast/对话框提示“图标已切换应用将返回桌面”然后执行切换最后调用moveTaskToBack(true)把应用退到后台而不是直接等待被杀。第二个层次把切换逻辑抽到一个轻量的前台 Service 或 WorkManager 任务中确保即使 Activity 被销毁切换流程也能走完。第三个层次如果功能要支持“定时自动切换”不要依赖应用进程存活。建议用 AlarmManager 或 WorkManager 到点触发切换而不是让玩家一直开着游戏。这样可以避免进程被杀导致整个切换任务中断。3.4 Android 12 之后的启动画面与图标状态不一致问题Android 12 引入全新的 SplashScreen 机制后又出现了一个新问题应用冷启动时系统启动画面显示的是启动入口的图标但如果你刚完成图标切换系统启动画面的缓存可能还挂着旧图标导致用户看到“启动画面图标和桌面图标不一样”的尴尬情况。这个问题目前没有完美解法因为 SplashScreen 的图标来自系统侧的启动画面缓存应用层无法强行刷新。实测下来的应对策略是切换后的第一次冷启动用户有一定概率看到旧的启动画面图标但游戏内容正常从第二次冷启动开始图标就会保持一致。所以不必太过担心只要不在切换后立刻引导用户杀进程再启动影响基本可控。4. iOS 端setAlternateIconName 是唯一公开路径但有限制iOS 端没有 Android 那么“绕”Apple 从 iOS 10.3 开始提供了官方备用图标 APIsetAlternateIconName(_:completionHandler:)。这套 API 允许 App 在运行时把主屏幕图标切换成预设的备选图标之一不需要用户手动设置也不用经过设置页。但能力上有清晰边界下面我把边界和工程实现讲透。4.1 为什么 iOS 不能真正“动态”换图标很多网上资料说“iOS 不能动态换图标”严格来说不准确。准确表述是iOS 不允许 App 在运行时动态生成或者从网络下载图标并设置为主屏图标Apple 只允许你在安装包内预置多套图标运行时在预置列表里切换。这意味着两件事所有备选图标必须在发版前准备好并且随 App 包一起分发。切换动作是“本地预设值之间的跳转”无法做到“设计图上传后全网立即生效”。对游戏项目来说这个限制其实可以接受。因为一个版本周期内涉及到的图标版本是有限的通常在几个到十几个之间。运营只要提前一个版本规划好节点把图标资源全部打进包服务端在对应时间点下发切换指令即可。4.2 Info.plist 配置备用图标声明与资源命名规则iOS 的备用图标信息全部写在 Info.plist 里。核心字段是CFBundleIcons下的CFBundleAlternateIcons。一个简单的配置片段如下keyCFBundleIcons/key dict keyCFBundleAlternateIcons/key dict keyFestivalIcon/key dict keyCFBundleIconFiles/key array stringFestivalIcon60/string stringFestivalIcon120/string stringFestivalIcon180/string /array keyUIPrerenderedIcon/key false/ /dict /dict /dict这里的关键点是CFBundleIconFiles数组里的名字对应工程 Asset Catalog 里的图片资源名称不需要带文件扩展名。iOS 会自动根据设备类型选取合适分辨率60pt、120pt、180pt 分别对应普通屏、2x、3x。在 Unity 工程里操作时有个很方便的做法直接修改 Xcode 工程导出后的 Info.plist或者使用 Unity 的 iOS 构建后处理接口自动把备用图标配置注入到生成的 Info.plist 中。我在项目里就是这么做的在IPostprocessBuildWithReport里读取构建配置把当前版本要启用哪些备用图标写进 plist避免手工维护。4.3 切换代码调用、回调和恢复默认图标的正确姿势iOS 端的代码比 Android 简单直接核心就是UIApplication的备用图标 API。Swift 代码示例import UIKit final class AppIconManager { static let shared AppIconManager() func switchToFestivalIcon() { guard UIApplication.shared.supportsAlternateIcons else { // 当前设备或系统版本不支持备用图标 return } UIApplication.shared.setAlternateIconName(FestivalIcon) { [weak self] error in if let error error { print(图标切换失败:, error.localizedDescription) } else { print(已切换到节日图标) } } } func switchToDefaultIcon() { guard UIApplication.shared.supportsAlternateIcons else { return } UIApplication.shared.setAlternateIconName(nil) { error in if let error error { print(恢复默认图标失败:, error.localizedDescription) } else { print(已恢复默认图标) } } } }这里要注意几个细节。第一传入的 name 必须和 Info.plist 里CFBundleAlternateIcons的 key 完全一致大小写和标点都不能错。不一致时系统会直接报错。第二传nil是恢复默认图标的唯一方式不要试图传默认图标的 name那是无效的。第三回调里的 error 一定要处理。在我测试过的 iPhone 机型上有一种情况是在系统刚升级完、图标缓存还没有完全建立时调用 API会收到一个“图标名称无效”的错误等几分钟后再调用就正常了。如果不做错误提示玩家会以为功能坏了。4.4 iOS 端的实际表现与系统限制因为 iOS 生态高度统一真机表现比 Android 好预测得多。但也不是完全没有坑。第一个坑是图标刷新时机。调用setAlternateIconName成功后桌面图标并不是立刻切换通常在 1 到 3 秒内更新如果你退出 App 过快偶尔会在桌面看到短暂延迟。这是正常的不用焦虑。第二个坑是 Honor 和部分旧机型上偶尔会出现“图标切换了但通知中心下拉界面里的图标没变”的现象。这个我没找到解法属于弹簧系统自身的问题过一段时间会自动恢复。第三个坑是它是一款“一次性用户可见”的切换频繁切换可能引起用户反感。如果你做 AB 测试时频繁变图标有些用户会在系统设置里给 App 开启“不自动更换图标”的权限吗答案是不会因为 iOS 目前没有这个开关。但基于用户体验我建议一个自然日内不要超过一次否则会显得很“弹窗广告风”。5. 双端统一把图标切换封装成运营可配置的能力两个平台的实现方式完全不同但如果我们的目标是让运营像配置活动一样配置图标就需要在上层做一层统一抽象。我建议把图标切换能力封装成一个独立模块对外暴露统一接口。5.1 客户端接口设计游戏侧不感知平台差异在 Unity 里做双端封装是最自然的因为 Unity 本身就是统一开发层通过原生桥接调用底层 API。我设计的接口大致是这样public enum GameAppIconType { Default 0, Festival 1, Anniversary 2, Collab 3 } public static class GameAppIconManager { private static IAppIconBridge _bridge; static GameAppIconManager() { #if UNITY_ANDROID !UNITY_EDITOR _bridge new AndroidAppIconBridge(); #elif UNITY_IOS !UNITY_EDITOR _bridge new IOSAppIconBridge(); #else _bridge new EditorAppIconBridge(); // 编辑器下模拟用于测试 #endif } public static void SwitchIcon(GameAppIconType iconType, System.Actionbool callback) { _bridge.SwitchIcon(iconType, callback); } }Android 桥接类通过 Unity 的AndroidJavaObject调用前面写的IconSwitcheriOS 桥接类通过UnitySendMessage或 Swift 原生插件调用AppIconManager。编辑器下用一个模拟桥接直接打印日志方便在没有真机的情况下联调逻辑。5.2 服务端下发与控制策略状态机与幂等性有了统一接口接下来就是服务端怎么控制。我的建议是引入一个“图标状态机”而不是简单地下发一个“切到节日图标”的指令。为什么因为运营配置图标通常会带有时间属性某天 0 点切节日图标某天 0 点切回默认。如果只下发“目标状态”那么客户端在离线期间可能漏掉切换如果下发“目标状态生效时间点”客户端逻辑就要复杂很多。更稳妥的做法是服务端维护一个“预期的图标状态”客户端每次启动或回到前台时拉取一次本地检测到“预期状态 ! 当前状态”时执行切换。这个模型本质上是幂等的不管客户端当前处于什么状态只要最终能收敛到服务端预期状态即可。意外的网络异常、进程被杀、系统切换失败都不会导致长期状态错误。下发格式可以非常简单例如{ icon: festival, startTime: 2025-01-01 00:00:00, endTime: 2025-01-15 23:59:59 }客户端拿到后只要当前时间落在起止时间内就确保图标是 festival超出区间则切回 default。我把这段逻辑放在 Unity 的Update外层每隔一段时间检查一次同时监听 App 从后台返回的事件保证玩家切回来时图标已经正确。5.3 定时切换的边界与离线兜底有一种场景会让“定时切换”失效玩家在图标切换期间恰好没打开过 App也没有通过任何机制触发检查。那么服务端下发的“切换”指令永远不会执行。这种离线兜底是否要做取决于业务容忍度。我的建议是如果切换是强运营需求就必须做服务端兜底即在商店图标的展示层也同步替换并且设置足够的活动周期给“未及时切换”的用户留出窗口如果只是弱装饰需求例如主题变色类切换就接受部分用户延迟生效不做复杂兜底。另外即使客户端本地没有执行切换下次启动时也会拉取服务端状态并纠正所以最坏情况是“晚生效一个用户启动周期的时长”不会出现永不生效。6. 上线前必须过的检查清单与我的经验总结做到这里方案已经基本完整。最后整理一份我每次发版前都会过的检查清单以及几条值得写进团队文档的经验。6.1 双端功能检查清单检查项说明Android 图标不重复确认主 Activity 无 LAUNCHER 条件Android 别名 exported 状态确认所有 alias 的 exported 为 trueAndroid 资源完整性确认每个 alias 都有默认圆角图标和 Adaptive IconAndroid 真机切换验证至少覆盖小米、华为、OPPO、三星各一台真机Android 切换后桌面刷新等待 10 秒以上再判断必要时重启桌面iOS 备用图标资源入库确认 CFBundleAliasedIcons 数组中的资源名与实际文件一致iOS 真机切换验证确认 supportsAlternateIcons 返回 trueiOS 恢复默认图标验证 setAlternateIconName(nil) 的正常回切服务端下发连通性弱网、断网、App 结束后拉起三种情况各测一轮幂等性验证重复执行同一目标切换确认最终状态不变此外还有一项容易忽略的检查游戏内如果做了“自定义桌面图标”的增值功能需要在 App Store 审核备注里说明使用的 API 和用途。虽然setAlternateIconName是公开 API审核基本不会卡但主动说明可以减少“被误判为异常功能”的风险。6.2 Unity 项目里的工程化注意事项Unity 项目做这个功能和纯原生项目有个重要区别Unity 打包出来的 Android 工程会自动生成并合并 Manifest。如果你直接在 Unity 的 Plugins/Android 下放自定义 Manifest 或 Android Library一定要确认打包后的最终 Manifest 内容符合预期。最好用 Android Studio 打开 Unity 导出的工程检查一次最终合并结果而不是只改 Unity 侧的 Manifest 就发版。iOS 侧则要注意构建后处理。Unity 每次构建 Xcode 工程时会重新生成 Info.plist如果手动改 Xcode 工程下一次构建可能被覆盖。我已经把备用图标的 plist 注入逻辑放进了构建后处理脚本每次构建自动执行省了很多重复工作。6.3 一些值得沉淀的个人经验最后分享几条我做这个功能之后的直接体会。第一动态换图标不是“一次写代码永久能用”的功能。Android 厂商 ROM 迭代很快每次 Android 大版本升级或新桌面发布后都值得重新做一轮真机回归。至少要保证核心机型——你游戏用户量最大的那些机型——切换功能不出大问题。第二图标的切换频率是真实的用户体验风险。我见过有运营把图标切换做成“一天换一个颜色”结果几天后玩家在社交媒体骂“App 是不是中毒了”。建议把动态图标当成一个需要克制的运营手段重点场景才使用。第三如果图标切换失败千万不要让用户卡在异常状态。我在 Android 端做过一个保护逻辑切换超过三次仍然失败时自动恢复默认图标并上报埋点避免出现桌面图标状态和业务配置不一致的“半失败”状态。这个兜底在实际运营中救了不止一次。动态更换 App 图标这个需求听起来像是平台层面的“魔法”拆开看其实就是一个组件状态切换加一张图标资源的事。但它涉及 Manifest、资源、厂商兼容、系统限制、服务端控制多个层面每一步都有细节。希望这篇整理能帮你在做同类功能时少踩几个坑。
返回列表