☰
Unity接入华为HMS SDK实战:从登录翻车到真机跑通
2026/11/2 11:23:32 网站建设 项目流程

简介:这份资源是面向Unity开发者与手游客户端同学的华为HMS SDK接入示例工程,聚焦在Unity项目中集成华为账号、游戏服务与推送等能力,解决跨平台引擎对接华为生态时的配置与调用问题,适合已具备一定Unity与Android基础、需要落地真机验证的中级开发者参考。压缩包为rar格式,共约2000个文件,整体28.82MB,其中bin、info、class、java、xml等Android构建与清单文件占主体,另有cs脚本、dll动态库、jar包、meta与asset等Unity工程资源,以及png、gif等界面素材和log日志,完整保留了示例工程与打包产物。资源已有1843人学习下载,热度较为稳定。读者可从中获得可直接运行的HuaweiSdkDemo工程结构、IL2CPP脚本后端配置参考、SDK初始化与登录接口的调用范例,以及权限声明和打包测试所需的配套文件,便于对照排查版本兼容与集成报错,快速搭建自己的接入验证环境。

1. 从一次登录翻车说起:这个 Unity 接华为 SDK 的 Demo 到底解决什么问题

去年帮一个做休闲竞技的小团队看包,Unity 工程在编辑器里跑得好好的,一打真机就卡在登录页转圈,日志里全是鉴权失败。问题不在业务代码,而在华为渠道 SDK 的接入层——初始化时机、证书指纹、回调线程,任何一处对不上都会让整个登录链路静默失败。这类问题在 Unity 接华为 HMS SDK 的场景里非常典型:编辑器环境和真机环境是两套逻辑,模拟器和华为设备又是两套逻辑。这个 Demo 的价值就在这儿,它把 Unity 工程接入华为 SDK 的最小可运行链路完整摆出来,包括初始化、登录、支付回调、生命周期桥接这几块最容易翻车的地方。适合两类人:一是第一次给 Unity 项目接渠道 SDK、被文档绕晕的新手;二是接过别的渠道、想快速对照华为这套差异点的熟手。下面按「能干什么 → 怎么跑 → 怎么改」的顺序拆。

2. 工程结构与接入架构:Unity 侧和原生侧怎么分工

2.1 目录分层与职责边界

这个 Demo 的工程结构是典型的「Unity 主工程 + 原生插件」双轨制。Unity 侧负责游戏逻辑和 UI,华为 SDK 的能力全部沉到 Android 原生层,中间靠一层 C# 封装做桥接。目录大致是这样组织的:

Assets/ Plugins/ Android/ hmssdk.aar # 华为 SDK 核心库 hmsbridge.aar # 自写的桥接层,暴露给 Unity 的接口 AndroidManifest.xml # 权限、Activity、meta-data 配置 Scripts/ HMS/ HMSManager.cs # 单例入口,初始化/登录/支付 HMSLogin.cs # 登录回调封装 HMSPay.cs # 支付回调封装 HMSLifecycle.cs # 生命周期转发 UI/ LoginPanel.cs # 登录界面逻辑 ProjectSettings/ Android/ # 包名、签名、minSdk 等

关键点在于hmsbridge.aar这一层。华为官方 SDK 是纯 Android 的 Java 接口,Unity 不能直接调,必须有一个中间层把 Java 方法用AndroidJavaObject/AndroidJavaProxy暴露出来。这个 Demo 把桥接层单独打成一个 aar,好处是升级华为 SDK 时只动hmssdk.aar,桥接代码不用重编。我一般也会这么拆,血泪经验是:桥接层和业务层混在一起,后面换渠道版本会非常痛苦。

2.2 初始化链路:谁先谁后决定成败

华为 SDK 的初始化有严格的时序要求,必须在Application或首个Activity的onCreate阶段完成,晚于这个时机就会出现「初始化成功但登录拿不到 token」的玄学现象。Demo 里的做法是在 Unity 的Awake阶段通过桥接层触发原生初始化:

// HMSManager.cs using UnityEngine; public class HMSManager : MonoBehaviour { private static HMSManager _instance; private AndroidJavaObject _bridge; public static HMSManager Instance { get { if (_instance == null) { var go = new GameObject("HMSManager"); _instance = go.AddComponent<HMSManager>(); DontDestroyOnLoad(go); } return _instance; } } void Awake() { #if UNITY_ANDROID && !UNITY_EDITOR // 拿到桥接层 Java 类,包名要和 aar 里一致 _bridge = new AndroidJavaObject("com.demo.hms.HMSBridge"); // 传入当前 Activity,华为 SDK 很多接口需要 Context using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) using (var activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity")) { _bridge.Call("init", activity); } #endif } }

