
简介本资源是面向Unity开发者、尤其是需要在华为设备上发布游戏的移动端工程师的华为HMS SDK接入示例工程围绕账号登录、推送、游戏服务等常见能力给出可运行的集成参考。压缩包共约2000个文件以bin、info、class、meta、java、xml、png、jar、cs、dll等为主涵盖Android原生库、Unity脚本、资源清单与配置说明整体约28.82MB结构接近真实工程目录便于对照排查。资源中保留了HuaweiSdkDemo示例代码与初始化、登录等接口调用片段可帮助读者理解IL2CPP后端下的接入流程、权限配置与打包测试要点并作为版本兼容与报错排查的参照。目前已有1842人学习下载适合具备一定Unity与Android基础、希望快速跑通华为渠道接入的开发者参考使用。1. Unity接入华为SDK demo从零跑通登录与支付的最小闭环很多做 Unity 手游的团队第一次接华为 SDK都会卡在同一个地方Unity 编辑器里跑得好好的一打安卓包就黑屏、闪退或者登录按钮点下去毫无反应。这不是代码写错了而是 Unity 和华为 SDK 之间隔着一层安卓原生桥接编辑器环境根本模拟不了。这篇笔记要讲的就是怎么从零搭一个能跑通的 Unity 接入华为 SDK demo把登录、支付这两个最核心的能力先跑起来再谈其他。适合已经会写 C#、但对安卓原生交互不熟、又必须把华为渠道包发出去的 Unity 开发者。我会按真实接入顺序走一遍环境准备、AAR 导入、桥接层写法、回调处理、打包验证最后把几个最容易翻车的地方单独拎出来讲。全程不依赖任何现成的商业插件纯手工接这样你才知道每一步到底在干什么。2. 接入前的环境与工程结构为什么不能直接在编辑器里测2.1 Unity 与华为 SDK 的版本匹配逻辑华为 SDK 对 Unity 版本和安卓构建管线有明确要求但官方文档往往只给一个范围实际踩坑都出在细节上。我一般会锁定 Unity 2021 LTS 或 2022 LTS因为这两个版本对 Gradle 和 AndroidX 的支持最稳再新的版本有时候反而会因为 AGP 升级导致 AAR 冲突。华为 SDK 这边AppGallery Connect 的 SDK 包通常分两部分基础能力包agconnect-core和具体服务包如 auth、iap。基础包必须和具体服务包版本对齐否则运行时会报类找不到。构建管线必须切到 IL2CPP ARM64这点没有商量余地。华为从某代机型开始就只收 ARM64 的包用 Mono 打出来的包连安装都过不了。Player Settings 里 Minimum API Level 建议设到 24Target API Level 设到 33 或更高具体看华为后台当时的要求。Scripting Backend 选 IL2CPP 之后打包时间会明显变长这是正常的别以为是卡死了。提示每次改完 Player Settings 里的架构或 API Level最好删掉 Library 目录重新导入一次否则 Gradle 缓存可能带着旧配置一起打包。2.2 工程目录该怎么摆Plugins/Android 下的文件组织Unity 接入任何安卓 SDK核心就是把 AAR 和 AndroidManifest 放到正确的位置。标准做法是在 Assets 下建 Plugins/Android 目录把华为给的 .aar 文件直接丢进去。如果有多个 AAR注意它们的依赖关系比如 auth 的 AAR 依赖 core 的 AAR两个都要放缺一个就会在打包时报 NoClassDefFoundError。AndroidManifest.xml 也要放在 Plugins/Android 下。华为 SDK 需要在 Manifest 里声明一些权限和组件比如网络权限、读取设备信息权限以及 AppGallery Connect 的 provider。这个文件不能直接覆盖 Unity 自动生成的那个而是要用 Unity 的 Manifest 合并机制只写增量部分。我一般会保留 Unity 默认生成的 Manifest 作为基础然后手动把华为要求的节点插进去。Assets/ Plugins/ Android/ agconnect-core-1.9.0.300.aar huawei-auth-1.9.0.300.aar huawei-iap-1.9.0.300.aar AndroidManifest.xml Scripts/ HuaweiSDK/ HuaweiBridge.cs HuaweiCallbackHandler.cs这个结构看起来简单但实际项目里经常有人把 AAR 放到 Assets 根目录下Unity 也能识别但打包时容易漏掉依赖。统一放 Plugins/Android 是最稳的。2.3 为什么编辑器模式必须做条件编译Unity 编辑器跑在 Windows 或 Mac 上根本没有安卓运行时所有华为 SDK 的 Java 类都不存在。如果你直接在 C# 里调用 AndroidJavaClass编辑器里会直接抛异常整个游戏都跑不起来。所以桥接层必须用 UNITY_ANDROID !UNITY_EDITOR 这样的宏把真机代码包起来编辑器里走一套空实现或者模拟返回。public class HuaweiBridge { public static void Init() { #if UNITY_ANDROID !UNITY_EDITOR using (var unityPlayer new AndroidJavaClass(com.unity3d.player.UnityPlayer)) using (var activity unityPlayer.GetStaticAndroidJavaObject(currentActivity)) using (var huaweiApi new AndroidJavaClass(com.huawei.hms.api.HuaweiApiAvailability)) { int status huaweiApi.CallStaticint(isHuaweiMobileServicesAvailable, activity); if (status ! 0) { Debug.LogError(HMS Core not available, status: status); return; } } Debug.Log(Huawei SDK init success); #else Debug.Log(Editor mode, skip Huawei SDK init); #endif } }这段代码的逻辑是先拿到 Unity 当前的 Activity然后通过华为的 HuaweiApiAvailability 检查设备上有没有安装 HMS Core。status 为 0 表示可用非 0 就是各种异常情况比如没装、版本太低、需要升级。编辑器模式下直接打日志跳过保证游戏逻辑不受影响。参数方面currentActivity 是 Unity 自动维护的不需要自己创建HuaweiApiAvailability 的类名和包名必须和 AAR 里的完全一致写错一个字母就会在真机上崩。3. 登录与支付的最小实现从 Java 桥接到 C# 回调3.1 用 AndroidJavaProxy 处理登录回调华为的登录接口是异步的调用之后结果通过回调返回。Unity 这边要用 AndroidJavaProxy 来模拟一个 Java 接口的实现。华为的 Auth 服务里登录回调接口通常是 com.huawei.hms.support.hwid.result.AuthHuaweiId 相关的 listener。你需要先查清楚当前 SDK 版本里回调接口的完整类名和方法签名然后写一个对应的 C# 代理类。public class HuaweiLoginCallback : AndroidJavaProxy { public Actionstring OnSuccess; public Actionint, string OnFailure; public HuaweiLoginCallback() : base(com.huawei.hms.support.hwid.service.HuaweiIdAuthService$AuthResultListener) { } void onSuccess(AndroidJavaObject authHuaweiId) { string openId authHuaweiId.Callstring(getOpenId); string displayName authHuaweiId.Callstring(getDisplayName); OnSuccess?.Invoke(openId | displayName); } void onFailure(int errorCode, string errorMsg) { OnFailure?.Invoke(errorCode, errorMsg); } }这个代理类的关键是 base 构造函数里的接口全名必须和 AAR 里定义的完全一致。onSuccess 和 onFailure 的方法名也要对得上Java 那边怎么写的C# 这边就得怎么命名大小写都不能错。拿到 authHuaweiId 之后通过 Call 方法反射调用 getOpenId 和 getDisplayName这两个是华为账号的唯一标识和昵称用来做游戏内的账号绑定。调用登录的代码大概长这样public static void Login(Actionstring onSuccess, Actionint, string onFailure) { #if UNITY_ANDROID !UNITY_EDITOR using (var unityPlayer new AndroidJavaClass(com.unity3d.player.UnityPlayer)) using (var activity unityPlayer.GetStaticAndroidJavaObject(currentActivity)) using (var authManager new AndroidJavaClass(com.huawei.hms.support.hwid.HuaweiIdAuthManager)) { var callback new HuaweiLoginCallback { OnSuccess onSuccess, OnFailure onFailure }; var paramsBuilder new AndroidJavaObject(com.huawei.hms.support.hwid.request.HuaweiIdAuthParamsHelper); var authParams paramsBuilder.CallAndroidJavaObject(setIdToken) .CallAndroidJavaObject(setProfile) .CallAndroidJavaObject(createParams); var service authManager.CallStaticAndroidJavaObject(getService, activity, authParams); service.CallAndroidJavaObject(signIn, callback); } #else onSuccess?.Invoke(editor_test_openid|EditorUser); #endif }这里先构建 HuaweiIdAuthParamsHelper设置需要 idToken 和 profile 信息然后 createParams 生成参数对象。再通过 HuaweiIdAuthManager.getService 拿到服务实例最后调 signIn 并传入回调。编辑器模式下直接返回一个假的 openId方便你在编辑器里调试后续逻辑。3.2 支付接口的调用顺序与参数含义华为支付IAP的流程比登录多几步先查商品信息再发起购买最后处理购买结果。商品信息需要在 AppGallery Connect 后台提前配置好拿到 productId 之后才能在代码里查。查询商品用 com.huawei.hms.iap.IapClient 的 getProductInfo 方法传入 productId 列表和价格类型。public static void QueryProducts(string[] productIds, Actionstring onResult) { #if UNITY_ANDROID !UNITY_EDITOR using (var unityPlayer new AndroidJavaClass(com.unity3d.player.UnityPlayer)) using (var activity unityPlayer.GetStaticAndroidJavaObject(currentActivity)) using (var iapClient new AndroidJavaClass(com.huawei.hms.iap.Iap)) { var client iapClient.CallStaticAndroidJavaObject(getIapClient, activity); var request new AndroidJavaObject(com.huawei.hms.iap.entity.ProductInfoReq); request.Set(priceType, 0); // 0 表示消耗型商品 request.Set(productIds, productIds); var result client.CallAndroidJavaObject(getProductInfo, request); onResult?.Invoke(result.Callstring(getProductInfoList)); } #else onResult?.Invoke(editor_mock_product_list); #endif }priceType 这个参数容易搞混0 是消耗型比如金币、道具1 是非消耗型比如去广告2 是订阅型。设错了后台对不上查出来的商品列表就是空的。productIds 是一个字符串数组对应后台配置的商品 ID大小写敏感。发起购买用 buy 方法传入 ProductInfo 对象和回调。购买回调里会返回一个 PurchaseResultInfo里面包含订单状态和购买令牌。订单状态分几种已购买、已取消、已退款等。拿到购买令牌之后还要把令牌发给你的游戏服务器由服务器去华为的订单校验接口验证验证通过才发货。这一步绝对不能省否则就是典型的“客户端自校验”漏洞很容易被刷单。3.3 回调线程与 Unity 主线程的交互华为 SDK 的回调不一定在 Unity 主线程上执行。如果你在回调里直接操作 GameObject 或调用 Unity API可能会报 “can only be called from the main thread” 的错误。稳妥的做法是在回调里只做数据解析然后把结果塞到一个线程安全的队列里在 Update 里取出来再处理。private static readonly ConcurrentQueueAction _mainThreadQueue new ConcurrentQueueAction(); public static void Enqueue(Action action) { _mainThreadQueue.Enqueue(action); } void Update() { while (_mainThreadQueue.TryDequeue(out var action)) { action?.Invoke(); } }在华为回调的 onSuccess 里不要直接更新 UI而是 Enqueue 一个 lambda把后续逻辑放进去。这样不管回调在哪个线程最终都在主线程执行。这个模式在接入任何安卓 SDK 时都通用不只是华为。4. 打包与真机验证从 Gradle 报错到登录成功4.1 自定义 Gradle 模板解决依赖冲突Unity 默认的 Gradle 模板有时候会和华为 SDK 的依赖打架典型报错是 “Duplicate class” 或者 “Program type already present”。这时候需要开启 Custom Main Gradle Template 和 Custom Launcher Gradle Template在 mainTemplate.gradle 里手动加华为的 Maven 仓库和依赖。repositories { maven { url https://developer.huawei.com/repo/ } } dependencies { implementation com.huawei.hms:base:6.9.0.301 implementation com.huawei.hms:auth:6.9.0.301 implementation com.huawei.hms:iap:6.9.0.301 }版本号要和 AAR 文件对应不能随便写。如果 AAR 是 1.9.0.300Gradle 里也写 1.9.0.300。仓库地址是华为的官方 Maven必须加否则 Gradle 找不到这些包。加完之后如果还报冲突用./gradlew :launcher: dependencies看依赖树找到冲突的包用 exclude 排除。4.2 真机日志抓取与常见错误码真机调试最怕的就是闪退没日志。安卓这边可以用 adb logcat 抓过滤 Unity 和华为的 tagadb logcat -s Unity:V Huawei:V HMS:V AndroidRuntime:EAndroidRuntime 的 E 级别日志会打出崩溃堆栈这是定位闪退的第一手资料。华为 SDK 常见的错误码有907135000 表示参数错误907135001 表示未登录907135002 表示网络异常6003 表示用户取消。这些错误码在华为的 API 文档里都有但文档往往只给一个列表实际排查时要结合 logcat 里的上下文看。注意如果 logcat 里看到 “HMS Core not available”先确认测试机是不是华为或荣耀设备非华为设备需要单独安装 HMS Core APK 才能跑。4.3 用华为后台的沙盒环境做支付测试支付测试不要用真实账号直接付钱华为提供了沙盒环境。在 AppGallery Connect 后台把测试账号加到沙盒名单里然后用这个账号登录游戏发起的支付不会真实扣款但流程和真实支付完全一样。沙盒环境的订单校验接口也是独立的服务器那边要区分对待。测试的时候重点看三个地方一是 buy 接口有没有正常返回 PurchaseResultInfo二是订单状态是不是 0已购买三是服务器校验接口有没有返回成功。三个都过了才算支付链路通了。任何一个环节断了先看 logcat再看后台的订单记录基本能定位到问题。5. 避坑与排查接入华为 SDK 时最容易翻车的五件事5.1 现象编辑器里正常真机一启动就闪退原因最常见的是 AAR 版本和 Gradle 依赖版本不一致或者 AndroidManifest 里少声明了华为的 provider。另一个高频原因是 Scripting Backend 没切到 IL2CPP或者架构没选 ARM64。解决先看 logcat 的 AndroidRuntime 堆栈如果是 ClassNotFoundException就是 AAR 没打进去或者被裁剪了。检查 Plugins/Android 下的 AAR 是否完整Gradle 依赖是否和 AAR 版本对齐。如果是 Manifest 问题对比华为文档里的 Manifest 示例逐项检查权限和组件声明。5.2 现象登录回调不触发点按钮没反应原因AndroidJavaProxy 的接口全名写错了或者方法签名对不上。华为 SDK 不同版本的回调接口名可能不一样比如有的版本是 AuthResultListener有的版本是 HuaweiIdAuthResultListener。解决把 AAR 解压用 jadx 或 Android Studio 打开找到回调接口的完整类名和方法签名照着写。不要凭记忆或旧文档写版本差异很容易在这里翻车。5.3 现象支付成功但没发货或者重复发货原因客户端拿到购买结果后直接发货没有经过服务器校验。或者服务器校验时没有做幂等处理同一个订单号多次请求都返回成功。解决客户端只负责发起支付和把购买令牌传给服务器发货逻辑全部放在服务器。服务器用购买令牌调华为的校验接口校验通过后先查订单号是否已处理没处理过才发货处理过直接返回成功。这样即使客户端重复提交也不会重复发货。5.4 现象Gradle 打包报 Duplicate class 或 Program type already present原因华为 SDK 依赖的某个库和 Unity 内置的库版本冲突比如 okhttp、gson 这些。或者多个 AAR 之间引用了同一个库的不同版本。解决在 mainTemplate.gradle 里用 exclude 排除冲突的模块或者用 resolutionStrategy 强制指定版本。具体排哪个看依赖树里哪个包出现了两次。这个没有通用答案每次冲突的包可能都不一样。5.5 现象华为设备上正常非华为设备上登录失败原因非华为设备没有预装 HMS CoreHuaweiApiAvailability 返回非 0登录自然失败。解决在 Init 阶段就检查 HMS Core 是否可用不可用就引导用户去应用市场安装或者直接走游客登录兜底。不要假设所有安卓设备都有 HMS Core这个假设在真机上一定会被打脸。6. 进阶技巧把华为 SDK 封装成可替换的渠道层接入华为 SDK 只是第一步真正做发行的时候你不可能只接华为一家。小米、OPPO、vivo、应用宝每家都有自己的 SDK接口设计各不相同。如果每接一家就把游戏逻辑改一遍维护成本会爆炸。我的习惯是在接入第一家的时候就抽象一层渠道接口把登录、支付、退出、上报这几个方法定义好华为 SDK 只是其中一个实现。public interface IChannelSDK { void Init(); void Login(Actionstring onSuccess, Actionint, string onFailure); void Pay(string productId, string orderId, Actionbool onResult); void Logout(); void ReportEvent(string eventName, Dictionarystring, string params); }华为的实现类叫 HuaweiChannel小米的叫 XiaomiChannel游戏逻辑只依赖 IChannelSDK不依赖具体实现。切换渠道的时候只需要换一个实现类游戏代码一行不用改。这个抽象层还有一个好处编辑器里可以写一个 EditorChannel所有方法都返回模拟数据方便在编辑器里跑完整流程。验证渠道层是否抽象干净有一个简单的标准把华为的 AAR 全部删掉游戏还能在编辑器里正常跑只是登录和支付走模拟数据。如果删掉 AAR 就编译不过说明抽象层没做干净游戏逻辑里还有直接引用华为类的地方。我自己的习惯是每接一个新渠道先写一个空的实现类把所有方法都抛 NotImplementedException然后跑一遍游戏看哪些地方会崩。崩的地方就是耦合点一个个改掉直到游戏能完整跑通。这个过程通常要来回好几轮但做完之后后面再接新渠道就是填实现类的事半天就能搞定一个。希望帮到你。本文还有配套的精品资源点击获取