1. 这不是“Unity打包APK”的简单延伸而是真正打通双引擎协作的起点很多人第一次听说“Unity和Android Studio联合开发”下意识反应是“不就是Unity导出Android工程然后在AS里改点Java代码”——这恰恰是踩坑的第一步。我带过三支跨平台团队90%的新手都在这个认知偏差上卡了至少两周他们以为自己在“修改Unity导出的工程”实际却是在维护一个被Unity单向覆盖、随时可能被重置的脆弱快照。真正的联合开发核心在于双向可控、职责清晰、变更可追溯Unity负责游戏逻辑与渲染管线Android Studio负责原生能力封装如NFC、系统级通知、硬件传感器深度调用两者通过AAR这个标准化“接口契约”通信而非把Unity当黑盒、把AS当补丁工具。你看到的标题里“快速入门”四个字背后藏着三个硬门槛第一关是环境配置的“隐性依赖”——JDK版本必须精确匹配Unity的Gradle插件要求不是随便装个JDK 17就行第二关是AAR集成时的“符号冲突陷阱”比如Unity自带的AndroidX库和你自研SDK里的support-v4混用会导致运行时ClassNotFound第三关是双向调用的“线程安全盲区”Unity主线程和Android主线程根本不是一回事直接在OnDestroy里调Java方法十次有九次闪退。这篇内容专为已经能独立完成Unity基础打包、但一碰原生交互就掉帧/崩溃/找不到类的中阶开发者而写。它不讲“Hello World”只拆解真实项目里从环境初始化到首屏调用成功的完整链路包括那些Unity官方文档里不会写的、Android Studio Build Analyzer里藏得最深的报错根因。如果你正被“Unity调Java成功但Java回调不到Unity”、“AAR集成后App启动白屏”、“Gradle sync失败提示‘Could not resolve androidx.core:core:1.10.1’”这类问题反复折磨接下来的内容就是你该逐行抄写的操作手册。2. 环境配置不是装软件而是构建一套可验证的协同基线2.1 Unity端必须锁定的四个关键参数Unity对Android构建环境的校验远比表面严格。我见过太多人因为跳过这一步在后续AAR集成时花三天排查“为什么同样的AAR在同事电脑上能跑我这里一直报NoClassDefFound”。核心在于Unity不是简单读取系统PATH里的JDK而是在Player Settings里硬编码了JDK路径与Gradle版本绑定关系。以Unity 2021.3.33f1为例这是当前LTS版本中兼容性最稳的必须手动确认以下四点JDK版本必须使用JDK 11具体为Adoptium Temurin 11.0.227而非JDK 17或OpenJDK。原因在于Unity 2021.x系列的Gradle插件com.android.tools.build:gradle:4.2.2仅支持JDK 11编译字节码JDK 17会触发Unsupported class file major version 61错误。实测Temurin 11.0.227在Windows/macOS/Linux三端均无兼容性问题而Oracle JDK 11.0.20在macOS M1芯片上偶发JNI加载失败。Android SDK路径在Unity Preferences → External Tools → Android中SDK路径必须指向独立安装的Android SDK非Android Studio内置SDK。这是因为Unity需要直接调用sdkmanager命令行工具安装特定组件而AS内置SDK的权限结构常导致sdkmanager --list返回空结果。推荐路径~/Library/Android/sdkmacOS、C:\Users\YourName\AppData\Local\Android\SdkWindows。NDK与CMake版本NDK必须为r21e21.4.7075529CMake必须为3.10.2.4988404。这两个版本是Unity 2021.3.x的黄金组合高版本NDK如r25会导致IL2CPP编译时libil2cpp.so链接失败错误日志里藏在BuildReport.txt末尾的ld: error: unable to find library -llog就是典型症状。Gradle版本在Project Settings → Player → Publishing Settings → Build System中选择Gradle并勾选Custom Gradle Template。此时Unity会生成Assets/Plugins/Android/mainTemplate.gradle其中classpath com.android.tools.build:gradle:4.2.2必须保持原样——任何手动升级到4.4都会导致Unity构建时Gradle sync失败因为Unity的内部Gradle Wrapper未同步更新。提示验证环境是否就绪的最快方式是创建空Unity项目File → Build Settings → Platform切换为Android点击Switch Platform然后Build → Build And Run。如果出现“Gradle build failed”且日志中包含Could not determine the dependencies of task :app:compileDebugJavaWithJavac90%是JDK版本不匹配若出现Failed to install the following Android SDK packages as some licences have not been accepted说明SDK组件缺失需在终端执行sdkmanager --licenses全量接受协议。2.2 Android Studio端的“静默配置”清单Android Studio的配置难点不在界面操作而在那些默认关闭、但联合开发中必须开启的隐藏开关。这些设置不写进build.gradle却直接影响AAR的符号解析与调试体验启用AndroidX强制迁移在gradle.properties文件位于AS安装目录的gradle/properties或用户主目录下的.gradle/gradle.properties中必须添加三行android.useAndroidXtrue android.enableJetifiertrue android.suppressUnsupportedCompileSdk33第一行强制使用AndroidX命名空间第二行将旧support库自动转换为AndroidX等价物避免AAR中混用support-v4导致的ClassCastException第三行是关键——Unity 2021.3.x默认编译SDK为30而新版本AS默认要求SDK 33此参数压制警告防止Gradle sync时卡死。禁用Instant Run现为Apply Changes在AS Settings → Build, Execution, Deployment → Runtime → Apply Changes中取消勾选Enable Apply Changes。原因在于Unity生成的APK中classes.dex由IL2CPP预编译生成Apply Changes试图热替换字节码会破坏Unity的Native Plugin加载机制导致java.lang.UnsatisfiedLinkError: dlopen failed: library libunity.so not found。配置ADB调试白名单在AS Settings → Build, Execution, Deployment → Debugger → Android Debug Bridge中勾选Use ADB from SDK platform-tools并确保路径指向你独立安装的SDK下的platform-tools目录。更重要的是在adb_usb.ini文件位于~/.android/中手动添加你的设备厂商ID如华为为0x1234否则AS无法识别设备进行真机调试——Unity Editor的Logcat窗口只能看日志而AS的Debugger才能断点Java/Kotlin代码。2.3 双环境协同验证三步确认基线可用环境配置完成后必须执行三步原子化验证而非直接导入AARUnity侧验证新建空场景添加一个Button脚本中调用AndroidJavaClass(java.lang.System).GetStaticint(currentTimeMillis)Build后在真机上点击按钮Logcat中应输出毫秒时间戳。此步骤确认Unity能正常调用Java标准库排除JDK/SDK路径错误。AS侧验证在Android Studio中创建Empty Activity项目MainActivity.kt中添加Log.d(AS_TEST, AS init success)Run到同一台真机AS Logcat过滤AS_TEST应可见日志。此步骤确认AS调试通道畅通ADB设备识别正常。交叉验证将AS项目中的app/build/outputs/aar/app-debug.aar复制到Unity项目的Assets/Plugins/Android/目录下Unity中新建脚本调用new AndroidJavaObject(com.example.asproject.MainActivity)注意包名需与AS项目一致Build后运行。若Logcat出现ClassNotFoundException说明AAR未正确解压或包名不匹配若无报错但无日志说明AAR中未包含可实例化的类需检查AS的build.gradle中是否遗漏android.defaultConfig.multiDexEnabled true。这三步耗时约8分钟但能提前拦截80%的后续集成故障。我坚持让所有新人先完成此验证再进入AAR开发因为90%的“Unity调Java失败”问题根源都在这三步的某一个环节。3. AAR集成从“扔进Plugins文件夹”到构建可复用的原生能力模块3.1 AAR不是ZIP包而是经过签名与混淆的ABI契约很多开发者把AAR当成普通ZIP解压后修改classes.jar这是灾难的开始。AAR本质是Android Archive其结构包含classes.jar、res/、AndroidManifest.xml、jni/含so库及proguard.txt但关键在于Unity在构建时会将AAR中的classes.jar与自身生成的classes.jar合并并按Gradle依赖顺序重排字节码。这意味着如果你的AAR里有com.unity.plugin.UnityBridge类而Unity项目里也有同名类后者会覆盖前者——除非你在AAR的AndroidManifest.xml中声明application android:allowBackupfalse来触发Unity的合并策略。更隐蔽的问题是ABIApplication Binary Interface匹配。Unity IL2CPP默认生成ARM64-v8a架构的libunity.so而你的AAR若只包含armeabi-v7a的so库如旧版FFmpeg运行时会因dlopen找不到对应架构so而崩溃。验证方法用unzip -l your-plugin.aar | grep so$查看so库列表必须包含jni/arm64-v8a/libyourplugin.so。若缺失需在AS的build.gradle中强制指定android { defaultConfig { ndk { abiFilters arm64-v8a, armeabi-v7a } } }3.2 构建一个最小可行AAR以“系统通知”为例我们以一个真实高频需求——从Unity触发Android系统通知——构建第一个AAR。这不是为了炫技而是暴露AAR开发中最易忽略的五个细节第一步AS中创建ModuleFile → New → New Module → Android Library命名为unity-notification-plugin包名设为com.unity.notification必须与Unity调用时的包名完全一致第二步编写核心Java类// NotificationHelper.java package com.unity.notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.os.Build; import androidx.core.app.NotificationCompat; public class NotificationHelper { private static final String CHANNEL_ID unity_channel; public static void createNotification(Context context, String title, String content) { // 关键点1Context必须来自Activity或Application不能是getApplicationContext() // 因为PendingIntent需要Activity上下文来启动Intent if (Build.VERSION.SDK_INT Build.VERSION_CODES.O) { NotificationChannel channel new NotificationChannel( CHANNEL_ID, Unity Notifications, NotificationManager.IMPORTANCE_HIGH); NotificationManager manager context.getSystemService(NotificationManager.class); manager.createNotificationChannel(channel); } Intent intent new Intent(context, context.getClass()); // 关键点2此处context.getClass()获取当前Activity类 PendingIntent pendingIntent PendingIntent.getActivity( context, 0, intent, PendingIntent.FLAG_IMMUTABLE); // 关键点3Android 12必须加FLAG_IMMUTABLE NotificationCompat.Builder builder new NotificationCompat.Builder(context, CHANNEL_ID) .setSmallIcon(android.R.drawable.ic_dialog_info) .setContentTitle(title) .setContentText(content) .setPriority(NotificationCompat.PRIORITY_HIGH) .setContentIntent(pendingIntent); NotificationManager manager context.getSystemService(NotificationManager.class); manager.notify(1, builder.build()); } }第三步配置AndroidManifest.xmlmanifest xmlns:androidhttp://schemas.android.com/apk/res/android packagecom.unity.notification !-- 关键点4必须声明uses-permission且放在manifest根节点下 -- uses-permission android:nameandroid.permission.POST_NOTIFICATIONS / !-- 关键点5AAR的AndroidManifest.xml会被合并到主APK因此activity声明无效 所有UI组件必须由Unity侧Activity承载 -- /manifest第四步生成AAR在AS右侧面板Gradle → :unity-notification-plugin → Tasks → build → assembleRelease输出路径unity-notification-plugin/build/outputs/aar/unity-notification-plugin-release.aar注意不要用assembleDebugDebug版AAR包含调试符号体积大且可能触发ProGuard规则冲突。Release版经R8优化符号干净是Unity构建的标准输入。3.3 Unity侧集成AAR的七处致命陷阱将生成的AAR拖入Assets/Plugins/Android/后你以为结束了不这才是真正考验经验的地方。以下是我在27个Unity项目中总结的七处高频陷阱文件名后缀陷阱AAR文件名必须以.aar结尾且不能包含空格或中文。my_plugin_v1.0.aar合法my plugin.aar或my_plugin_v1.0.aar.bak会导致Unity构建时静默跳过该文件。目录层级陷阱AAR必须放在Assets/Plugins/Android/的直接子目录下不可嵌套。Assets/Plugins/Android/libs/my.aar会被识别但Assets/Plugins/Android/thirdparty/my.aar会被忽略——Unity的Android插件扫描器只遍历一级子目录。AndroidManifest合并冲突若AAR中AndroidManifest.xml声明了application android:theme而Unity的mainTemplate.gradle中已定义theme会导致Merge Conflict。解决方案是在AAR的AndroidManifest.xml中移除所有application属性仅保留uses-permission和meta-data。资源ID冲突AAR中的res/values/strings.xml若定义了string nameapp_name会与Unity的app_name冲突。必须重命名string nameunity_notification_title并在Java代码中用context.getString(R.string.unity_notification_title)引用。ProGuard规则缺失若AAR中使用了反射如Class.forName(com.unity.xxx)必须在AAR的proguard-rules.pro中添加-keep class com.unity.notification.** { *; }否则R8会移除相关类。Unity版本兼容性标记在AAR的AndroidManifest.xml中添加meta-data android:nameunityplayer.ForwardNativeEventsToDalvik android:valuetrue /确保Android原生事件如触摸能正确转发给Unity。Gradle依赖传递陷阱若AAR的build.gradle中声明了implementation androidx.core:core-ktx:1.10.1Unity构建时会尝试下载该依赖但网络超时导致失败。正确做法是将所有依赖的jar/aar显式打包进AAR的libs目录并在build.gradle中改为implementation files(libs/core-ktx-1.10.1.jar)。4. 双向调用从“能通”到“稳通”的线程、生命周期与异常处理4.1 Unity调Java为什么90%的“调用成功”都是假象Unity调用Java看似简单new AndroidJavaObject(com.unity.notification.NotificationHelper)。但实际项目中85%的“调用成功”只停留在Logcat打印日志一旦涉及UI操作如弹Toast或耗时任务如网络请求立刻崩溃。根因在于线程模型错配。Unity的C#脚本默认运行在Unity主线程即游戏逻辑线程而Android的UI操作如Toast.makeText().show()必须在Android主线程Main Thread/UI Thread执行。直接在C#中调用Java的UI方法等同于让Unity线程去操作Android UI控件触发CalledFromWrongThreadException。解决方案是强制切换到Android主线程// C#调用代码 private AndroidJavaObject notificationHelper; void Start() { using (var unityPlayer new AndroidJavaClass(com.unity3d.player.UnityPlayer)) { var currentActivity unityPlayer.GetStaticAndroidJavaObject(currentActivity); notificationHelper new AndroidJavaObject(com.unity.notification.NotificationHelper, currentActivity); } } public void ShowNotification(string title, string content) { // 关键通过currentActivity.post()切到Android主线程 using (var handler notificationHelper.GetAndroidJavaObject(mHandler)) { handler.Call(post, new AndroidJavaRunnable(() { notificationHelper.Call(createNotification, title, content); })); } }但这段代码仍有隐患notificationHelper是Java对象引用若Activity重建如屏幕旋转currentActivity失效notificationHelper会变成悬空引用。因此必须在Java层做双重保障// NotificationHelper.java中添加Handler初始化 private Handler mHandler; public NotificationHelper(Context context) { // 使用Activity的Looper确保post到UI线程 mHandler new Handler(context.getMainLooper()); }这样即使Unity侧currentActivity被GCJava层的mHandler仍持有有效的Looper引用保证post调用安全。4.2 Java调Unity不是“回调”而是“事件注入”Java调Unity常被误解为“回调函数”实则是Android向Unity的消息队列注入事件。Unity不提供类似setCallback()的API而是通过UnityPlayer.UnitySendMessage实现。这是最易出错的环节。常见错误写法// 错误直接在子线程调用UnitySendMessage new Thread(() - { UnityPlayer.UnitySendMessage(NotificationManager, OnNotificationReceived, data); }).start();这会导致java.lang.RuntimeException: Cant create handler inside thread that has not called Looper.prepare()因为UnitySendMessage内部需要Android主线程的Looper。正确流程分三步第一步在Unity中注册接收器// NotificationManager.cs public class NotificationManager : MonoBehaviour { // 必须是public static且方法签名固定void MethodName(string message) public static void OnNotificationReceived(string payload) { Debug.Log(Java notified: payload); // 处理业务逻辑 } }注意NotificationManager必须挂载在场景中的GameObject上且该GameObject的name必须为NotificationManager与UnitySendMessage第一个参数一致。第二步Java中安全调用// 在NotificationHelper.java中 public void notifyUnity(String payload) { // 关键必须在Android主线程调用 new Handler(Looper.getMainLooper()).post(() - { UnityPlayer.UnitySendMessage(NotificationManager, OnNotificationReceived, payload); }); }第三步处理Unity侧的线程安全UnitySendMessage会将消息投递到Unity主线程但若OnNotificationReceived中执行耗时操作如解析JSON会阻塞游戏帧率。最佳实践是将其转为协程public static void OnNotificationReceived(string payload) { instance.StartCoroutine(ProcessNotification(payload)); } private IEnumerator ProcessNotification(string payload) { // 模拟耗时解析 yield return new WaitForSeconds(0.1f); Debug.Log(Processed: payload); }4.3 生命周期同步Activity重建时的引用保活策略Android Activity因配置变更如横竖屏切换或内存回收会频繁重建而Unity的currentActivity引用会随之失效。若Java层持有该引用并尝试调用会触发NullPointerException。根本解法是不依赖Activity实例改用Application Context BroadcastReceiverJava端改造// 在NotificationHelper.java中 private static Context sAppContext; public static void init(Context context) { sAppContext context.getApplicationContext(); // 获取Application Context生命周期与APP同级 } public static void sendBroadcast(String action, String data) { Intent intent new Intent(sAppContext, NotificationReceiver.class); intent.setAction(action); intent.putExtra(payload, data); sAppContext.sendBroadcast(intent); }新增BroadcastReceiverpublic class NotificationReceiver extends BroadcastReceiver { Override public void onReceive(Context context, Intent intent) { String payload intent.getStringExtra(payload); UnityPlayer.UnitySendMessage(NotificationManager, OnNotificationReceived, payload); } }Unity端注册广播接收器// 在Awake中注册 void Awake() { using (var unityPlayer new AndroidJavaClass(com.unity3d.player.UnityPlayer)) { var activity unityPlayer.GetStaticAndroidJavaObject(currentActivity); activity.Call(registerReceiver, new AndroidJavaObject(com.unity.notification.NotificationReceiver), new AndroidJavaObject(android.content.IntentFilter, com.unity.notification.RECEIVE)); } }这样无论Activity如何重建Application Context始终有效广播接收器持续监听彻底解决引用失效问题。4.4 异常处理捕获Java层崩溃并透传到Unity日志Java层未捕获的异常如NullPointerException会导致整个APP崩溃而Unity日志中只显示FATAL EXCEPTION无法定位Java代码行号。必须在AAR中植入全局异常处理器// 在NotificationHelper.init()中添加 public static void init(Context context) { sAppContext context.getApplicationContext(); // 全局异常捕获 Thread.setDefaultUncaughtExceptionHandler((thread, ex) - { String errorMsg ex.getMessage() \n Log.getStackTraceString(ex); // 透传到Unity日志 UnityPlayer.UnitySendMessage(CrashHandler, OnJavaCrash, errorMsg); // 同时写入Android日志 Log.e(UNITY_AAR, Java crash: errorMsg); }); }Unity侧创建CrashHandler.cspublic class CrashHandler : MonoBehaviour { public static void OnJavaCrash(string errorMsg) { Debug.LogError(Java Crash: errorMsg); // 可在此触发上报服务 } }此方案让Java崩溃像C#异常一样出现在Unity Console中行号精准调试效率提升3倍以上。5. 实战排错从Logcat堆栈到Gradle依赖树的完整溯源链5.1 “NoClassDefFoundError”不是类不存在而是类加载失败当Logcat出现java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/gson/Gson;新手第一反应是“Gson库没加”。但真相往往是Gson的jar包被Unity的IL2CPP构建过程剥离了。Unity在生成classes.dex时会对所有Java依赖执行ProGuard规则若AAR中未提供proguard-rules.proUnity的默认规则会移除未被直接引用的类。验证方法在Unity构建后的Temp/StagingArea/android-libraries/目录下找到你的AAR解压后的classes.jar执行jar -tf classes.jar | grep Gson若无输出证明类已被移除。解决方案在AAR的build.gradle中添加ProGuard保留规则android { buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile(proguard-android-optimize.txt), proguard-rules.pro } } }并在proguard-rules.pro中写-keep class com.google.gson.** { *; } -keep class com.google.gson.reflect.TypeToken { *; }5.2 Gradle sync失败从“Could not resolve”到依赖树分析当AS中Gradle sync失败提示Could not resolve androidx.core:core:1.10.1这不是网络问题而是Unity生成的mainTemplate.gradle与AAR的Gradle插件版本冲突。Unity 2021.3.x的mainTemplate.gradle中buildscript块声明了com.android.tools.build:gradle:4.2.2而你的AAR若在build.gradle中声明了com.android.tools.build:gradle:7.4.2Gradle会因版本不兼容拒绝解析。诊断命令在AS Terminal中执行./gradlew app:dependencies --configuration compileClasspath deps.txt打开deps.txt搜索androidx.core:core观察其来源若显示--- androidx.core:core:1.10.1且无(*)标记说明该依赖被直接引入若显示--- androidx.core:core:1.10.1 (*)说明该依赖被其他库传递引入且版本被统一管理。解决方案在AAR的build.gradle中强制指定版本configurations.all { resolutionStrategy { force androidx.core:core:1.10.1 force androidx.appcompat:appcompat:1.6.1 } }5.3 真机调试白屏从SurfaceView生命周期到Unity Player初始化App启动后黑屏/白屏Logcat无明显错误这是Unity与Android SurfaceView生命周期不同步的经典症状。Unity Player的初始化必须等待AndroidSurfaceView完全就绪否则渲染线程无法绑定。根因在于Unity的UnityPlayer类在onCreate()中初始化但此时SurfaceView的surfaceCreated()回调尚未触发。解决方案是在AS的MainActivity.java中重写onResume()并延迟Unity初始化Override protected void onResume() { super.onResume(); // 等待SurfaceView就绪后再启动Unity getWindow().getDecorView().post(() - { if (mUnityPlayer ! null) { mUnityPlayer.resume(); } }); }同时在Unity的AndroidManifest.xml中为UnityPlayerActivity添加android:exportedtrueAndroid 12必需并确保intent-filter中包含action android:nameandroid.intent.action.MAIN /。5.4 AAR集成后APK体积暴增从so库冗余到资源压缩一个1MB的AAR集成后APK体积增加20MB大概率是so库重复打包。Unity IL2CPP默认生成libunity.so而你的AAR若也包含libunity.so如误将Unity SDK打包进AAR会导致APK中存在两份相同so体积翻倍。诊断命令解压APK执行find . -name *.so | xargs ls -lh若发现lib/arm64-v8a/libunity.so和lib/arm64-v8a/libyourplugin.so大小接近均10MB则确认冗余。解决方案在AAR的build.gradle中移除所有与Unity相关的so依赖android { packagingOptions { exclude lib/*/libunity.so exclude lib/*/libil2cpp.so exclude lib/*/libmain.so } }此外启用APK资源压缩在UnityPlayer Settings → Publishing Settings中勾选Split Application Binary并将Install Location设为Prefer External可减少APK体积30%以上。6. 进阶技巧自动化构建、CI/CD集成与性能监控6.1 用Gradle Task自动化AAR构建与Unity同步每次手动复制AAR到Unity太低效。在AS的build.gradle中添加自定义Tasktask copyAARToUnity(type: Copy) { from(build/outputs/aar/unity-notification-plugin-release.aar) into(../MyUnityProject/Assets/Plugins/Android/) rename(unity-notification-plugin-release.aar, unity-notification.aar) }然后在Unity外部执行./gradlew copyAARToUnityAAR自动同步。更进一步结合Unity命令行构建# 在AS构建AAR后自动触发Unity构建 ./gradlew copyAARToUnity \ /Applications/Unity/Hub/Editor/2021.3.33f1/Unity.app/Contents/MacOS/Unity \ -batchmode -quit -projectPath /path/to/MyUnityProject \ -buildTarget Android -buildName app-release.apk -buildPath ./build/6.2 CI/CD流水线中的AAR版本管理在GitLab CI中为AAR添加语义化版本号stages: - build-aar - build-unity build-aar: stage: build-aar script: - ./gradlew assembleRelease - echo AAR_VERSION1.2.0 build.env artifacts: paths: - build/outputs/aar/*.aar expire_in: 1 week build-unity: stage: build-unity needs: [build-aar] script: - source build.env - cp build/outputs/aar/*.aar $UNITY_PROJECT_PATH/Assets/Plugins/Android/ - $UNITY_EXEC -batchmode -quit -projectPath $UNITY_PROJECT_PATH -buildTarget Android -buildName app-$AAR_VERSION.apk6.3 性能监控在AAR中注入Method Trace为定位Java调用耗时在关键方法开头插入Debug.startMethodTracing(notification_create); // ... 业务代码 Debug.stopMethodTracing();生成的.trace文件可通过adb pull /sdcard/Download/notification_create.trace拉取用Android Studio Profiler打开分析。最后分享一个血泪教训在Unity 2021.3.x中若AAR的minSdkVersion设为21而Unity Player Settings中Target API Level设为30构建时会静默降级AAR的API级别导致Android 12设备上POST_NOTIFICATIONS权限申请失败。解决方案是AAR的minSdkVersion必须等于Unity的Target API Level并在build.gradle中显式声明android { defaultConfig { minSdkVersion 30 targetSdkVersion 30 } }这个细节Unity官方文档从未提及却是线上崩溃率最高的原因之一。