这段代码有三个参数值得说清楚。com.demo.hms.HMSBridge是桥接 aar 里的入口类全限定名,改包名时这里必须同步改,否则AndroidJavaObject构造直接抛异常。currentActivity是 Unity 播放器的主 Activity,华为 SDK 的HMSInstance.getInstance(activity)依赖它,传 null 会初始化失败。#if UNITY_ANDROID && !UNITY_EDITOR这个条件编译不能省,编辑器里没有 Android 运行时,不排除会直接报ClassNotFoundException。初始化完成后,桥接层通过UnitySendMessage回调通知 C# 侧,这个回调名要和 GameObject 名字对上,对不上就是静默失败,日志里什么都看不到。

2.3 登录与支付:回调怎么从 Java 回到 C#

登录和支付都是异步的,华为 SDK 在原生层回调结果,桥接层再把结果转回 Unity。Demo 用的是UnitySendMessage方案,简单直接:

// HMSBridge.java 片段 public void login() { HMSInstance.getInstance(activity).getHmsMessageService() .signIn(new SignInOptions.Builder() .setSignInType(SignInType.ID) .build()) .addOnSuccessListener(result -> { // 把 AuthAccount 的 openId 和 token 拼成字符串回传 String payload = result.getAuthAccount().getOpenId() + "|" + result.getAuthAccount().getAccessToken(); UnityPlayer.UnitySendMessage("HMSManager", "OnLoginSuccess", payload); }) .addOnFailureListener(e -> { UnityPlayer.UnitySendMessage("HMSManager", "OnLoginFail", e.getMessage()); }); }

C# 侧对应两个方法,方法名必须和 Java 里UnitySendMessage的第二个参数完全一致:

// HMSLogin.cs public void OnLoginSuccess(string payload) { var parts = payload.Split('|'); string openId = parts[0]; string token = parts[1]; // 交给业务层做账号绑定 LoginPanel.Instance.OnLoginResult(true, openId, token); } public void OnLoginFail(string error) { Debug.LogError($"[HMS] login failed: {error}"); LoginPanel.Instance.OnLoginResult(false, null, null); }

参数说明:payload用|分隔是图省事,正式项目建议用 JSON,避免 token 里出现分隔符导致解析错位。OnLoginSuccess和OnLoginFail这两个方法名是桥接层硬编码的,改名字要两边一起改。支付回调同理,只是多一个订单号透传,Demo 里HMSPay.cs的结构和登录几乎一样,这里不重复贴。要注意的是回调线程:UnitySendMessage会把调用投递到 Unity 主线程,所以 C# 侧可以直接操作 UI,不用再Loom或SynchronizationContext转一道,这点比自建 JNI 回调省心。

3. 跑起来:从导入工程到真机登录成功的完整步骤

3.1 环境准备与工程导入

先把 Unity 版本对齐,这个 Demo 用的是 Unity 2021 LTS 系列,Android 构建支持模块要装全。华为 SDK 对minSdkVersion有要求,Demo 里设的是 21,低于这个值构建阶段就会报错。导入工程后先别急着点运行,按顺序检查三样东西:ProjectSettings/Android里的包名、签名文件、以及AndroidManifest.xml里的权限声明。包名必须和你在华为开发者后台注册的应用包名完全一致,差一个字符鉴权就过不去。

签名这块是新手最容易翻车的地方。华为 SDK 校验的是 APK 的签名指纹,不是调试签名的默认值。你需要用自己的 keystore 打包,然后把签名指纹填到后台应用配置里。Demo 的ProjectSettings里留了签名配置的占位,把 keystore 路径、alias、密码填进去,构建时 Unity 会自动签名。用默认调试签名打出来的包,登录一定失败,而且失败日志很含糊,只会告诉你鉴权异常,不会直接说签名不对。

3.2 华为后台配置与参数回填

登录华为开发者后台,在应用管理里创建应用,拿到AppId和ClientId,这两个值要回填到原生层的初始化配置里。Demo 里放在hmsbridge的HMSBridge.java常量区:

// HMSBridge.java 顶部常量 private static final String APP_ID = "你的AppId"; private static final String CLIENT_ID = "你的ClientId"; private static final String CERT_FINGERPRINT = "你的签名SHA256";

CERT_FINGERPRINT是签名证书的 SHA256 指纹,用keytool -list -v -keystore your.keystore可以拿到。三个值任何一个填错,表现都是登录回调失败,但错误码不同:AppId 错通常是参数校验失败,指纹错是鉴权失败,ClientId 错是应用未授权。排查时先看错误码再定位,别一上来就怀疑代码。

3.3 构建、安装与首次登录验证

