简介:这是一份面向Unity开发者与Android应用接入人员的华为HMS SDK演示工程,围绕在Unity项目中集成华为账号、游戏、推送等能力,提供可直接参照的工程结构与示例代码。压缩包共2000个文件,涵盖C#脚本、DLL库、JAR包、Java源码、AndroidManifest等XML配置、资源文件与日志,整体约28.82MB,既有SDK依赖,也保留Unity工程资源与打包产物,便于对照排查接入环境。已有1845人学习,适合需要了解HMS SDK在Unity中落地方式的初中级开发者。工程内含HuaweiSdkDemo相关示例、初始化与登录等接口调用痕迹、Android插件源码及场景资源,可作为搭建测试工程、核对依赖版本和梳理调用链的参考,帮助读者理解Unity与华为SDK之间的工程组织方式。
1. 版本先对齐:Unity 接入华为 HMS SDK 最容易翻车的一步
Unity 接入华为 HMS SDK 这件事,劝退新人的从来不是代码,是版本。我见过太多次:unitypackage 导进去了,插件菜单点了,AndroidManifest 也更新了,结果一打包就卡在 Gradle,报一串找不到 agconnect-core 的错;或者包能装上,点开闪退,logcat 里只有一句客户端信息为空。这类问题九成不是 SDK 有 bug,是 Unity 版本、JDK、Gradle、targetSdkVersion 四者里有一个跟插件要求对不上。这套环境适合已经能把 Unity 项目打出 Android 包、准备接登录、推送、支付这类系统能力的人;如果你连一次真机安装都没跑通,先回去把空工程打出来,别急着上 SDK。
2. 装前把环境钉死:Unity 版本、JDK 与 Gradle 三件套
装 SDK 之前,我会先花二十分钟把环境信息抄在一张纸上:Unity 具体版本号、JDK 大版本、Gradle 版本、Android targetSdkVersion、包名、签名文件路径。这六项定不下来,后面每改一次配置都要重新排一遍雷。最省事的做法是新建一个干净的空工程专门用来验证 SDK,跑通之后再往正式工程里搬,这样出问题时能立刻判断是 SDK 的问题还是自己项目里其它插件打架。
2.1 Unity 与 Android Build Support 的勾选姿势
用 Unity Hub 安装编辑器的时候,Android Build Support 下面有三个子项必须一起勾:Android SDK & NDK Tools、OpenJDK。很多人只勾了最外层那个 Android Build Support,以为带 SDK 了,结果 Preferences 里 External Tools 全是红的,一构建就提示找不到 sdkmanager。这个坑不影响编辑器启动,只在你点 Build 的那一刻爆发,所以特别容易被忽略。
版本选择上,我的经验是不要追最新。HMS 的 Unity 插件对大版本的支持通常滞后一两个小版本,插件 README 里写支持的区间,超出区间不是一定不能用,但你会成为第一个踩雷的人。下面这张表是我自己用过的组合,具体以你下载的插件包里 README 写的为准:
| Unity 版本 | JDK | 说明 |
|---|---|---|
| 2019.4 LTS | JDK 8 | 兼容性最稳,老项目迁移常用 |
| 2020.3 LTS | JDK 8 | 我目前主力组合,插件适配成熟 |
| 2021.3 LTS | JDK 11 | 需要较新的插件版本,Gradle 模板改动多 |
| 2022 及以上 | JDK 11 | 谨慎,先拿空工程验证 |
装好之后打开 Preferences → External Tools,确认 JDK、SDK、NDK、Gradle 四项都有路径。如果用 Unity 自带的,就保持默认勾选;如果公司要求统一用某一套 Android SDK,就把 Unity 自带那几项勾掉,手动指过去,最忌讳一半自带一半手动,路径混着用。
2.2 JDK 与 Gradle 的版本对不上会怎么炸
JDK 版本不对的报错长这样:Unsupported class file major version 55,或者构建时提示Could not determine java version。55 对应 JDK 11,52 对应 JDK 8,看到这行字就说明编译用的 JDK 和你配置的 Gradle 期望的不一致。先跑两条命令确认当前环境:
# 看系统默认 JDK,注意这里是系统环境,不是 Unity 里配的那个 java -version # 看 Unity 工程生成的 Gradle 版本 cd Temp/gradleOut ./gradlew -v第一条输出里的 version 和第二条里的 JVM 必须能对上。如果 Unity 里配的是自带 OpenJDK,但系统 JAVA_HOME 指向另一个版本,容易在命令行构建时串味。我的做法是干脆不在系统层配 JAVA_HOME,全部交给 Unity 的 External Tools 管,命令行只在排错时临时export JAVA_HOME=...一下。
Gradle 这边,如果项目开了 Custom Gradle Template,工程里会多出Assets/Plugins/Android/mainTemplate.gradle和gradleTemplate.properties。后者有几行是必配的:
# 打包内存,SDK 依赖多的时候 2G 容易 OOM org.gradle.jvmargs=-Xmx4096M -Dfile.encoding=UTF-8 # 新版 Android 依赖基本都要求开启 android.useAndroidX=true android.enableJetifier=true # 关掉 R8 的完整模式,减少混淆阶段跟 SDK 的冲突 android.enableR8.fullMode=falsejvmargs给到 4096M 是因为 HMS 的 aar 加起来体积不小,加上你自己的业务依赖,2048M 在低配机器上大概率在中途 OOM,报错信息还特别不直观,只说Java heap space。enableJetifier打开会让构建变慢,但能自动把老的 support 库转成 AndroidX,接第三方 SDK 时省掉很多手工改包名的事。
2.3 包名、签名、minSdkVersion:三件必须提前定死的事
包名一旦在 AGC 后台建了应用就不能改,只能重建。所以第一步是把 Player Settings → Other Settings 里的 Package Name 定下来,并且跟后台一个字一个字对上,大小写都算。我见过有人后台填com.demo.hmsgame,工程里写com.demo.HmsGame,装上去登录回调永远不回来,查了两天才发现是大小写。
签名文件也要提前生成好,不要等到最后出 release 包才想起来:
# 生成一个有效期 25 年的签名,HMS 后台登记指纹要用 keytool -genkeypair -v \ -keystore hms-demo.keystore \ -alias hmsdemo \ -keyalg RSA \ -keysize 2048 \ -validity 9125参数含义:-keysize 2048是 RSA 密钥长度,低于 2048 有些平台会拒绝;-validity 9125是 25 年,应用市场上架普遍要求有效期覆盖到 2033 年以后,写短了后面要换签名,换签名等于换应用,用户全部要重装。生成完把 keystore 文件和密码放在工程外面单独保管,不要提交进 git。
minSdkVersion 同样要提前统一。HMS 各 Kit 对最低版本有要求,Unity 默认的 22 或 19 可能偏低,报错形式是uses-sdk:minSdkVersion 19 cannot be smaller than version 21 declared in library。改法是在 Player Settings → Minimum API Level 抬到 21 或以上,如果项目里有第三方 aar 声明了更高版本,就按最高的那个来。这一步没做,会在 Manifest 合并阶段直接失败,而不是等到运行时报错,反倒好排查。
3. 导入 HMS SDK:从 AGC 后台到 Unity 工程的落地动作
环境定死之后,接下来是按顺序落地文件。这里最容易犯的错是顺序颠倒——先把 unitypackage 导进工程,再去后台建应用,结果 json 文件拿回来不知道往哪放,随手丢在 Assets 根目录,运行起来提示配置缺失。我习惯的顺序是:后台建应用 → 拿 json 和参数 → 导入插件 → 按插件菜单配置 → 改 Manifest 和 Gradle。
3.1 在 AGC 建应用并下载 agconnect-services.json
进 AGC 后台,新建应用,包名必须和 Unity 工程里完全一致。建完之后在「项目设置」里能拿到几个关键参数:APP ID、API Key、Client Secret,以及一个agconnect-services.json配置文件。这个 json 是整套接入的核心,里面写了包名、APP ID、各 Kit 的开关状态,SDK 启动时会读它。
要注意的是,json 是从后台下载的静态文件,一旦你在后台改了包名、加了新的 Kit、或者换了签名指纹,都得重新下载覆盖。很多「昨天还好好的今天就不行了」的情况,都是后台改过配置但本地文件还是旧的。我的习惯是在工程根目录建一个HMSConfig/放原始文件,每次覆盖前先比对一下修改时间。
3.2 导入 unitypackage 与目录摆放
下载下来的插件包一般是.unitypackage,直接双击或者 Assets → Import Package 导入。导入后菜单栏会多出一个 HMS 或者 Huawei 的菜单项,不同版本叫法略有差异。点进去的 Settings 面板会让你填 APP ID 和包名,填完点保存,插件会做两件事:把配置写进Assets/Plugins/Android/下的对应文件,以及尝试更新 AndroidManifest。
json 文件的位置是这一步最容易出错的点。插件不同版本对这个文件的查找路径要求不完全一样,有的认Assets/Plugins/Android/下的,有的认工程根目录,还有的要求放在Assets/顶层。判断方法很直接:构建出 apk 之后解包看assets/目录里有没有这个 json,没有就是位置错了。我第一次接的时候就是因为放错了一层目录,包能装能开,一调登录就返回空,翻了两小时日志。
导入之后确认工程里出现下面这些目录,缺一个都建议重新导入:
Assets/Plugins/Android/下的 aar 与 AndroidManifest.xmlAssets/Huawei/或插件自带目录下的 C# 封装脚本Assets/Plugins/Android/assets/下的agconnect-services.json
3.3 AndroidManifest 与 Gradle 模板的必改项
Manifest 里需要补的是应用级元数据,让 SDK 启动时能读到 APP ID 和 API Key。插件自动更新通常会写进去,但没有的话要手工加:
<application> <!-- APP ID 和 API Key 从 AGC 后台项目设置里复制,注意 appid= 前缀不能丢 --> <meta-data android:name="com.huawei.hms.client.appid" android:value="appid=你的APP_ID" /> <meta-data android:name="com.huawei.hms.client.api_key" android:value="你的API_KEY" /> </application>android:value里那个appid=前缀是固定格式,少写这四个字符加等号,SDK 解析时会当作非法值直接跳过,表现为初始化返回失败但不告诉你原因。这一点文档里写得很小,实际踩的人不少。
Gradle 那边要在mainTemplate.gradle里加上 HMS 的依赖仓库,否则构建时会报找不到com.huawei.agconnect系列包:
allprojects { repositories { google() mavenCentral() // HMS 依赖的仓库,加在 google() 后面即可 maven { url 'https://developer.huawei.com/repo/' } } } dependencies { // 版本号从插件目录下的 README 或 AGC 后台的接入指引里复制,不要凭记忆填 implementation 'com.huawei.agconnect:agconnect-core:<版本号>' implementation 'com.huawei.hms:base:<版本号>' }仓库地址写错一个字符就是 404,而 Gradle 的报错往往只显示「Could not find xxx」,不会告诉你仓库地址不对,得往上翻几屏看Could not resolve后面的 URL。另外allprojects块要放对位置,有的模板本身已经有这个块,直接追加maven行就行,重复定义会导致模板冲突。
如果项目开了Custom Main Gradle Template,注意构建时代码里Assets/Plugins/Android/mainTemplate.gradle会覆盖默认模板,改错了不会报错,只会静默生效。改完之后建议做一次Assets → External Dependency Manager → Android Resolver → Force Resolve(装了 EDM4U 的话),让依赖重新拉一遍。
4. 初始化与配置:把 APP_ID、包名和回调一次配对
文件放对只是及格线,真正决定能不能跑通的是初始化时机和参数。HMS 的初始化是异步的,回调没回来之前调任何 Kit 都可能返回未初始化错误。我见过最常见的写法是把初始化塞进第一个场景某个空物体的 Start 里,结果场景切换太快的项目在登录界面弹出来的时候初始化还没完成,表现就是登录按钮点了没反应。
4.1 初始化调用时机:为什么不能写在第一个场景的 Start 里
正确做法是在游戏启动最早的入口调用一次,并且保证调用对象跨场景不销毁。Unity 里通常用一个挂了DontDestroyOnLoad的引导脚本承担这个角色:
using UnityEngine; using Huawei.Hms.Agent; // 命名空间随插件版本略有差异,以导入后的 API 为准 public class HmsBootstrap : MonoBehaviour { private static bool s_inited = false; void Awake() { // 跨场景常驻,避免切场景后回调丢失 DontDestroyOnLoad(gameObject); if (s_inited) return; // 防止重复初始化 s_inited = true; // 初始化是异步的,结果在回调里给出 HMSAgent.Init(OnInitResult); } private void OnInitResult(int retCode) { if (retCode == 0) { Debug.Log("[HMS] init success"); // 只有这里返回成功,后面的登录、推送、支付才可用 } else { Debug.LogError($"[HMS] init failed, retCode={retCode}"); } } }retCode == 0表示成功,非 0 的取值各家 Kit 含义不同,文档里有对照表,排错时先把 retCode 记下来再查,比盲猜快得多。s_inited这个静态标记看着多余,但热更新或者场景重载的场景下,重复初始化会让 SDK 内部状态错乱,表现是偶发回调不触发,属于那种「玄学」级别的偶发问题。
还有一点:初始化失败不要直接抛异常阻断游戏。HMS 的很多能力是可选增强,初始化失败时应该降级到自有账号体系或者游客模式,而不是白屏。这个判断要写在业务层,SDK 层不管。
4.2 登录、推送、支付的最小可用配置
每个 Kit 的接入都分三步:Manifest 声明、初始化后调用、回调处理。以账号登录为例,Manifest 里需要声明对应的 Activity,插件导入时一般已经写好,检查一下有没有被你的主 Manifest 覆盖掉。调用侧大致是这样:
// 登录是异步的,回调里拿到 authCode,再拿去自己的服务器换 token HMSAccount.Login((result) => { if (result.IsSuccess) { // authCode 有时效性,拿到后立刻上送服务端,不要存在本地 string authCode = result.AuthCode; Debug.Log($"[HMS] login ok, code length={authCode?.Length}"); } else { // 用户主动取消也会走这里,要跟真正的失败区分开 Debug.LogWarning($"[HMS] login failed, code={result.RetCode}"); } });注意authCode不能当作用户标识长期保存,它有过期时间,正确流程是上送自己的服务端换 session。RetCode里用户取消和网络失败是不同值,UI 上要区别对待,取消不该弹错误提示。
推送这块,除了 Manifest 里声明服务,还要确认后台的推送开关是打开的。测试阶段最容易忽略的是「测试设备」没登记,导致后台发消息永远显示送达 0。支付则是必须先在后台配置商品和回调地址,本地代码写对了但商品没上架,调用一样会失败,且失败码和参数错误长得差不多。
4.3 release 包的混淆与签名配置
debug 包一切正常、release 包登录失败,几乎是每个接 SDK 的人都会遇到一次的经典剧情。原因通常是混淆把 SDK 的回调类或者反射用到的类名改了。解决办法是在proguard-user.txt里补规则:
# 保留 HMS 相关类,避免混淆后回调找不到 -keep class com.huawei.** { *; } -keep class com.huawei.agconnect.** { *; } # 保留 Android 组件入口 -keep public class * extends android.app.Activity -keep public class * extends android.app.Service规则写完要确认构建时真的用上了这个文件:Player Settings → Publishing Settings 里勾选Custom ProGuard File,文件放到Assets/Plugins/Android/下。只建文件不勾选,等于没写。
签名这块,release 用的 keystore 指纹必须登记到 AGC 后台。debug 和 release 用的签名不同,很多人只在后台填了 debug 的指纹,测试一切正常,一上架就登录失败。把 SHA256 指纹也算出来一起填:
# 输出签名指纹,把 SHA256 那行复制到 AGC 后台 keytool -list -v -keystore hms-demo.keystore -alias hmsdemo5. 排查记录:五条最费时间的踩坑
下面这五条是我在不同项目里真实耗过时间的,按发生阶段排。写的时候尽量保留了当时的报错特征,方便对照。
5.1 构建阶段的三类报错
现象一:Could not find com.huawei.agconnect:agconnect-core。构建刚开始就失败,提示解析不到依赖。原因基本只有两个:mainTemplate.gradle里没加 HMS 的 maven 仓库,或者仓库地址写错。解决是把maven { url 'https://developer.huawei.com/repo/' }加到allprojects.repositories里,并且确认没被后面的仓库配置覆盖。Gradle 的仓库是有顺序的,先找到就不再找,所以 HMS 那段要放在google()之后、其它镜像之前。
现象二:Manifest merger failed,提示 minSdkVersion 冲突。原因是某个 aar 声明了比你工程更高的最低版本。解决是把 Player Settings 里的 Minimum API Level 抬到冲突方要求的版本,实在不能抬就在 Manifest 里加tools:replace="android:minSdkVersion",但这是权宜之计,抬版本才是正解。
现象三:Java heap space,构建到一半 OOM。原因是 Gradle 默认堆内存太小,HMS 的 aar 加上业务依赖后不够用。解决是改gradleTemplate.properties里的org.gradle.jvmargs=-Xmx4096M。加到 4096 还不行,多半是机器本身内存吃紧,关掉几个 Chrome 标签页比继续加参数有效。
5.2 运行阶段:闪退与初始化失败
现象四:包能装上,点开立刻闪退,logcat 只有一行client info is null。原因是 SDK 启动时没读到agconnect-services.json,或者读到了但包名对不上。解决分两步:先用adb shell pm path 你的包名找到 apk 路径,解包确认assets/下真的有这个 json;再核对 json 里的package_name字段和工程里的 Package Name 是否完全一致,包括大小写。我遇到过一次是构建缓存导致的,Clean 之后重新构建就好了,所以确认文件存在却没生效时,先 Clean。
现象五:初始化一直不回调,日志里没有任何 HMS 输出。原因是初始化调用的时机太早,早于 SDK 内部依赖的某些 Android 组件完成注册;或者是调用对象被销毁了。解决办法是把初始化从Start挪到Awake并挂DontDestroyOnLoad,同时确认调用前没有其它代码先调用了 Kit 的接口。排查时用adb logcat -s HMSAgent:* HuaweiID:*过滤关键字,能省掉大量翻日志的时间。
5.3 上架与签名相关的两个坑
release 包登录失败是最典型的签名问题。现象是 debug 包正常,release 包装上后一切调用返回鉴权失败。原因就是后台没登记 release 签名的指纹。解决方法前面提过,keytool -list -v拿到 SHA256 后填进后台,然后重新下载agconnect-services.json覆盖到工程里——注意这一步不能省,后台改了配置本地文件不更新等于没改。
另一个坑是用了 V2 签名但没开 V1。部分机型上装 release 包会提示安装失败而不是运行失败,很容易误判成包体损坏。解决方法是在 Publishing Settings 里同时勾选 V1 和 V2,虽然 V1 已经过时,但兼容性上留一道保险更省事。
6. 装完怎么验证:真机上跑通 HMS 的检查顺序
验证这件事,我吃过「在编辑器里看日志觉得没问题,真机一装全是错」的亏。Unity 编辑器里跑的是 Mono 模拟环境,HMS 的 aar 根本没加载,所以任何在编辑器里的「成功」都不算数。必须出 apk,装真机,按顺序过一遍。
第一步先看包体和配置有没有进对。用 adb 把 apk 拉出来解包,确认assets/agconnect-services.json存在且内容是最新的:
# 找到已安装应用的实际路径并解包,检查配置文件有没有打进去 adb shell pm path com.demo.hmsgame adb pull /data/app/.../base.apk ./check.apk unzip -l check.apk | grep agconnect第二步看初始化。启动应用后立刻抓日志,只过滤 HMS 相关 tag,避免被业务日志淹没:
adb logcat -c && adb logcat -s HMSAgent:* AGConnect:* HuaweiID:*这一步的目标只有一个:看到初始化成功的那行输出,retCode为 0。看不到就回去查第 4 章和第 5 章,不要往后走。
第三步逐项验能力,顺序是登录 → 推送 → 支付,因为后者依赖前者的账号态。每一项都按「触发 → 回调 → 后台确认」三段验,比如登录成功后再去后台看用户数有没有加 1,支付成功后在后台看订单状态。只看客户端回调是不够的,客户端成功服务端没收到的情况并不少见。
最后整理一张自己的检查表,每次出新包都过一遍:
| 检查项 | 通过标准 | 常见失败原因 |
|---|---|---|
| 配置文件 | apk 内存在且为最新 | 后台改配置未重新下载 |
| 包名一致性 | 工程与后台完全相同 | 大小写不一致 |
| 初始化 | 日志 retCode 为 0 | 时机太早或被销毁 |
| 登录 | 回调拿到 authCode 且服务端可换 token | 签名指纹未登记 |
| 推送 | 后台显示送达数大于 0 | 测试设备未登记 |
| release 包 | 与 debug 表现一致 | 混淆规则缺失 |
从那以后我每次接新的 SDK,都会先用一个空工程跑通最小闭环,再往业务工程里搬,绝不在一个已经装了七八个插件的工程里做第一次接入——那种环境里出问题,你连是哪个插件引起的都判断不了。希望帮到你。
本文还有配套的精品资源,点击获取