8.总结
8.1 核心要点速览
SDK 是什么
- 全称:Software Development Kit,常称「软件开发工具包」:面向某一平台或框架,把库、工具、文档打在一起,用来在该平台上做应用开发。
- 和游戏开发的关系:你不会自己去实现微信登录协议栈或地图引擎;厂商把能力封进 SDK,你按文档接 初始化、权限、回调,业务侧只关心「什么时候调、数据从哪来」。
为什么要接入 SDK
- 登录、支付、统计、渠道与平台能力等,多数由第三方提供;客户端要启用这些能力,就要接入对应 SDK。
- 典型场景:渠道要求 QQ / 微信登录——包内必须带对方提供的登录能力,用 SDK 接入比自研对接成本低几个数量级。
学习接入时的三条主线
| 主线 | 要搞清的事 | 落地时 |
|---|---|---|
| Unity ↔ Android 互调 | C# 怎么拿到 Activity、Java 怎么 UnitySendMessage |
多数流程是:原生里初始化 SDK,Unity 只调封装好的 public 方法 |
| 读文档 | API 顺序、Manifest、Gradle、混淆规则 | 以 当前版本 官方文档为准,旧博客只能当线索 |
| 打包验证 | 依赖是否进最终 APK | 集成第三方库后,用 真机 + 与线上一致的签名 跑一遍 |
在项目周期里的位置
- 一般放在版本中后期:包名、签名、渠道参数相对稳定后再接,少返工。前期若频繁改包名,地图、支付类控制台里的 包名 + SHA1 也要跟着改。
交互工程在整条链路里的位置
- 只在 Android 工程里初始化原生 SDK、再封装给 Unity 时,才需要单独的 Android 交互工程:里面引 jar/aar、写
MainActivity桥接,最后 Make Module 出 aar 给 Unity。 - 本系列示例:百度定位用 jar,Java 侧建
LocationClient、拼地址字符串;Unity 侧只Call("GetCurrentPosition")并接收字符串——交互工程把「百度那套 API」挡在 Android 层。 - Unity 先切 Android,Application Id、Minimum API Level 与 Android Studio 工程对齐,否则清单合并、签名、控制台登记会对不上。
Android Studio 工程初始化
- 新建 Empty Activity,设备类型选 Phone and Tablet。
- 包名、Minimum SDK 必须与 Unity Player Settings 中一致;若本机没有对应 API 级别,在 Android Studio 的 SDK 管理里补装。
- 工程首次同步完成后,按教程清理:删除两个 Test 相关工程,以及
res下暂时用不到的资源,减少干扰。
从「可运行 App」改成「给 Unity 用的库」
| 环节 | 操作 | 说明 |
|---|---|---|
| Gradle 插件类型 | com.android.application 改为 com.android.library |
模块以 库 形式参与打包,而不是独立应用 |
| 应用 ID | 删除 defaultConfig 里的 applicationId |
library 模块不写 applicationId,由最终 Unity 出包统一决定包名 |
| 同步工程 | 点击 Sync Now | 让依赖与模块类型生效 |
classes.jar、UnityPlayerActivity、入口 Activity
- classes.jar:从 Unity 安装目录
Data\PlaybackEngines\AndroidPlayer\Variations\mono或il2cpp下的Release\Classes拷贝到app\libs,再 Add As Library。mono 与 il2cpp 两条路径对应不同脚本后端,与当前 Unity 工程设置一致即可。 - UnityPlayerActivity 源码:从
Data\PlaybackEngines\AndroidPlayer\Source\下拷贝到app\src\main\java,与 Unity 提供的活动基类对齐。 - MainActivity:改为继承
UnityPlayerActivity;在onCreate里注释掉setContentView,避免用原生布局盖住 Unity 的渲染视图。
AndroidManifest 与产出 aar
- 清理
application节点里与 Unity 无关的多余声明,避免清单合并冲突。 - 在作为 Unity 入口的
activity内增加:meta-data名unityplayer.UnityActivity,值true,标明该 Activity 由 Unity 驱动。 - 使用 Build → Make Module 生成 aar。若首次构建提示正在下载 Gradle 或依赖,等下载完成后再试,一般即可成功。
第三方 SDK 接入的通用套路
- 文档从 合作方包体 或 官网 拿;按文档配 Gradle / 权限 / Manifest / 初始化顺序。
- 报错时先看 Gradle 同步 和 Logcat / 构建日志,再对照文档缺啥补啥。
- 要不要交互工程:官方出 Unity 包(.unitypackage 等)时,往往直接在 Editor 里接;只有 Android / iOS 原生 SDK 时,才像本系列这样在 AS / Xcode 里包一层再给 Unity。支付宝、微信、百度地图等常见能力两条路径都有,以文档为准。
获取 SDK 与开发者账号(百度定位示例)
- 开放平台进入 Android 定位 SDK 下载页,按功能选包;需要轻量接入时可下 jar。
- 注册开发者账号(个人 / 企业按平台要求选),完成邮箱、实名等验证后才能创建应用、拿密钥。
SHA1 指纹与 AK
- 控制台要把 包名 + 签名指纹 绑在一起,有点像服务端 IP 白名单:只有「这个包名、且用这个证书签出来的包」才能用你的 AK。指纹常用 SHA1,以平台当前要求为准。
- 页面取指纹失败时,用 keytool;可执行文件在 Android Studio 目录的 **
jbr\bin**(新版)或 **jre\bin**(旧版)。 - 提交 SHA1 后拿到 AK,再写进 Manifest 或初始化代码(百度各版本写法看文档)。
解压、放入 libs 与文档驱动配置
| 步骤 | 内容 |
|---|---|
| 解压与拷贝 | 解压下载包,将 jar 等放入 app/libs |
| 依赖 | 按官方文档在 build.gradle 中 implementation fileTree、files 或远程仓库坐标,以当前文档为准 |
| AK | 在 AndroidManifest 或文档指定位置填入 AK |
| 权限 | 按文档补充 定位等权限 与合并规则 |
具体改法以 当前版官方接入文档 为准:jar 拷进 libs 后若没配 implementation,构建阶段仍可能找不到类。
百度定位:Android 侧初始化与回调
- 隐私:
LocationClient.setAgreePrivacy(true)必须在new LocationClient(...)之前;否则按文档逻辑定位直接不工作。 - 监听:
MyLocationListener继承BDAbstractLocationListener,在onReceiveLocation(BDLocation)里取经纬度、精度、地址等;别在回调里做耗时阻塞。 - 入口方法:
MainActivity里写GetCurrentPosition():new LocationClient→registerLocationListener→ 配LocationClientOption→start();官方示例用 try-catch 包住初始化,异常时能在 log 里看到栈,而不是只闪退。 - 回传:
UnityPlayer.UnitySendMessage("Lesson6", "GetCurrentPosition", message)里,Lesson6 是场景里 GameObject 的名字(不是脚本类名);GetCurrentPosition 对应 C# 里public void GetCurrentPosition(string info)。改名字时三处一起改:Hierarchy 名、Java 字符串、脚本所在物体。
Unity 侧:Java 调用与字符串回传
| 方向 | 写法 | 易错点 |
|---|---|---|
| C# → Java | AndroidJavaClass("com.unity3d.player.UnityPlayer") → GetStatic<AndroidJavaObject>("currentActivity") → Call("GetCurrentPosition") |
Call 的方法名、参数类型与 Java public 方法一致;混淆要 keep |
| Java → C# | UnityPlayer.UnitySendMessage(物体名, 方法名, 字符串) |
物体名与 Hierarchy 完全一致(大小写敏感);方法必须是带 一个 string 的 public 实例方法 |
| UI | 示例里 info.Contains("null") 时显示「定位中」,否则显示整段文本 |
字符串协议是业务约定,和百度返回字段强相关 |
aar 回灌与出包策略
- aar 放到 Unity 的
Assets/Plugins/Android(或你项目里统一放 Android 插件的目录),再在 Unity 里打 Android 包。 - Unity 自带 Gradle 也能出 APK,但 多个 aar/jar 叠在一起 时,传递依赖、重复类、Manifest 合并问题在 Android Studio 里看依赖树、跑 assemble 更直观。所以:日常迭代可以 Unity 一键出包;发版前用 AS 收一遍依赖更稳,属于工程上的保险,不是否定 Unity Build。
8.2 面试题精选
基础题
1. 什么是 SDK?它和「调几个 HTTP 接口」有什么本质区别?
题目
说明 SDK 交付物一般包含什么;游戏项目里哪些能力更适合通过 SDK 接入而不是自研或纯接口对接?
深入解析
- SDK 通常包含 原生库 / jar、aar、资源、Manifest 片段、混淆 consumer 规则 等,是「可链接进工程」的一整套交付,而不是只有 REST 文档。
- 纯接口适合无客户端证书、无复杂原生能力的场景;登录、支付、地图定位、推送 等往往涉及 签名、密钥、系统权限、前后台保活,厂商用 SDK 把复杂度收进库里。
- 自研部分侧重 玩法、战斗、资源管线;第三方能力侧重 合规、风控、渠道对接成本。
答题示例
SDK 是一整套库和配置,而不只是几个 URL。接进来要在工程里加依赖、改清单、按生命周期初始化,有时还要过应用商店和隐私合规。
支付、渠道登录、地图这类,平台要求用他们的客户端能力和密钥体系,自研对接成本和审核风险都高,一般用官方 SDK。我们写玩法和表现,不把支付协议从零实现一遍。
参考文章
- 1.SDK是什么
2. 接入第三方 SDK 前,你会做哪些工程向准备?(排期与验证)
题目
结合 Unity + Android,说明文档、构建、签名与联调上至少要覆盖什么。
深入解析
- 互调与生命周期:弄清 SDK 在哪个线程回调、是否必须在
Activity内初始化(本系列在MainActivity封装百度定位)。 - 文档版本:锁定 SDK 版本号、最低 Android 版本、Gradle 插件 要求,避免「教程是旧的、工程是新的」。
- 构建验证:用 与线上一致的 keystore 打一次包;必要时 Export Gradle Project 到 Android Studio 看 依赖树 和 Manifest 合并结果,只在 Editor 里 Play 测不到这些。
- 控制台:地图类提前准备 包名 + SHA1 占位,避免临上线才登记。
答题示例
先读 release note 和混淆说明,确认 minSdk 和 Gradle 要求。排期里留联调时间,因为权限和签名一错就是整包失败。
集成后在真机上用发布签名打 APK,看初始化是否成功。复杂依赖可以导出 Gradle 在 AS 里看一眼依赖树,比在 Unity 控制台里猜传递依赖快。
参考文章
- 1.SDK是什么
3. AndroidJavaClass / AndroidJavaObject 与 currentActivity 是什么关系?(JNI 封装层)
题目
说明从 C# 调用 MainActivity 上实例方法的典型代码路径;UnityPlayer.currentActivity 指向谁?
深入解析
- Unity 在 Android 上通过 JNI 调 Java;
AndroidJavaClass、AndroidJavaObject属于 高级封装,避免手写AndroidJNI。 com.unity3d.player.UnityPlayer的静态字段currentActivity是当前承载游戏的 Activity,本系列里即继承UnityPlayerActivity的 **MainActivity**。GetStatic<AndroidJavaObject>("currentActivity")拿到该实例后,**Call("GetCurrentPosition")** 调的是 Java 侧 public 方法;方法名、参数类型需与 ProGuard 保留规则 一致,否则 Release 会找不到符号。
答题示例
AndroidJavaClass用来拿 Java 类,GetStatic取UnityPlayer上的currentActivity,类型是AndroidJavaObject,对应正在跑游戏的那个 Activity。对它
Call就是在调 Java 实例方法。我们项目里MainActivity继承UnityPlayerActivity,上面挂了GetCurrentPosition,所以从 C# 能直接 Call 过去。
参考文章
- 6.Unity调用对应百度定位API
4. UnitySendMessage 的限制有哪些?收不到回调时你怎么查?
题目
说明三个参数含义;除 GameObject 名写错外,还有哪些常见坑?
深入解析
- 签名:
UnityPlayer.UnitySendMessage(gameObjectName, methodName, message),payload 只能是string,无返回值,适合把定位结果拼成一段文本回传(本系列做法)。 - gameObjectName 必须与 Hierarchy 名称完全一致(大小写敏感);不是脚本类名。
- methodName 对应 public、恰好一个
string参数 的实例方法;多脚本挂在同一物体上时,需保证 方法名不歧义(Unity 按消息派发规则查找)。 - 进阶追问:高频回调或要强类型接口时,团队会评估
AndroidJavaProxy等方案;本系列正文采用UnitySendMessage,实现成本最低。
答题示例
三个参数是:场景里物体名、方法名、字符串参数。接收方法要写
public void xxx(string s)。收不到先看 Hierarchy 名字和 Java 里字符串是不是逐字一致,再看方法是不是 public、参数是不是只有一个 string。物体被禁用或场景没加载时也会调不到。
参考文章
- 6.Unity调用对应百度定位API
进阶题
1. 只有原生 Android SDK、没有 Unity 插件时,一般有哪几种集成路径?各适合什么场景?
题目
对比「Plugins/Android 直放 aar」「Unity 导出 Gradle 再在 AS 里集成」「先建 Android Library 出 aar 再给 Unity」。
深入解析
- 官方提供 Unity 包:优先 Plugins/Android 或 Package 方式接入,迭代快。
- 只有原生文档:常见两条路——导出 Gradle,在 AS 里按原生文档改
build.gradle和 Manifest;或像本系列一样 先建 library 模块,把 SDK 和桥接代码封进 aar,Unity 只引用 aar,业务边界清晰。 - 选型看 谁维护原生工程、是否要 多项目复用同一套 aar、CI 是否要求 可脚本化的 AS 构建。
答题示例
有 Unity 插件就按官方放 Plugins 里。只有 jar 文档时,要么导出工程在 AS 里硬集成,要么单独建 Android 工程打成 aar,Unity 当插件引用。
我们这条线是先 AS 里接百度 SDK 和桥接,再 Make Module 出 aar,Unity 只调封装好的方法,原生细节不污染 C#。
参考文章
- 3.了解SDK接入套路
2. 把 Application 模块改成给 Unity 用的 Android Library,Gradle 与入口类通常要动哪些点?
题目
说明 com.android.application 与 applicationId 的处理;classes.jar、UnityPlayerActivity、meta-data 各自解决什么问题。
深入解析
- **
com.android.library+ 去掉applicationId**:库模块由最终 Unity 出包 统一决定 applicationId,避免与宿主 App 模型冲突。 - **
classes.jar**:让 Java 侧能引用 Unity 引擎侧 API(如UnityPlayer);放入libs并参与 implementation。 - **继承
UnityPlayerActivity**:生命周期与 GL 视图 由 Unity 接管;注释 **setContentView**,避免原生布局盖住游戏画面。 - Manifest:
unityplayer.UnityActivity的 meta-data 声明该 Activity 由 Unity 驱动,合并进最终 APK 时主工程能识别。
答题示例
application 改成 library,删掉 applicationId,同步 Gradle。classes.jar 加进依赖,MainActivity 继承 UnityPlayerActivity,onCreate 不要 setContentView。
清单里给主 Activity 加上 Unity 的 meta-data。最后 Make Module 打出 aar。
参考文章
- 2.交互项目创建
3. 地图 / 定位开放平台为什么要登记包名和 SHA1?填错会怎样?
题目
从「密钥与盗用风险」角度说明平台侧逻辑;同一包名 Debug 与 Release 为何可能都要配。
深入解析
- AK 与客户端绑定:包名 限制进程命名空间,SHA1 限制「谁签的名」;二者一起降低 密钥被其他应用盗用 的风险。
- Debug / Release 使用 不同 keystore 时 SHA1 不同;控制台只配 Debug 时,上架包会 鉴权失败。
- 现象多为 错误码、初始化失败、有权限仍无定位;用
keytool -list -v对 实际安装包所用证书 核对。
答题示例
平台要把 AK 和你的应用身份绑死:包名对上,还要证明这个包是你用指定证书签的,所以登记 SHA1。
Debug 和 Release 证书不一样就要各配一套指纹,否则线上包过不了校验。
参考文章
- 4.获取SDK完成准备工作
4. Release 开启 R8/混淆后,Call 调不到 Java 或回调异常,优先怀疑什么?
题目
说明 JNI 反射与混淆的关系;MainActivity 上给 Unity 调的方法、第三方 SDK 包名在 proguard-rules 里通常怎么处理?
深入解析
- R8 会裁剪并重命名未保留的 public API;C# 侧用 字符串 调
Call("方法名")时,若 Java 方法被改名或类被删掉,运行期会 NoSuchMethodError 或静默失败。 - 工程上需
-keep住桥接类:如MainActivity及供 Unity 调用的 public 方法;第三方 SDK 按 官方文档 追加 consumer rules 或手动 **-keep class com.thirdparty.** { *; }**(以文档为准)。 - 与 UnitySendMessage 同理:接收方法若被 混淆成短名,字符串对不上也会收不到。
答题示例
混淆会把方法名改掉,而 Unity 这边写的是字符串方法名,对不上就调不到。要在 proguard 里 keep 住给 Unity 调的那几个 public 方法,第三方库按厂商给的规则加。
出问题先看 mapping.txt 里方法有没有被改掉,再对照 Call 里的字符串。
参考文章
- 2.交互项目创建
- 6.Unity调用对应百度定位API
深度题
1. Debug 定位正常,Release 鉴权失败;已确认 SHA1 无误时,还可能是什么?
题目
在 签名、控制台、混淆 三条线上扩展排查;本系列桥接代码要注意什么?
深入解析
- 签名线:仍用
keytool核对 实际安装包 证书,排除 多渠道重签名、Google Play App Signing 与本地证书不一致。 - 控制台线:包名大小写、是否登记了 多个指纹、是否限制了 包名后缀。
- 混淆线:AK 是否在 Manifest / meta-data 被 manifestPlaceholders 或 buildType 覆盖错误;**
LocationClient初始化** 是否被 R8 剔除(按百度文档 keep)。 - 桥接线:**
setAgreePrivacy** 是否在LocationClient构造之前;Release 若 日志关闭,需用 Logcat + 非脱敏日志 区分「鉴权失败」与「隐私未同意」。
答题示例
SHA1 对齐仍失败,我会再看控制台包名是否完全一致,以及是不是上架包走了另一套签名。
再查混淆:桥接 Activity 和 SDK 包名有没有按文档 keep。百度这边隐私接口顺序错了 Release 上也会直接不定位,和 SHA1 无关,要对照官方初始化顺序。
参考文章
- 4.获取SDK完成准备工作
- 5.配置修改和权限修改
- 6.Unity调用对应百度定位API
2. 多个第三方 aar/jar 叠在一起时,Unity Build 与 Android Studio 各自擅长解决什么问题?
题目
追问 Duplicate class、传递依赖、Manifest merger 冲突时,你在哪一侧排查?与「插件已放进 Unity」是否矛盾?
深入解析
- Unity 导出 Gradle 时会把 Plugins/Android 下插件 合并进模板;多厂商 SDK 常见 同一传递依赖不同版本、support 与 AndroidX 混用、同名类打进多份,报错
Duplicate class或 dex 冲突。 - Android Studio 里可看 Gradle 依赖树、用
./gradlew :module:dependencies、对冲突包做exclude或 强制版本;Manifest merger 可在 AS 里看 合并报告。 - 不矛盾:Unity 负责 产出可运行的导出工程;AS 负责 在完整 Android 工程语境下收敛依赖。实践上 日常用 Unity 出包迭代,发版前用 AS 打 release 或至少同步一次依赖,与本系列 优先 AS 打包 的建议一致。
答题示例
多 aar 最容易撞重复类和传递依赖版本。Unity 日志有时只报 dex 或合并失败,不好一眼看出是哪两个库冲突。
导出到 AS 里打开依赖树,看哪两个库引入了同名的不同版本,按文档 exclude 或统一版本。Manifest 冲突也在 AS 的 merger 报告里更直观。Unity 只管把插件放进去,最终 Gradle 能不能收干净,AS 更好查。
参考文章
- 7.打包测试
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com