Unity SDK接入原理


SDK(登录、支付、广告、统计、推送)大多是原生平台SDK:Android用Java/Kotlin,iOS用OC/Swift。 Unity C#不能直接调用系统原生API,必须通过插件桥接层完成 C# ↔ 原生代码互相调用。

整体分层

  1. 上层:C#业务层:游戏业务逻辑,统一对外封装SDK接口
  2. 桥接层:Unity插件机制,C#与Java/OC跨语言通信
  3. 底层:原生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)

  1. 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
  1. Java回调C#
  • UnityPlayer.UnitySendMessage("对象名","方法名","字符串参数")
    • 限制:目标GameObject名字必须固定;回调函数签名只能是void Func(string msg);只能传字符串;异步延迟一帧;子线程调用容易丢失消息或崩溃。
  • 高级:AndroidJavaProxy接口代理,可以传递复杂参数。

Java层注意:SDK回调经常跑在子线程,必须切回Android UI线程runOnUiThread,再执行UnitySendMessage,否则异常崩溃。

iOS端(C# ↔ Objective‑C)

  1. 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
  1. OC回调C# 同样使用C函数UnitySendMessage("GameObject","Func","json")。>

⚠️iOS原生SDK回调经常在GCD后台队列,必须手动Dispatch切换到主线程再调用UnitySendMessage,否则直接崩溃。

重要:跨平台条件编译

#if UNITY_ANDROID
//安卓逻辑
#elif UNITY_IOS
//iOS逻辑
#else
//编辑器/PC占位,防止编辑器报错
#endif

三、SDK接入标准工作流

  1. 下载第三方SDK,拿到Android(aar/jar/so)、iOS(framework/a)原生库。
  2. 将原生库放入Plugins对应目录,配置插件导入设置。
  3. Manifest / Info.plist配置:Android权限、Activity;iOS权限、白名单、LSApplicationQueriesSchemes。
  4. C#封装统一SDK对外接口,隔离Android/iOS平台差异。
  5. 写PostProcessBuild打包后脚本(Editor脚本)
    • Android:修改Gradle依赖,处理冲突;使用EDM4U自动解析依赖。
    • iOS:自动修改XCode工程,添加Framework、设置编译参数,避免每次手动改Xcode。
  6. 游戏启动初始化SDK;做生命周期处理OnPause/OnResume。
  7. 真机测试:初始化、登录、支付回调、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硬编码。

五、高频踩坑(非常重要)

  1. 线程问题(最多崩溃来源)>

Android/iOS SDK回调经常跑在子线程!

  • 原生子线程不能直接操作原生UI;
  • 原生子线程直接调用UnitySendMessage会消息丢失、随机崩溃。 ✅解决方案:原生层把回调调度回平台主线程,再通知Unity。
  1. UnitySendMessage缺点 只能传字符串;GameObject名字不能改;不能传复杂对象。建议全部用JSON序列化传递数据。
  2. AndroidManifest冲突 多个SDK各自带Manifest,合并冲突;需要自定义Custom Main Manifest。 SDK的Activity必须继承UnityPlayerActivity,否则会黑屏闪退。
  3. IL2CPP托管代码剥离(Stripping) Release打包IL2CPP会裁剪未被直接引用的C#代码;SDK回调、反射用到的类被删掉,导致线上闪退。 解决:link.xml保留需要的类不被裁剪。
  4. iOS打包坑
  • Swift SDK需要设置Swift版本;需要嵌入Swift标准库。
  • 缺少系统Framework链接,打包报错;用PostProcessBuild自动加。
  • Bitcode已经废弃,新版本Xcode不再支持。
  1. 生命周期丢失 App切后台、回到前台,SDK需要通知OnPause/OnResume;部分SDK不处理会丢失回调。
  2. 编辑器无法调试真机SDK PC/Editor只能跑Dummy空实现,登录支付必须真机打包。

六、两种SDK接入模式

  1. 官方提供UnityPackage(推荐) 第三方已经封装好C#桥接层,直接导入unitypackage,只需填AppID。例如友盟、腾讯广点通。
  2. 只有原生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示例。


21 8 月, 2026Garfield God学习笔记阅读 1 次

0 条回复

还没有留言,来说点什么吧。

留言