Unity SDK接入原理
SDK(登录、支付、广告、统计、推送)大多是原生平台SDK:Android用Java/Kotlin,iOS用OC/Swift。 Unity C#不能直接调用系统原生API,必须通过插件桥接层完成 C# ↔ 原生代码互相调用。
整体分层
- 上层:C#业务层:游戏业务逻辑,统一对外封装SDK接口
- 桥接层:Unity插件机制,C#与Java/OC跨语言通信
- 底层:原生SDK:Android AAR/JAR/.so、iOS Framework/.a,真正实现登录/支付等功能
⚠️编辑器下无法运行移动端SDK,只能真机打包调试。
一、目录与插件机制
Unity识别特殊文件夹Plugins,做平台隔离编译
Assets/
└── Plugins/
├── Android/ # 仅Android打包生效
│ ├── xxx.aar # Android库,包含java、资源、Manifest、so库
│ ├── xxx.jar
│ └── AndroidManifest.xml
└── iOS/ # 仅iOS打包生效
├── xxx.framework
└── libxxx.a
选中插件文件,Inspector勾选对应平台,打包时Unity自动把这些原生库打进APK/IPA包中。
二、双向通信原理
Android端(C# ↔ Java/Kotlin)
- C#调用Java
使用
AndroidJavaClass、AndroidJavaObject,底层基于JNI跨虚拟机调用;需要拿到Unity的currentActivity上下文,很多SDK初始化必须Activity对象。
#if UNITY_ANDROID
//拿到UnityPlayerActivity
AndroidJavaClass unityPlayerCls = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject activity = unityPlayerCls.GetStatic<AndroidJavaObject>("currentActivity");
//调用SDK静态/实例方法
activity.Call("showToast","测试");
#endif
- Java回调C#
UnityPlayer.UnitySendMessage("对象名","方法名","字符串参数")- 限制:目标GameObject名字必须固定;回调函数签名只能是
void Func(string msg);只能传字符串;异步延迟一帧;子线程调用容易丢失消息或崩溃。
- 限制:目标GameObject名字必须固定;回调函数签名只能是
- 高级:
AndroidJavaProxy接口代理,可以传递复杂参数。
Java层注意:SDK回调经常跑在子线程,必须切回Android UI线程
runOnUiThread,再执行UnitySendMessage,否则异常崩溃。
iOS端(C# ↔ Objective‑C)
- C#调用OC
C#使用
[DllImport("__Internal")]声明extern C函数,直接链接原生C符号;OC代码包装一层C导出函数供C#调用。
#if UNITY_IOS
[DllImport("__Internal")]
private static extern void SDK_Init(string appId);
#endif
- OC回调C#
同样使用C函数
UnitySendMessage("GameObject","Func","json")。>
⚠️iOS原生SDK回调经常在GCD后台队列,必须手动Dispatch切换到主线程再调用UnitySendMessage,否则直接崩溃。
重要:跨平台条件编译
#if UNITY_ANDROID
//安卓逻辑
#elif UNITY_IOS
//iOS逻辑
#else
//编辑器/PC占位,防止编辑器报错
#endif
三、SDK接入标准工作流
- 下载第三方SDK,拿到Android(aar/jar/so)、iOS(framework/a)原生库。
- 将原生库放入Plugins对应目录,配置插件导入设置。
- Manifest / Info.plist配置:Android权限、Activity;iOS权限、白名单、LSApplicationQueriesSchemes。
- C#封装统一SDK对外接口,隔离Android/iOS平台差异。
- 写PostProcessBuild打包后脚本(Editor脚本)
- Android:修改Gradle依赖,处理冲突;使用EDM4U自动解析依赖。
- iOS:自动修改XCode工程,添加Framework、设置编译参数,避免每次手动改Xcode。
- 游戏启动初始化SDK;做生命周期处理
OnPause/OnResume。 - 真机测试:初始化、登录、支付回调、App前后台切换、杀进程重启。
EDM4U:Google的Unity包,自动解析Android Gradle依赖,解决多个SDK之间依赖版本冲突。
四、SDK封装架构最佳实践
业务层不直接写
#if UNITY_ANDROID到处散落在业务代码。
SDKManager(业务统一调用层,无平台分支)
└── ISDKApi 接口定义(Init、Login、Pay、Share)
├── AndroidSDKImpl #if UNITY_ANDROID
├── IosSDKImpl #if UNITY_IOS
└── DummySDKImpl #编辑器PC空实现,编辑器运行不报错
业务只调用SDKManager.Instance.Login(),底层自动走不同平台实现。
回调全部用C#事件Action<string>,不要到处写UnitySendMessage硬编码。
五、高频踩坑(非常重要)
- 线程问题(最多崩溃来源)>
Android/iOS SDK回调经常跑在子线程!
- 原生子线程不能直接操作原生UI;
- 原生子线程直接调用
UnitySendMessage会消息丢失、随机崩溃。 ✅解决方案:原生层把回调调度回平台主线程,再通知Unity。
- UnitySendMessage缺点 只能传字符串;GameObject名字不能改;不能传复杂对象。建议全部用JSON序列化传递数据。
- AndroidManifest冲突
多个SDK各自带Manifest,合并冲突;需要自定义Custom Main Manifest。
SDK的Activity必须继承
UnityPlayerActivity,否则会黑屏闪退。 - IL2CPP托管代码剥离(Stripping)
Release打包IL2CPP会裁剪未被直接引用的C#代码;SDK回调、反射用到的类被删掉,导致线上闪退。
解决:
link.xml保留需要的类不被裁剪。 - iOS打包坑
- Swift SDK需要设置Swift版本;需要嵌入Swift标准库。
- 缺少系统Framework链接,打包报错;用PostProcessBuild自动加。
- Bitcode已经废弃,新版本Xcode不再支持。
- 生命周期丢失
App切后台、回到前台,SDK需要通知
OnPause/OnResume;部分SDK不处理会丢失回调。 - 编辑器无法调试真机SDK PC/Editor只能跑Dummy空实现,登录支付必须真机打包。
六、两种SDK接入模式
- 官方提供UnityPackage(推荐) 第三方已经封装好C#桥接层,直接导入unitypackage,只需填AppID。例如友盟、腾讯广点通。
- 只有原生SDK,自己写桥接 自己写Android Studio生成AAR、iOS写OC包装层;自己写C#桥接,工作量大。适合自研SDK。
七、和热更新关系
SDK初始化、Activity、Manifest、iOS Info.plist、原生库属于AOT/原生包内容,不能热更新。
- SDK版本升级,必须重新发App商店包;
- 业务层调用SDK的C#逻辑可以放到HybridCLR热更DLL;但SDK本身无法热更。
简短总结
SDK接入本质:C#不直接碰原生系统API,靠插件桥接层做跨语言互调;Android用JNI,iOS用C‑DllImport;原生回调绝大多数跑子线程,务必切回平台主线程再通知Unity;Manifest、权限、XCode配置、代码剥离是最容易踩坑的点。
如果你需要,我可以给一份极简ISDK接口封装示例,或者PostProcessBuild示例。


0 条回复
还没有留言,来说点什么吧。