ARTICLE DETAIL

资讯详情

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

Unity手游双端动态更换App图标:Android与iOS完整方案

Unity手游双端动态更换App图标:Android与iOS完整方案 做过手游发行或版本运营的应该都遇到过类似的脑洞新版本上线、节日活动、冲榜集量产品经理和运营总会过来问一句“App图标能不能在某个时间点自动换一下活动结束再换回来”。这在纯原生开发里倒还好Android有activity-aliasiOS有setAlternateIconName但到了Unity这边很多团队要么只能举着Android包和iOS包各改一遍要么干脆放弃动态方案。今天就从Unity的视角把两套原生机制放到一个工程里聊透直接给你一套能落到真机上的双端动态更换App图标方案。1. 需求拆解与技术选型逻辑1.1 手游里为什么需要“换图标”App图标是下载转化率最高的视觉位也是用户每天在桌面上看到次数最多的品牌触点。对运营来说图标变化带来的新鲜感和活动暗示非常直接春节换上喜庆套装、周年庆换上限定LOGO、联动IP换上合作原画都能在短时间内刺激老用户回流也给新用户留下“这个产品还在活跃运营”的印象。如果这个动作依赖发版节奏会非常痛苦。应用商店审核周期摆在那错过节日节点就只能等下一年而且为了一个图标单独发一版成本也不合算。动态更换图标的价值就在于此运营后台或者客户端策略在指定时间点触发会议室里还在讨论版本计划线上图标已经静默换成了活动视觉。手游在这个需求上比普通App更迫切因为游戏本身的活动节奏极快一周一个新SKU、两周一个大版本并不少见。图标作为桌面入口如果一直是一张“陈年久图”点击率自然会被那些会玩运营的产品拉开差距。1.2 双端实现路径的核心差异Android和iOS在动态图标这件事上的底层思路完全不同。iOS是官方能力直接调用UIApplication.setAlternateIconName就好前提是你在Info.plist里把备用图标都登记好系统会帮你处理弹窗和切换过程。Android从Java时代开始就没有专门换图标的API真正可用的方案是利用activity-alias配合PackageManager.setComponentEnabledSetting去动态启停入口组件桌面图标跟随入口组件的变化同步刷新。我最早也想过在Android侧直接替换资源文件比如安装后把新的icon覆盖进私有目录——这不可行桌面上显示的图标是安装时由PackageManager解析的资源快照App自身无法修改已经发布的资源映射。而activity-alias之所以能绕过去是因为它在Manifest里本身就是独立的组件可以拥有独立的android:icon属性系统把alias当成一个可启动的入口看待。切换图标时本质上是让Launcher从旧的alias入口切换到新的alias入口。iOS的限制也不少最低支持iOS 10.3App Store审核时要求切换图标必须由用户明确操作感知不能做成完全静默的后台行为。另外备用图标不能带透明通道不能运行时动态生成必须在打包前就固化进bundle。Android在8.0之前动态切换后桌面基本不刷新8.0之后系统会主动广播PACKAGE_CHANGED给Launcher主流桌面才普遍适配。1.3 为什么必须在Unity层做统一封装一个Unity手游工程要同时出Android和iOS包如果不做统一封装业务层的代码会长成“到处是#if UNITY_ANDROID”的面条结构。运营给一个iconIdC#层要判断当前平台走AndroidJavaClass还是DllImport还要处理回调、状态保存这些逻辑堆在业务类里后面每次接新渠道SDK都会想骂人。我的建议是单独做一个AppIconManager静态类对外只暴露“切换图标”和“当前是否支持”两个接口内部隐藏所有平台细节。Android桥接给我的封装返回void我就当同步操作处理iOS桥接通过UnitySendMessage回调通知结果统一在C#层包装成Action回调。这样策划、服务器或者运营后台只需要下发一个枚举ID客户端所有平台差异都被关在一个文件里。2. Android端activity-alias 动态切换方案2.1 AndroidManifest配置要点Android端的核心配置都集中在一个地方AndroidManifest里的activity和activity-alias。先看我实际用在项目里的一个最小化示例这段配置放在Android Libraryaar的AndroidManifest.xml里最干净?xml version1.0 encodingutf-8? manifest xmlns:androidhttp://schemas.android.com/apk/res/android xmlns:toolshttp://schemas.android.com/tools application activity android:namecom.unity3d.player.UnityPlayerActivity android:exportedtrue intent-filter tools:noderemove/ /activity activity-alias android:name.icon.DefaultIcon android:targetActivitycom.unity3d.player.UnityPlayerActivity android:exportedtrue android:enabledtrue android:icondrawable/ic_default intent-filter action android:nameandroid.intent.action.MAIN / category android:nameandroid.intent.category.LAUNCHER / /intent-filter /activity-alias activity-alias android:name.icon.HalloweenIcon android:targetActivitycom.unity3d.player.UnityPlayerActivity android:exportedtrue android:enabledfalse android:icondrawable/ic_halloween intent-filter action android:nameandroid.intent.action.MAIN / category android:nameandroid.intent.category.LAUNCHER / /intent-filter /activity-alias /application /manifest解释一下为什么这么设计。UnityPlayerActivity是Unity默认的GameActivity我让它保留在Manifest里作为所有alias的targetActivity但通过tools:noderemove把Unity默认生成的launcher intent-filter删掉。这样做的原因是我要让所有桌面入口都走alias主Activity只负责承载游戏进程不能被直接Launcher启动也不要被禁用。早期我试过直接禁用主Activity再启用alias结果在一些机型上会出现入口失效、点击图标没反应的情况后来改成“target永远保持enabled入口在alias之间切换”稳定性明显好很多。每个activity-alias的android:name是唯一标识推荐用.icon.xxx的命名空间区分。注意android:enabledtrue只放在默认图标的alias上其他alias默认禁用否则安装完桌面上会同时出现多个图标。所有alias都要带MAIN和LAUNCHER的intent-filter系统才会把它们当成可启动入口。2.2 Java桥接类封装Manifest配好之后需要在Java侧写一个桥接类核心逻辑就是切换组件状态。这里有个重要细节先启用目标alias再禁用其他alias避免在切换过程中出现“所有入口同时消失”的极端状态。package com.example.icon; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; public class IconBridge { private static final String[] ALIAS_NAMES { .icon.DefaultIcon, .icon.HalloweenIcon, .icon.SpringIcon }; public static void switchIcon(Context context, String targetAlias) { PackageManager pm context.getPackageManager(); String packageName context.getPackageName(); String targetName packageName targetAlias; ComponentName targetCn new ComponentName(packageName, targetName); pm.setComponentEnabledSetting(targetCn, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP); for (String alias : ALIAS_NAMES) { String fullName packageName alias; if (fullName.equals(targetName)) { continue; } ComponentName cn new ComponentName(packageName, fullName); pm.setComponentEnabledSetting(cn, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); } } }这里的ALIAS_NAMES数组必须和Manifest里定义的名字一一对应完整类名是“包名后缀”。比如包名是com.example.game第一个alias的完整组件名就是com.example.game.icon.DefaultIcon。DONT_KILL_APP参数必须带上否则系统在切换组件状态时有可能会顺手杀进程正在跑的用户就掉线了。也可以加一个查询当前状态的方法用PackageManager.getComponentEnabledSetting返回的int值判断当前是哪个组件处于启用状态。这个方法在App启动时很有用可以用它来自动校正运营图标ID和本地记录的偏差。2.3 在Unity中集成与常见坑集成方式很简单把上面这个类做成aar放在Assets/Plugins/Android下Unity构建时会自动合并Manifest和Java代码。C#这边用AndroidJavaClass反射调用即可public static class AndroidIconBridge { private const string BridgeClass com.example.icon.IconBridge; public static void SwitchIcon(string aliasSuffix) { using (var cls new AndroidJavaClass(BridgeClass)) { using (var activity new AndroidJavaClass(com.unity3d.player.UnityPlayer) .GetStaticAndroidJavaObject(currentActivity)) { cls.CallStatic(switchIcon, activity, aliasSuffix); } } } }这里调用必须发生在Unity主线程也就是Android的UI线程。不要把switchIcon丢到Task.Run里执行否则会直接crash。如果业务层有耗时前的异步逻辑记得先用主线程调度器包装一下再调用这个换图标方法。常见坑有两个。第一个是Manifest合并问题Unity默认生成的AndroidManifest里UnityPlayerActivity是带launcher intent-filter的如果不做tools:noderemove处理桌面上会同时出现UnityPlayerActivity和默认alias两个图标。第二个是分渠道打包时有些渠道SDK会在启动时主动enable UnityPlayerActivity把我们的alias状态覆盖掉导致动态图标在某几次冷启动后失效。出现这种情况要么让渠道SDK不要动组件状态要么在启动流程末尾重新按本地记录校正一次入口组件。3. iOS端setAlternateIconName 官方能力3.1 Info.plist 与图片资源摆放iOS侧没有Manifest概念所有配置都集中在Info.plist核心是CFBundleIcons下的CFBundleAlternateIcons字典。先看我的一个示例配置keyCFBundleIcons/key dict keyCFBundleAlternateIcons/key dict keyIconBlue/key dict keyCFBundleIconFiles/key array stringIconBlue/string /array /dict keyIconRed/key dict keyCFBundleIconFiles/key array stringIconRed/string /array /dict /dict /dict这里IconBlue、IconRed是备用图标的逻辑名调用setAlternateIconName时传入的名字必须和这里的key一致。CFBundleIconFiles数组里填的是图片文件名前缀不带2x、3x后缀也不带.png扩展名。实际图片文件我会直接放进Xcode工程根目录或者一个独立Group里确保“Copy Bundle Resources”里能看到它们。我踩过最大的坑是试图把备用图标塞进Assets.xcassets。这样做的确能让setAlternateIconName正常工作但Asset Catalog编译之后图片路径是经过压缩的偶尔会出现资源找不到的问题。后来直接放到Bundle根目录稳妥多了。另外备用图标不允许包含Alpha透明通道打包前必须先把PNG的透明通道去掉否则系统会直接拒绝加载。尺寸方面iPhone的主屏图标是60pt规格备用图标建议提供60x60、120x120、180x180三张也就是对应1x、2x、3x。Android侧的资源则建议按mipmap的xhdpi、xxhdpi等密度单独准备不要让系统做粗暴的缩放否则圆角在部分机型上会失真。3.2 Objective-C 桥接与C#调用iOS原生桥接放在Assets/Plugins/iOS目录下Unity会自动把.mm文件编进Xcode工程。下面是核心代码#import UIKit/UIKit.h extern C { bool _SupportsAlternateIcons() { if ([[UIApplication sharedApplication] respondsToSelector: selector(supportsAlternateIcons)]) { return [[UIApplication sharedApplication] supportsAlternateIcons]; } return false; } void _SwitchAppIcon(const char* iconName) { NSString *name [NSString stringWithUTF8String:iconName]; if (name.length 0) { name nil; } if (![[UIApplication sharedApplication] supportsAlternateIcons]) { UnitySendMessage(IconCallbackHandler, OnIOSSwitchResult, unsupported); return; } [[UIApplication sharedApplication] setAlternateIconName:name completionHandler:^(NSError * _Nullable error) { if (error) { UnitySendMessage(IconCallbackHandler, OnIOSSwitchResult, [[error localizedDescription] UTF8String]); } else { UnitySendMessage(IconCallbackHandler, OnIOSSwitchResult, success); } }]; } }如果用Unity 2019以上的Framework构建方式可能需要额外引入UnityFramework/UnityFramework.h否则UnitySendMessage会编译报错。这点根据项目Unity版本调整即可。C#这边用DllImport声明[DllImport(__Internal)] private static extern bool _SupportsAlternateIcons(); [DllImport(__Internal)] private static extern void _SwitchAppIcon(string iconName);iOS没有Android那种“直接同步完成”的机制切换结果是异步的通过UnitySendMessage发送给名为IconCallbackHandler的GameObject。在C#侧建议维护一个静态回调变量在收到原生回调时统一派发给业务层避免多帧后回调丢失。3.3 用户确认弹窗与审核侧注意点在iOS上调用setAlternateIconName系统一定会弹出确认弹窗提示用户“是否要将图标替换为xxx”用户点击“取消”时completionHandler会收到一个error。这一点和Android完全不同不是静默切换所以在设计上要提前想清楚不要在用户毫无感知或者恰好在进行操作时触发比如游戏对局中突然弹窗观感会很差。审核侧的实际经验是如果你的App在设置页或者某个用户主动触发位置有明确的“更换图标”功能Apple的审核员一般不会刁难。反过来如果图标完全是后台服务端远程控制的没有任何用户感知入口审核风险会明显增加。所以我的方案里一定包含一个“图标设置”页哪怕只是简单的两个选项也能给审核一个交代。后台下发模式可以在审核结束之后再启用。另外要记住备用图标在App Store商店页面里不会展示用户只有下载安装后才能看到。这意味着你没法用“商店截图展示备用图标”来吸引用户运营上可以把“图标切换”作为游戏内的彩蛋或者成就奖励而不是商店展示位。4. 双端统一接口设计与状态管理4.1 C# 侧统一接口双端统一管理的第一步是定义一个业务无关的图标枚举和统一入口类。下面是我常用的结构public enum GameIcon { Default 0, Halloween 1, Spring 2 } public static class AppIconManager { public static bool IsSupported() { #if UNITY_ANDROID !UNITY_EDITOR return new AndroidJavaClass(android.os.Build$VERSION) .GetStaticint(SDK_INT) 26; #elif UNITY_IOS !UNITY_EDITOR return _SupportsAlternateIcons(); #else return false; #endif } public static void SwitchIcon(GameIcon icon, Actionbool, string callback) { string iconId icon.ToString(); #if UNITY_ANDROID !UNITY_EDITOR AndroidIconBridge.SwitchIcon(.icon. iconId); callback?.Invoke(true, ); #elif UNITY_IOS !UNITY_EDITOR _iosCallback callback; _SwitchAppIcon(iconId); #endif } }Android和iOS的图标ID都用枚举的ToString()这样在Android侧拼成alias后缀.icon.Halloween在iOS侧直接对应CFBundleAlternateIcons里的keyHalloween。业务层只关心传一个GameIcon过去完全感受不到两端的差异。一个容易忽略的点Android侧没有系统回调但你可以自己检查状态。比如切换完成后用PackageManager.getComponentEnabledSetting查询目标alias是不是已经返回COMPONENT_ENABLED_STATE_ENABLED再回调给上层。iOS侧系统是异步的必须等原生回调回来才认为完成。所以这个接口不要设计成“同步函数返回状态”一定要用Action回调否则iOS会把回调丢到下一帧接口就变脏了。4.2 切换状态的持久化与启动恢复很多人以为动态切换图标后系统重启或App更新后状态会丢实际在Android上setComponentEnabledSetting是持久化的系统会把启用/禁用状态存在/data/system/里用户重启手机不会改变。iOS上系统也会记住你选的备用图标除非用户自己改了或者App卸载。但“系统记住了”不代表“你记住了运营配置”。我的做法是在本地PlayerPrefs里存一个current_icon_id字符串同时启动时做一个校正逻辑如果本地记录的图标ID和后台上发的配置不一致就再触发一次切换。这里有个潜在问题Android的getComponentEnabledSetting可以反查当前启用状态但iOS SDK没有提供“读取当前备用图标名”的API只能靠本地记录。所以iOS侧校正时要小心不要每次启动都调用setAlternateIconName否则用户每天都会看到弹窗。我建议在启动时先检查本地记录是否为空为空则说明从未切换过不要动不为空则和目标iconId对比不一致时再切换。如果一致就不要调原生API。iOS的setAlternateIconName在传入相同图标时虽然不会造成视觉变化但弹窗还是会出白惹用户烦。4.3 运营侧的时间窗与冷却控制动态图标其实是一个运营工具不是产品核心玩法所以在接入层必须加防抖和冷却。Android侧虽然不会弹窗但频繁切换会让系统在短时间内多次更新launcher状态有的桌面会出现图标闪烁甚至短暂的“全部图标都变空白”。iOS侧更不用说每次切换都伴随一个系统弹窗用户体验很不友好。我的经验是一个自然日内只切一次。实现上就是本地记录一个时间戳last_icon_switch_time每次收到切换请求先判断距上次切换是否超过最小间隔没有就直接忽略并上报服务器“冷却中”。运营后台也要有对应的下发频率限制防止运维脚本误操作短时间给全量用户推了十几条切换指令。另外建议所有切换请求都走后台配置不要在客户端硬编码时间点。因为玩家时区不同、版本状态不同、渠道包进度不同硬编码时机很容易出偏差。后台下发的好处是可以先切一小批用户试运行确认双端都没问题后再放全量免得上线当天翻车。5. 常见问题与真机排查实录5.1 Android切换后桌面图标纹丝不动遇到这种情况先别急着怀疑代码按顺序排查。第一步确认系统版本。setComponentEnabledSetting虽然Android 1.0就有但Android 8.0之前Launcher基本不会监听组件启用状态广播所以强行用旧方案基本没戏。如果项目必须兼容Android 7.x只能接受“切换后桌面不刷新”或者引导用户重启手机或者用桌面快捷方式方案曲线救国。第二步看Manifest合并结果。Unity构建后可以到temp/StagingArea或者build/intermediates/merged_manifests下翻最终的AndroidManifest.xml确认UnityPlayerActivity的intent-filter确实被移除了确认alias的enabled状态是符合预期的。第三步看桌面自身的刷新策略。Pixel原生桌面切换后12秒内必然刷新但部分国产ROM存在延时索引的问题。我实测小米MIX 4的MIUI 14有时要等5秒以上才变OPPO的ColorOS个别版本必须手动回到桌面才能看到新图标游戏还在前台时桌面上根本不会刷新。5.2 Android切换完出现两个图标出现两个图标的直接原因是同时有多个组件处于启用状态而且都带MAIN/LAUNCHER的intent-filter。第一种情况是UnityPlayerActivity的launcher filter没有移除干净主Activity和默认alias同时存在。第二种情况是切换代码里的alias数组写错比如目标alias的前缀和Manifest不一致导致新alias没被启用而旧alias又没被禁用桌面上保留旧图标。排查方式很简单切完之后跑一遍adb shell dumpsys package com.your.game | grep -A2 Activity Resolver Table看有几个entry指向MAIN/LAUNCHER。正常情况下只应该有一个。如果发现有两个再逐个列出enabled状态找到哪个漏关了。这种问题在开发阶段就要通过自动化检查卡住我一般会在CI脚本里加一条grep校验只要merged manifest里的launcher组件数量大于1构建直接失败。5.3 iOS报错“alternate icon name not set”这个错误几乎可以锁定在Info.plist配置上。调用setAlternateIconName传入的字符串必须在CFBundleAlternateIcons字典里有同名key。检查点有三个key拼写是否一致大小写是否敏感系统默认大小写敏感图标文件是否真正出现在main bundle里而不是只在工程导航器里显示。还有一个很隐蔽的问题如果我把备用图标放在Assets.xcassets中并用Xcode自动生成的AppIcon集来管理有时编译器会把文件路径结构改掉导致系统找不到。我的建议是放弃Assets方式直接把三张镜像文件拖到Copy Bundle Resources对应位置然后在Info.plist里写文件前缀名这样最容易确认。值得注意的是系统弹窗被用户取消时error里的描述经常是很笼统的“The operation couldn’t be completed”这个不是配置错了而是用户真的点了取消。回调逻辑里对这类错误要特殊处理不要直接弹业务提示说“切换失败”而应该静默恢复或者提示“已取消”。5.4 图标带透明通道或尺寸不对iOS备用图标对Alpha通道是零容忍的。PNG里哪怕有一个透明像素系统在加载时都会报错表现就是切换后图标变成空白。在线工具的“去透明通道”功能有时会连圆角一起填上白底如果不想破坏设计稿应该让美术直接导出不含Alpha的版本或者用Python脚本统一flatten到指定底色上。尺寸方面iOS系统会读取图片的像素尺寸和scale信息来判断是否合格。只给一张120x120的图系统可能会用但最好成套提供。Android的mipmap资源则要覆盖从mdpi到xxxhdpi至少保证主流机型密度覆盖。如果只放一张drawable-nodpi的大图部分桌面会把图标缩得模糊影响整体质感。5.5 一些真机兼容性观察这个东西光靠模拟器是测不出来的必须堆真机。我当前的测试矩阵是Pixel 7、小米MIX 4、华为Mate 60 Pro、OPPO Reno11、三星S23以及iPhone 12 mini和iPhone 15 Pro。Pixel 7在Android 13系统下响应最标准切换后几乎立即刷新华为Mate 60 Pro在HarmonyOS 4.0上表现稳定基本在1秒内刷新小米的MIUI 14存在桌面索引延迟偶尔超过5秒OPPO在ColorOS 14上需要在游戏切回桌面时才刷新如果一直停留在游戏内等你切回桌面那一刻才生效三星One UI在Android 14上也有类似情况但总体比OPPO好一些。iOS端我还没遇到过严重的兼容问题唯一的特殊现象是iPhone 12 mini的桌面缓存比较顽固切换成功但桌面上的图标要等一个动画过渡才更新用户如果眼神够快能看到一瞬间的“旧图标重叠新图标”。这些视觉上的小瑕疵不影响功能但建议在上线前拍一段录屏存档万一运营反馈“没变”可以直接对照。6. 上线前的验证清单与经验建议6.1 双端验证清单动态图标功能不算复杂但涉及原生、Unity、运营配置三层漏一环就容易出事。我每做一次版本迭代都会过一遍下面这张表检查项通过标准Android版本下限仅在API 26及以上允许切换低版本隐藏入口Android多图标检查切换前后桌面始终只有一个入口图标Android反复切换连续切换20次无崩溃、无图标闪烁Android冷启动恢复杀掉进程再启动图标保持上次选择Android渠道SDK适配第三方SDK不重置入口组件状态iOS Plist配置备用图标key与C#枚举一一对应iOS图片资源无Alpha通道三套尺寸齐备iOS弹窗交互成功/取消两条路径回调均正确时间冷却冷却期内重复请求被拦截这些条目可以做成CI自动化的就自动化比如Android Manifest检查、iOS图标的Alpha通道检查。不能自动化的部分就是真机。每周发版前至少跑一轮双端真机验证比什么固化文档都实用。6.2 我踩过的一些坑第一次做这个功能时我在AndroidManifest里保留了UnityPlayerActivity的launcher filter结果安装包一装上去桌面出现两个图标用户懵了我也懵了后来看dumpsys才定位到是多入口共存。修完之后我以为没问题结果在部分华为机型上切换后图标没变查了半天发现是aar里的drawable资源命名有大小写问题打包后被资源混淆工具改了路径alias的icon引用失效。还有一次是iOS收到回调时我的业务层是挂在GameObject实例上的但游戏做了热重启GameObject被销毁了UnitySendMessage直接找不到接收者导致回调丢失。后来我改成用静态方法配合消息队列保证GameObject不存在时也能暂存结果等主逻辑恢复后再消费。最隐蔽的一个坑来自推送SDK。某渠道SDK在初始化时会自动查询并重新enable应用的主Activity而我们的主Activity是没有launcher filter的但SDK并不知道这一点结果把主Activity的enabled状态改了系统启动时就会看到主Activity和alias同时处于启用状态虽然主Activity没有launcher filter不会出现多图标但部分桌面的“推荐应用”列表会抽风。应对方法是保证启动流程在SDK初始化之后再校正一次入口组件状态。6.3 和“快捷方式/角标”等方案的对比不是所有人都适合用动态图标。快捷方式方案其实是Android常见的替代手段使用ShortcutManager或者动态创建桌面快捷方式可以在任意Android版本上给用户“一个带活动图标的入口”。但它本质上不替换原来的应用图标桌面会同时出现旧入口和新快捷方式用户如果不理解还以为App中病毒了。iOS同样有快捷方式能力但流程更隐蔽需要用户手动添加转化率很低。角标方案可以配合动态图标使用比如在图标右上角挂一个活动数字角标。但角标在Android上依赖厂商授权华为、小米、OPPO各有一套透明度不一的限制iOS则要求后台推送权限不是纯客户端能控制的。动态图标的价值在于“整体视觉替换”角标承担的是“提醒”功能两者不是一个维度。所以我的结论是短期强运营节点必须用Android activity-alias和iOS setAlternateIconName这套双端方案如果是为了长期召回或者老用户回流可以做一套基于本地通知快捷方式的辅助策略但不要单独依赖它。最后分享一个我常用的验证方法Android真机上连续切换20次每次切完直接回桌面看图标变化同时留意logcat里有没有android.intent.action.PACKAGE_CHANGED广播iOS真机上把“取消弹窗”和“成功切换”两条路径都测一遍确认回调不会挂住业务逻辑。动态图标这种东西方案本身不复杂但能不能在运营节点上稳定地起作用拼的是细节。把封装放在Unity工程里之后每次新增图标只需要做三件事补一张图、加一个alias、在Java的alias数组和C#枚举里各登记一次运营后台下个配置就能全局生效。
返回列表