简介:本资源是面向Unity游戏开发者的一站式华为HMS SDK接入实践Demo,专为需在华为生态设备上集成账号、推送、游戏服务等功能的中高级开发者设计。压缩包含2000个文件,主体为1604个bin库文件(HMS核心SDK二进制)、136个info配置说明、127个class字节码及88个meta元数据,辅以83个Java桥接代码、40个XML配置、37个PNG图标资源和10个DLL插件,完整复现Unity项目从SDK导入、IL2CPP配置、初始化调用到登录与推送功能验证的全流程工程结构。资源大小28.82MB,结构规范、即导即用,避免开发者自行拼装碎片化文档。已有1842人学习下载,内含可直接运行的HuaweiSdkDemo工程、标准化AndroidManifest权限模板、典型回调处理脚本(如HMSManager.Init/Login)及真机调试关键配置注释,大幅降低HMS接入门槛与兼容性踩坑成本。
1. Unity接入华为SDK demo:不是“点几下就跑通”的黑匣子,而是要亲手拆开HMS Core的通信链路
你是不是也试过在Unity里拖进华为的Unity插件包,改两行AppGallery Connect上的包名,点击Build——然后卡在启动页不动?或者日志里刷出一长串HMSClient: Failed to connect to HMS Core却找不到源头?这不是你环境有问题,而是这个demo本质不是“演示效果”,而是一份可调试、可断点、可替换模块的HMS Core接入最小可行验证集。它覆盖了华为移动服务(HMS Core)中最常被调用的四大能力:账号登录(Account Kit)、推送(Push Kit)、分析(Analytics Kit)和应用内支付(IAP Kit),全部基于Unity 2021.3 LTS + Android 12+真机实测封装。适合两类人:一是正被甲方要求“必须上架华为应用市场且启用HMS能力”的Unity中台开发者,二是想绕过AGC控制台玄学配置、直接从源码层理解Token刷新机制与Intent Filter绑定逻辑的进阶使用者。它不教你怎么注册开发者账号,但会告诉你为什么AndroidManifest.xml里那三行<meta-data>缺一不可,以及HuaweiMobileServices/Editor/HuaweiMobileServicesPostprocessor.cs这个脚本到底在编译期干了什么。
2. 搭建基础环境:从JDK到AGC,每一步都得亲手敲命令验证
2.1 JDK与Android SDK版本对齐:别信Unity Hub的“推荐版本”
Unity官方文档常建议用JDK 11,但华为HMS SDK 6.10.0+明确要求JDK 17(注意:不是JRE,是完整JDK)。很多团队翻车第一站就在这里——Unity Editor能启动,但打包时Gradle报Unsupported class file major version 61。这是因为HMS AAR包已用Java 17编译,而旧版JDK生成的字节码版本不兼容。
提示:不要用Unity Hub自动安装的JDK,它默认装JDK 11。手动下载 Oracle JDK 17 或 OpenJDK 17 ,解压后在Unity → Preferences → External Tools → JDK Path中指向
jdk-17.x.x根目录。
验证是否生效:
# 终端执行 java -version # 必须输出类似:java version "17.0.2" 2022-01-18 LTSAndroid SDK方面,必须确保以下三项已安装(通过Android Studio SDK Manager或命令行):
- Android SDK Build-Tools 33.0.2(HMS 6.10+强制要求)
- Android SDK Platform-Tools(adb必须可用)
- Android SDK Platforms API Level 33(targetSdkVersion硬性依赖)
验证命令:
# 查看已安装build-tools ls $ANDROID_HOME/build-tools/ # 应看到 33.0.2 目录存在 # 查看platforms ls $ANDROID_HOME/platforms/ # 应看到 android-33 目录2.2 AGC控制台配置:不是填完就完事,关键字段要反向校验
登录 华为开发者联盟AGC控制台 后,创建新应用时,包名(Bundle ID)必须与Unity Player Settings中设置的完全一致,且字母大小写敏感。常见错误是Unity里设成com.example.mygame,AGC里误填为com.Example.MyGame,导致后续所有HMS初始化失败。
配置完成后,必须下载agconnect-services.json并放入Unity工程的Assets/Plugins/Android/目录(注意:不是Assets/Plugins/Android/libs/,也不是StreamingAssets/)。这个文件不是静态资源,它会被HuaweiMobileServices/Editor/HuaweiMobileServicesPostprocessor.cs在每次Build前解析,并注入到最终APK的assets/目录中。
验证该文件是否生效:
# 打包APK后,用zip工具打开,检查路径: # assets/agconnect-services.json # 内容中必须包含 "client_id" 和 "package_name" 字段,且值与AGC中一致2.3 Unity插件导入:拒绝“一键导入”,必须分层验证
华为官方提供两种Unity插件包:
HMS-Unity-Plugin-X.X.X.zip(全量包,含所有Kit)HMS-Unity-Plugin-Account-X.X.X.zip(单Kit精简包)
强烈建议首次接入使用全量包,因为Account Kit依赖Base Kit,Push Kit依赖Base + Analytics,手动拼凑极易漏依赖。解压后,将Assets/目录下的所有内容(含HuaweiMobileServices/、Plugins/、Resources/)整体复制到你的Unity工程根目录,不要覆盖已有同名文件夹(如已有Plugins/Android/,则合并子目录)。
关键验证点:
Assets/HuaweiMobileServices/Editor/下必须有HuaweiMobileServicesPostprocessor.csAssets/Plugins/Android/下必须有huawei-hms-base-6.10.0.300.aar等AAR文件(版本号需与AGC控制台显示的SDK版本一致)Assets/Resources/下应有HMSConfig.asset(这是Unity侧配置入口,双击可编辑)
注意:导入后Unity会自动触发编译。若Console出现
error CS0246: The type or namespace name 'HuaweiMobileServices' could not be found,说明HuaweiMobileServices/目录未正确落位,或.asmdef引用损坏——此时删除Assets/HuaweiMobileServices/重新导入。
3. 四大核心Kit接入实操:从初始化到回调,每行代码都带参数注释
3.1 Account Kit:登录流程不是“调个API”,而是状态机管理
Account Kit的登录不是简单SignIn()一下就完事。它实际是三阶段状态机:
- 预检(Pre-check):检查设备是否安装HMS Core、版本是否达标
- 授权(Auth):拉起系统级授权页,用户选择账号并授予权限
- 凭证交换(Token Exchange):用临时Code向华为服务器换ID Token与Access Token
DemoLoginManager.cs中关键代码如下:
// Assets/Scripts/Login/DemoLoginManager.cs public class DemoLoginManager : MonoBehaviour { private HuaweiIdAuthService authService; void Start() { // 初始化必须在Awake或Start早期执行,且仅一次 HuaweiIdAuthManager.Instance.Init(); authService = HuaweiIdAuthManager.Instance.GetService(); } public void OnLoginClick() { // 1. 预检:检查HMS Core可用性(非网络请求,本地判断) authService.CheckAvailability((result) => { if (result.IsOk()) { // 2. 构建登录请求:指定scope(权限范围)、responseType(code/id_token) var signInRequest = new SignInRequest.Builder() .SetScope("email profile") // 请求邮箱和基础资料 .SetResponseType("code id_token") // 必须同时请求code和id_token .Build(); // 3. 发起登录:此操作会拉起系统UI,阻塞当前线程 authService.SignIn(signInRequest, (signInResult) => { if (signInResult.IsOk()) { // 4. 成功回调:signInResult.GetSignInResult()返回完整响应体 var signInResponse = signInResult.GetSignInResult(); Debug.Log($"Login Success: ID Token = {signInResponse.IdToken}"); Debug.Log($"Login Success: Auth Code = {signInResponse.Code}"); // 后端校验用 } else { Debug.LogError($"Login Failed: {signInResult.Status.StatusCode}"); } }); } else { Debug.LogError($"HMS Core unavailable: {result.Status.StatusCode}"); } }); } }参数说明:
SetScope("email profile"):不是字符串拼接,是华为定义的固定scope列表,email和profile必须小写,空格分隔;若请求phone需单独申请权限。SetResponseType("code id_token"):生产环境必须同时请求code(用于后端校验)和id_token(前端快速解析用户信息),只传id_token会导致signInResult.GetSignInResult().Code为空。SignIn()回调中的signInResult.Status.StatusCode是HMS内部状态码(如8000表示用户取消,8001表示网络错误),不是HTTP状态码,需查 HMS错误码文档 。
3.2 Push Kit:设备Token不是“获取一次”,而是生命周期监听
Push Kit的Token(即token)并非调用HmsInstanceId.GetInstance().GetToken()就能稳定拿到。它受三重影响:HMS Core版本、设备网络状态、华为推送服务开关。因此,demo中采用主动拉取 + 被动监听双保险:
// Assets/Scripts/Push/DemoPushManager.cs public class DemoPushManager : MonoBehaviour { private HmsInstanceId instanceId; void Start() { instanceId = HmsInstanceId.GetInstance(); // 1. 主动拉取Token(首次或Token失效时) GetToken(); // 2. 注册Token刷新监听器(关键!) HmsMessaging.GetInstance().SetTokenRefreshListener(new TokenRefreshListener()); } void GetToken() { instanceId.GetToken("your_project_id", "HCM", (tokenResult) => { if (tokenResult.IsOk()) { string token = tokenResult.GetToken(); Debug.Log($"Push Token: {token}"); // 上传至自己服务器 UploadTokenToServer(token); } else { Debug.LogError($"Get Token Failed: {tokenResult.Status.StatusCode}"); // 失败时尝试延迟重试(避免频繁轮询) Invoke(nameof(GetToken), 30f); } }); } } // 自定义监听器:当HMS Core主动刷新Token时触发 public class TokenRefreshListener : JavaProxy, IHmsTokenRefreshListener { public void OnNewToken(string token) { Debug.Log($"Token Refreshed: {token}"); // 此处必须再次上传新Token UploadTokenToServer(token); } }关键点:
GetToken()的第一个参数"your_project_id"必须与agconnect-services.json中的client_id前缀一致(如client_id为100000001234567890123#com.example.app,则此处填100000001234567890123)。SetTokenRefreshListener()必须在GetToken()之后立即注册,否则可能错过首次刷新事件。- Token有效期通常为6个月,但设备重置、HMS Core升级、用户手动清除HMS数据都会导致Token变更,不能缓存Token到PlayerPrefs长期使用。
3.3 Analytics Kit:事件埋点不是“打点就完”,而是属性约束与采样率控制
Analytics Kit的HmsAnalyticsInstance支持自定义事件,但有严格约束:
- 事件名(
eventName)长度≤40字符,仅允许字母、数字、下划线,且不能以数字开头 - 属性名(
key)长度≤40字符,值(value)长度≤100字符,且value类型只能是string、long、double
demo中封装了安全埋点方法:
// Assets/Scripts/Analytics/DemoAnalyticsManager.cs public class DemoAnalyticsManager : MonoBehaviour { private HmsAnalyticsInstance analytics; void Start() { analytics = HmsAnalytics.GetInstance(); // 开启Debug模式(仅开发环境) analytics.SetAnalyticsEnabled(true); analytics.SetReportEnable(true); } // 安全埋点:自动截断超长字段,过滤非法字符 public void LogEvent(string eventName, Dictionary<string, object> parameters) { if (string.IsNullOrEmpty(eventName) || eventName.Length > 40) return; // 清洗事件名:只保留字母数字下划线,首字符强制为字母 string safeName = Regex.Replace(eventName, @"[^a-zA-Z0-9_]", "_"); if (char.IsDigit(safeName[0])) safeName = "e_" + safeName; // 构建安全参数字典 Bundle bundle = new Bundle(); foreach (var kvp in parameters) { string key = Regex.Replace(kvp.Key, @"[^a-zA-Z0-9_]", "_").Substring(0, Math.Min(40, kvp.Key.Length)); object value = kvp.Value; if (value is string s && s.Length > 100) value = s.Substring(0, 100); else if (value is long l) bundle.PutLong(key, l); else if (value is double d) bundle.PutDouble(key, d); else if (value is string) bundle.PutString(key, (string)value); } analytics.LogEvent(safeName, bundle); } } // 使用示例 public void OnLevelComplete(int levelId, float timeCost) { var paramsDict = new Dictionary<string, object> { {"level_id", levelId}, {"time_cost_sec", timeCost}, {"player_level", PlayerPrefs.GetInt("player_level", 1)} }; DemoAnalyticsManager.Instance.LogEvent("level_complete", paramsDict); }参数说明:
SetAnalyticsEnabled(true):开启分析功能(默认关闭)SetReportEnable(true):开启上报(默认开启,但显式设置更稳妥)LogEvent()的bundle参数必须是AndroidJavaObject类型的Bundle,不能直接传Dictionary,demo中已封装转换逻辑。- 生产环境务必调用
analytics.SetReportEnable(false)关闭上报以节省流量,或通过AB测试灰度开启。
3.4 IAP Kit:商品查询不是“查一次”,而是缓存+失效检测
IAP Kit的商品查询(ProductClient.QueryInAppProducts())耗时较长(平均800ms+),且受网络波动影响大。demo采用三级缓存策略:
- 内存缓存:
Dictionary<string, InAppProduct>保存最近查询结果 - 本地缓存:
PlayerPrefs存储商品列表JSON(带时间戳) - 服务端校验:每次查询前先比对本地缓存时间,超24小时则强制刷新
// Assets/Scripts/IAP/DemoIAPManager.cs public class DemoIAPManager : MonoBehaviour { private ProductClient productClient; private readonly string CACHE_KEY = "iap_product_cache"; private readonly int CACHE_EXPIRE_HOURS = 24; void Start() { productClient = HmsIapManager.GetInstance().GetProductClient(); } public void QueryProducts(List<string> productIds, Action<List<InAppProduct>> onSuccess, Action<string> onError) { // 1. 检查本地缓存是否有效 string cacheJson = PlayerPrefs.GetString(CACHE_KEY, ""); if (!string.IsNullOrEmpty(cacheJson)) { try { var cacheData = JsonUtility.FromJson<CacheData>(cacheJson); var now = DateTime.Now; var cacheTime = DateTime.FromBinary(cacheData.Timestamp); if ((now - cacheTime).TotalHours < CACHE_EXPIRE_HOURS) { // 缓存有效,直接返回 onSuccess?.Invoke(cacheData.Products); return; } } catch { /* JSON解析失败,忽略缓存 */ } } // 2. 缓存失效或不存在,发起网络查询 productClient.QueryInAppProducts(productIds, (result) => { if (result.IsOk()) { var products = result.GetInAppProducts(); // 3. 更新缓存 var cacheData = new CacheData { Timestamp = DateTime.Now.ToBinary(), Products = products }; PlayerPrefs.SetString(CACHE_KEY, JsonUtility.ToJson(cacheData)); PlayerPrefs.Save(); onSuccess?.Invoke(products); } else { onError?.Invoke($"Query failed: {result.Status.StatusCode}"); } }); } } [System.Serializable] public class CacheData { public long Timestamp; public List<InAppProduct> Products; }关键参数:
productIds:商品ID列表,必须与AGC控制台中配置的Product ID完全一致(大小写敏感)QueryInAppProducts()返回的InAppProduct对象中,PriceAmountMicros是价格(单位:微元),需除以1000000得到元;PriceCurrencyCode是货币代码(如CNY)- 缓存时间
CACHE_EXPIRE_HOURS可根据业务调整,游戏道具类建议2~4小时,会员订阅类建议24小时
4. 避坑指南:四个血泪经验总结,省下三天排查时间
4.1 现象:Unity Editor中HMSClient: Failed to connect to HMS Core反复报错,但真机运行正常
原因:HMS Core服务仅在真实华为设备或华为模拟器(HUAWEI DevEco Studio Emulator)中运行,Unity Editor无法加载HMS Core的Android Service。这是设计使然,非Bug。很多开发者误以为Editor必须跑通,于是疯狂修改AndroidManifest.xml或proguard-rules.pro,反而引入新问题。
解决:接受Editor不支持HMS调用的事实。所有HMS相关逻辑必须包裹#if UNITY_ANDROID宏,Editor下走Mock逻辑或直接跳过。例如:
#if UNITY_ANDROID // 真机执行HMS调用 authService.CheckAvailability(...); #else // Editor下模拟成功 Debug.Log("Mock HMS check success"); #endif4.2 现象:AGC控制台已配置签名证书指纹,但真机仍提示Sign in failed: 8000(用户取消)
原因:华为要求APK签名证书的SHA-256指纹必须与AGC中配置的完全一致,且区分Debug与Release签名。常见错误是只配置了Release指纹,但用Unity的Development Build(Debug模式)运行,此时使用的是Unity默认debug keystore,其SHA-256与AGC中不同。
解决:
- 获取当前Build使用的keystore指纹:
keytool -list -v -keystore "C:\Users\YourName\.android\debug.keystore" -alias androiddebugkey -storepass android -keypass android - 将输出的
SHA256值(去掉冒号)粘贴到AGC控制台的“应用管理 > 应用信息 > 应用签名”中 - 若使用自定义keystore,确保Unity → Player Settings → Publishing Settings → Keystore中路径与密码正确,并用相同keystore生成指纹
4.3 现象:Push Token获取成功,但华为推送后台发送消息后设备收不到
原因:华为推送需满足三个条件才可送达:
- 设备已安装HMS Core且版本≥5.0.0
- 设备网络畅通,且能访问
https://push-api.cloud.huawei.com(国内直连,无需代理) - 应用进程未被系统深度清理(华为手机需在“手机管家 > 应用启动管理”中将你的应用设为“允许自启动”、“允许关联启动”、“允许后台活动”)
解决: - 在真机上进入“设置 > 应用 > 应用启动管理”,找到你的应用,关闭“智能省电”并开启所有启动权限
- 使用
adb shell dumpsys activity service com.huawei.hms.push检查推送服务状态 - 若仍失败,在AGC推送后台发送时勾选“测试设备”,输入设备Token单独发送,排除群发配置问题
4.4 现象:IAP支付成功回调中PurchaseResultInfo.PurchaseState始终为0(未支付)
原因:PurchaseState为0表示“未支付”,但实际可能是支付成功后服务端未及时通知客户端。根本原因是HmsIapManager.GetInstance().GetPmsClient().CreatePurchaseIntent()返回的Intent未被正确处理。
解决:
- 确保
AndroidManifest.xml中已声明<activity android:name="com.huawei.hms.iap.IapActivity" ... /> - 在Unity的
AndroidManifest.xml中添加<intent-filter>:<activity android:name="com.huawei.hms.iap.IapActivity" android:exported="true"> <intent-filter> <action android:name="com.huawei.hms.iap.ACTION_IAP_ACTIVITY" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> - 在Unity
Activity的OnActivityResult中必须调用HmsIapManager.GetInstance().GetPmsClient().HandleActivityResult(...),demo中已封装在HuaweiMobileServices/Editor/AndroidManifestPostprocessor.cs中自动注入
4.5 现象:Analytics事件在AGC后台看不到数据,或延迟超2小时
原因:Analytics Kit默认启用数据压缩与批量上报,且有最低上报阈值(10条事件或30秒超时)。开发阶段因事件稀疏,常导致“零上报”。
解决:
- 开发时调用
HmsAnalytics.GetInstance().SetAnalyticsEnabled(true)后,必须再调用HmsAnalytics.GetInstance().SetReportEnable(true)(部分版本默认false) - 强制立即上报:
HmsAnalytics.GetInstance().ForceReport()(仅调试用,勿在生产环境调用) - 检查AGC后台“分析服务 > 数据管理 > 数据源”中,是否已开启“应用内事件”采集开关
5. 进阶技巧:用ADB命令直连HMS Core,绕过Unity层定位问题
当你遇到“Unity日志没报错,但功能就是不工作”的情况,最有效的手段不是加Debug.Log,而是用ADB直连HMS Core服务,查看底层通信。这招我用了五年,救过三次上线前的火线故障。
5.1 查看HMS Core服务状态与版本
HMS Core作为系统级服务,其状态直接影响所有Kit。用以下命令确认:
# 查看HMS Core是否运行 adb shell ps | grep "com.huawei.hwid" # 查看HMS Core版本(精确到build号) adb shell dumpsys package com.huawei.hwid | grep versionName # 查看HMS Core进程PID及内存占用 adb shell dumpsys meminfo com.huawei.hwid解读:
- 若
ps命令无输出,说明设备未安装HMS Core或被禁用,需引导用户去应用市场安装 versionName必须≥6.10.0.300(对应HMS Unity Plugin 6.10.0),旧版本不支持Unity 2021.3+的Target SDK 33meminfo中TOTAL PSS超过200MB可能表明服务异常,需重启设备
5.2 抓取HMS Core网络请求(无需Root)
HMS Core所有网络请求均走https://*.cloud.huawei.com域名,可用ADB的logcat过滤关键词:
# 实时抓取HMS Core所有网络请求(含URL、状态码、耗时) adb logcat -s HMSClient:H HMSNetwork:H # 专门抓取Account Kit登录请求 adb logcat -s HuaweiIdAuthService:H # 抓取Push Kit Token请求 adb logcat -s HmsInstanceId:H关键日志模式:
[HMSNetwork] Request URL: https://oauth-login.cloud.huawei.com/oauth2/v3/token→ Account Kit换Token请求[HMSNetwork] Response Status: 200, Cost: 423ms→ 成功响应,耗时423毫秒[HMSNetwork] Error: java.net.UnknownHostException: Unable to resolve host "push-api.cloud.huawei.com"→ DNS解析失败,检查网络
5.3 强制触发HMS Core更新与重置
当怀疑HMS Core缓存损坏(如Token一直不刷新),可执行以下命令:
# 清除HMS Core数据(等效于设置中“清除数据”) adb shell pm clear com.huawei.hwid # 重启HMS Core服务(无需重启设备) adb shell am force-stop com.huawei.hwid adb shell am startservice -n com.huawei.hwid/.service.HuaweiIdService # 检查服务是否重启成功 adb shell ps | grep "com.huawei.hwid"注意:
pm clear会清除所有HMS相关登录态,用户需重新登录,仅限开发调试使用。
5.4 验证AGC配置文件是否正确注入
agconnect-services.json是否被正确打包进APK,是90%初始化失败的根源。用以下命令验证:
# 解包APK,提取assets目录 unzip -p YourApp-release.apk assets/agconnect-services.json | python -m json.tool # 或直接查看是否存在 unzip -l YourApp-release.apk | grep "agconnect-services.json"正确输出应包含:
{ "client_id": "100000001234567890123#com.example.app", "package_name": "com.example.app", "project_id": "100000001234567890123", "app_id": "100000001234567890123" }若client_id中#后包名与Unity设置不一致,或project_id为空,则说明AGC配置导出错误或Postprocessor未执行。
从那以后我每次接入新Kit,都强制走一遍这四步ADB验证:查服务状态 → 抓网络日志 → 清缓存重试 → 验证JSON注入。它不保证100%解决问题,但能把排查时间从“一天”压缩到“十五分钟”。希望帮到你。
本文还有配套的精品资源,点击获取