构建前确认Build Settings里平台切到 Android,Player Settings里Scripting Backend选 IL2CPP,Target Architectures勾上 ARM64。华为新设备基本都是 ARM64,只打 ARMv7 的包装上去可能直接闪退。构建出 APK 后用adb install装到真机,模拟器不行,华为 SDK 的很多能力依赖设备服务,模拟器上初始化就会失败。

首次启动看 logcat,过滤HMS和Unity两个 tag。正常流程的日志顺序是:初始化成功 → 登录请求发出 → 登录回调成功 → 拿到 openId。如果卡在第二步,检查网络和后台配置;卡在第三步,检查签名指纹;拿到 openId 但业务层没反应,检查UnitySendMessage的 GameObject 名字对不对。这个顺序排查法比盲目看代码快得多。

4. 避坑与排查:五个真机上踩过的坑

4.1 编辑器里能跑、真机初始化失败

现象是编辑器里一切正常,真机启动后日志里没有任何华为 SDK 的输出。原因通常是条件编译没写对,#if UNITY_ANDROID && !UNITY_EDITOR只排除了编辑器,但有些开发者会写成#if UNITY_ANDROID,导致编辑器里也去构造AndroidJavaObject,异常被吞掉后真机逻辑也没走到。解决是把初始化代码严格包在平台判断里,并且在Awake里加一行Debug.Log确认分支真的进了。

4.2 登录回调收不到,日志一片空白

现象是点了登录按钮,原生层日志显示请求已发出,但 C# 侧的回调方法始终不触发。原因是UnitySendMessage的第一个参数是 GameObject 名字,必须和场景里挂HMSManager的那个对象名完全一致。Demo 里用new GameObject("HMSManager")动态创建,名字是固定的;如果你改成场景里手动挂,对象名可能带空格或后缀,就对不上了。解决是统一用代码动态创建,或者把名字抽成常量两边引用。

4.3 支付成功但订单状态没同步

现象是华为支付页面显示支付成功,回到游戏后订单还是待支付。原因是支付回调里只处理了onSuccess,没处理「支付成功但服务端确认延迟」的情况。华为的支付回调只代表客户端支付动作完成,最终订单状态要以服务端校验为准。解决是客户端收到成功回调后先置为「确认中」,再调自己的服务端接口查单,查到已支付才改状态。这个坑不接华为也会遇到,但华为的回调时机偏早,更容易暴露。

4.4 打包报错 duplicate class

现象是构建阶段报Duplicate class或Program type already present。原因是hmssdk.aar里已经带了某些基础库,而 Unity 工程或别的插件又引入了一份。解决是用 Gradle 的exclude排除重复依赖,或者在 Unity 的mainTemplate.gradle里加packagingOptions。这个坑在接多个渠道 SDK 时几乎必现,建议一开始就把依赖树理清楚。

4.5 生命周期没转发导致登录态丢失

现象是游戏切后台再回来,登录态没了,要重新登录。原因是 Unity 的 Activity 生命周期没有转发给华为 SDK,SDK 认为会话已失效。Demo 里HMSLifecycle.cs做了OnApplicationPause的转发,但很多人会漏掉OnApplicationFocus。解决是在两个回调里都调桥接层的onPause/onResume,确保原生层和 Unity 层状态一致。

5. 二次开发:把 Demo 改成能进生产项目的接入层

Demo 跑通只是起点,真正要进项目还得做几件事。第一件是把桥接层的回调协议从字符串分隔改成 JSON,|分隔在 token 含特殊字符时必炸,我一般用JsonUtility在 C# 侧解析,Java 侧用org.json拼,两边都轻量。第二件是把HMSManager的单例改成接口注入,登录、支付、推送各自实现一个IHMSModule,方便后面接别的渠道时做统一抽象。第三件是补错误码映射表,华为的错误码是数字,直接抛给业务层没法用,得映射成「网络错误 / 配置错误 / 用户取消」这类语义化枚举。

验证改造是否成功,我习惯用一张最小回归清单:冷启动登录、切后台再回来、支付成功、支付取消、断网登录,五个场景全过才算接入层稳定。这张清单比任何单元测试都直接,因为渠道 SDK 的问题基本都出在时序和状态上,不是逻辑分支。

改造项Demo 现状生产建议
回调协议字符串|分隔JSON,字段可扩展
模块组织单例直调接口 + 多实现
错误处理直接打日志错误码映射枚举
生命周期部分转发Pause/Focus 全转发
配置管理常量硬编码构建时注入或远程配置

最后说个习惯。从那以后我每次接新渠道 SDK,都强制先只跑「初始化 + 登录」这一条链路,跑通了再往上叠支付和推送,绝不一次性全接完再调。因为渠道 SDK 的失败往往是静默的,一次只动一个变量,出问题才知道是哪一步。希望这个 Demo 的拆解能帮你少走几趟真机调试的弯路,把时间花在业务上而不是和回调搏斗。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询