Unity 2021后Android插件AAR迁移实战:从目录到标准库的完整指南
1. 项目概述为什么Unity 2021后Android资源管理必须拥抱AAR如果你是一个Unity开发者并且你的项目需要与Android原生平台进行深度交互比如集成第三方SDK、自定义原生插件或者进行复杂的性能优化那么你很可能对Assets/Plugins/Android目录下的res、libs、AndroidManifest.xml等文件夹无比熟悉。在Unity 2021版本之前这是我们管理Android侧资源的“标准操作间”。然而从Unity 2021开始官方开始强力推动一种更现代、更规范的打包方式——将Android资源打包成AARAndroid Archive文件。这个变化对于许多习惯了旧有工作流的团队来说无异于一次“架构升级”过程中充满了各种意想不到的“坑”。我最近主导了公司一个大型Unity项目的引擎升级从2019 LTS直接跳到了2021 LTS。升级本身还算顺利但就在我们以为大功告成准备打出第一个Android包进行测试时构建过程直接“红屏”报错或者打包出来的APK在真机上运行时插件功能完全失效。核心问题就出在Android资源的处理上。旧项目里散落在Plugins/Android下的资源在新的构建管道尤其是Gradle构建系统下不再被正确地识别和打包。经过一周多的摸索、试错和源码查阅我终于把这套从传统目录结构迁移到AAR的完整流程和所有关键陷阱梳理清楚了。这篇文章就是这次“踩坑”之旅的完整记录旨在为你提供一份可以直接“抄作业”的避坑指南。简单来说这次迁移的核心目标是将你项目中所有与Android平台相关的原生代码Java/Kotlin、资源文件res、清单配置AndroidManifest.xml以及原生库.so文件从一个松散的文件夹集合规整地打包成一个或多个独立的AAR文件并由Unity的Gradle项目正确引用。这样做的好处是显而易见的依赖管理更清晰、构建过程更稳定、与Android Studio项目的互操作性更强也符合Google官方对Android库分发的推荐实践。接下来我将从设计思路、实操步骤到疑难杂症为你层层拆解。2. 核心思路与迁移方案设计在动手之前我们必须理解Unity构建Android应用时内部流程的变化这是所有“坑”的根源。理解了这个你才能明白为什么必须这么做而不是盲目地跟着步骤操作。2.1 Unity构建管道的演变从Internal到Gradle在较旧的Unity版本中构建Android APK主要有两种方式Internal默认内部Java构建系统和Gradle。Internal模式简单快捷但对复杂Android插件的支持有限。而Gradle模式功能强大支持自定义。在Unity 2021及以后版本中即使用户在Build Settings中选择了Internal模式Unity底层也默认使用了更接近Gradle的构建流程来处理插件和资源。这个变化是导致旧资源目录失效的根本原因。新的构建管道对Assets/Plugins/Android目录下的内容有了更严格的预期。它不再简单地复制所有文件而是期望找到一个结构规范的AAR文件或者一个符合Android Library模块标准的目录结构。散落的res、libs会被忽略或处理不当从而导致资源丢失、编译错误或运行时崩溃。2.2 迁移的两种核心路径基于上述变化我们的迁移方案主要有两种路径你需要根据项目实际情况选择路径一封装为独立AAR推荐尤其适合第三方SDK或成熟插件这是最彻底、最规范的方式。你需要创建一个独立的Android Library工程通常在Android Studio中完成将你所有的原生代码、资源、清单和库文件都放入这个工程中然后编译生成一个独立的.aar文件。最后将这个AAR文件放入Unity项目的Assets/Plugins/Android目录下。Unity在构建时会自动解压并合并这个AAR中的内容。优点高度解耦依赖关系清晰便于版本管理和复用。AAR内部可以包含自己的依赖声明如build.gradle管理起来非常方便。缺点前期需要一些Android开发的知识并搭建Android Studio环境。每次修改原生代码都需要重新编译生成AAR。路径二改造为“Exploded AAR”目录结构适合快速迁移或内部插件如果你觉得创建独立工程太麻烦或者你的插件结构简单且不常改动可以采用这种方式。你不需要生成.aar文件而是直接在Assets/Plugins/Android下创建一个符合AAR内部结构的文件夹通常以插件名命名如myplugin然后将资源按规范放入其中。Unity会将这个目录视同一个“展开的AAR”Exploded AAR进行处理。优点无需额外编译修改文件后立即在Unity中生效适合快速迭代和调试。缺点结构管理稍显混乱依赖管理能力弱不适合复杂或对外分发的插件。注意无论选择哪条路彻底删除或移走旧的、散落的Assets/Plugins/Android/res、/libs等目录是第一步也是避免冲突最关键的一步。新旧结构混用是绝大多数构建失败的元凶。2.3 工具选型Android Studio还是命令行生成AAR首推Android Studio。它提供了完整的IDE支持包括代码提示、依赖管理和一键编译。对于不熟悉Android开发的Unity程序员来说虽然有个学习曲线但长远来看收益巨大。你可以使用Android Studio创建一个新的“Android Library”模块这比从头配置要简单得多。如果你追求自动化也可以在配置好环境后使用Gradle命令行./gradlew :mylibrary:assembleRelease在CI/CD流水线中生成AAR。但初期调试阶段强烈建议使用Android Studio的图形界面直观地解决环境配置和编译错误。3. 实战演练从零构建一个Unity可用的AAR这里我以路径一创建独立AAR为例展示最完整的迁移流程。假设我们有一个简单的插件包含一个自定义的ToastHelper工具类Java代码和一些图标资源res。3.1 第一步在Android Studio中创建Android Library模块新建项目打开Android Studio选择“New Project”。模板选择“Empty Activity”即可我们只需要一个容器项目。添加Library模块项目创建好后在项目根目录右键 -New-Module。选择Android Library输入模块名称例如unityandroidplugin包名建议与你的Unity项目包名保持一致或在其子包下以减少冲突。其他设置保持默认点击完成。现在你的项目结构应该包含一个app模块可忽略和一个unityandroidplugin模块。我们所有的工作都在unityandroidplugin模块内进行。3.2 第二步迁移代码与资源到Library模块这是最核心的一步需要将旧Plugins/Android下的内容精准地搬家到Library模块的对应目录。Java/Kotlin源码将原有的.java或.kt文件复制到unityandroidplugin/src/main/java/com/yourcompany/plugin/目录下。注意包路径要正确。资源文件res将旧res目录下的所有子目录如drawable-hdpi,layout,values等整体复制到unityandroidplugin/src/main/res/目录下。如果存在资源ID冲突与Unity默认资源或你其他AAR中的资源同名需要在这里进行重命名或合并。清单文件AndroidManifest.xml这是大坑高发区不要简单复制旧的清单文件。你需要在unityandroidplugin/src/main/目录下创建AndroidManifest.xml文件。这个清单文件只应包含该插件自身需要的权限、组件声明和元数据。!-- unityandroidplugin/src/main/AndroidManifest.xml -- ?xml version1.0 encodingutf-8? manifest xmlns:androidhttp://schemas.android.com/apk/res/android packagecom.yourcompany.plugin !-- 声明这个插件需要的权限 -- uses-permission android:nameandroid.permission.INTERNET / !-- 声明这个插件提供的组件如Activity、Service等 -- application activity android:name.CustomActivity android:themeandroid:style/Theme.Translucent.NoTitleBar / !-- 其他组件或 meta-data -- /application /manifest关键点这个清单最终会与Unity主应用的清单合并。切勿在此重复声明Unity主AndroidManifest.xml中已经存在的权限或组件如主Activity否则会导致合并冲突构建失败。通常第三方SDK的集成指南会提供它需要的清单片段你只需要把那个片段加到这里即可。原生库.so文件将旧libs/armeabi-v7a,libs/arm64-v8a等目录复制到unityandroidplugin/src/main/jniLibs/目录下。注意目录名从libs变成了jniLibs这是Android Library的标准结构。依赖管理build.gradle打开unityandroidplugin/build.gradle文件。在这里你可以声明该插件所依赖的第三方库如Google Play Services, Firebase等。这能极大简化Unity侧的依赖管理。// unityandroidplugin/build.gradle dependencies { implementation com.google.android.gms:play-services-ads:22.0.0 // 其他依赖... }3.3 第三步编译生成AAR文件在Android Studio右侧的Gradle面板中找到unityandroidplugin-Tasks-build-assemble或assembleRelease。双击运行等待构建完成。生成的AAR文件位于unityandroidplugin/build/outputs/aar/目录下通常名为unityandroidplugin-release.aar。3.4 第四步在Unity中集成AAR将上一步生成的.aar文件例如unityandroidplugin-release.aar复制到Unity项目的Assets/Plugins/Android目录下。你可以为其创建一个子文件夹以便管理如Assets/Plugins/Android/MyPlugins/。关键操作在Unity编辑器中选中这个AAR文件在Inspector面板中确保Android平台被勾选。对于包含原生库.so的AAR通常还需要在插件设置中指定目标CPU架构。彻底删除或移出旧的、散落的Assets/Plugins/Android/res,/libs等文件夹和文件。检查Unity项目的主Assets/Plugins/Android/AndroidManifest.xml文件如果没有Unity会使用默认的。确保它包含了应用运行所需的所有基础权限和配置。你从AAR模块清单中移除的权限如果需要应该加到这里。至此一个完整的AAR迁移流程就完成了。构建你的Unity项目理论上应该能成功打包。4. “Exploded AAR”目录结构快速迁移法如果你的插件非常简单或者你想先快速验证迁移是否可行可以采用“Exploded AAR”方式。在Assets/Plugins/Android下创建一个文件夹例如MyExplodedPlugin然后在其内部模仿AAR的结构创建子目录Assets/Plugins/Android/ └── MyExplodedPlugin/ -- 这个文件夹名就是你的“AAR”名 ├── AndroidManifest.xml ├── res/ │ ├── drawable/ │ └── values/ ├── jniLibs/ -- 注意不是libs │ ├── arm64-v8a/ │ └── armeabi-v7a/ └── libs/ -- 这个libs放的是.jar依赖不是.so └── some-library.jar将对应的文件放入相应目录。Unity的构建系统会识别这种结构。这种方法的好处是修改即时生效但缺点如前所述在管理复杂依赖和多个插件时容易混乱。5. 深度避坑指南与疑难杂症排查理论流程走通了但实战中你会遇到各种报错。下面是我踩过或见过的典型问题及解决方案。5.1 清单文件合并冲突这是最常见的问题。错误信息通常包含Manifest merger failed。原因你的AAR中的AndroidManifest.xml与Unity主清单或其它AAR中的清单存在属性冲突例如android:theme重复定义uses-sdk版本不一致。解决方案精简AAR清单确保AAR清单只包含该插件绝对必要的声明。移除任何可能重复的权限、application标签属性或组件。使用合并规则标记在冲突的元素上添加合并工具指令。例如如果某个属性你想让主清单的版本覆盖AAR的版本可以在AAR清单中这样写activity android:name.MyActivity android:themestyle/MyTheme tools:replaceandroid:theme /注意需要在manifest标签中加入xmlns:toolshttp://schemas.android.com/tools。检查主清单在Unity中有时需要通过后处理脚本PostProcessBuild来动态修改最终合并后的清单以解决一些棘手冲突。5.2 资源ID冲突“Resource linking failed”错误信息指向R文件生成失败提示某个资源重复定义。原因不同AAR或AAR与Unity默认资源中存在同名的资源文件如ic_launcher.png或同名的字符串资源app_name。解决方案重命名资源这是最根本的方法。为你插件中的所有资源文件图片、布局文件和values中的资源项字符串、颜色等加上独特的前缀。例如将ic_icon.png改为myplugin_ic_icon.png将字符串app_name改为myplugin_app_name。更新引用在Java代码和清单文件中同步更新所有对重命名资源的引用。5.3 原生库.so文件加载失败游戏在Android上运行时崩溃logcat报错java.lang.UnsatisfiedLinkError。原因路径错误.so文件没有放在AAR内的jniLibs目录下而是放在了libs目录。架构缺失你的AAR只包含了armeabi-v7a的库但运行在64位设备arm64-v8a上或者反之。Unity插件设置不当在Unity中AAR文件的Inspector里没有为对应的CPU架构打勾。解决方案确认.so文件在AAR中的路径为jniLibs/ABI/。尽可能提供全平台架构的库arm64-v8a,armeabi-v7a,x86,x86_64。如果SDK提供商没有提供可以尝试联系获取或考虑使用纯Java实现。在Unity中选中AAR文件在Inspector的Platform Settings中确保CPU部分勾选了所有你提供了.so文件的架构。5.4 依赖传递问题你的AAR依赖了第三方库如OkHttp但Unity打包后运行时找不到类。原因AAR中声明的依赖implementation默认不具有传递性。Unity的Gradle构建可能没有正确获取这些传递依赖。解决方案使用api替代implementation在你AAR模块的build.gradle中如果你希望依赖项能传递给Unity主项目使用api关键字声明依赖。implementation会将依赖隐藏在该模块内部。dependencies { api com.squareup.okhttp3:okhttp:4.10.0 // Unity项目也能用到这个库 }在Unity中手动添加依赖如果方法1不奏效或者依赖比较复杂可以在Unity项目的Assets/Plugins/Android目录下创建一个mainTemplate.gradle文件需要开启Gradle构建并在Player Settings中勾选“Custom Main Gradle Template”在dependencies块中手动添加所需的依赖。// Assets/Plugins/Android/mainTemplate.gradle 中的 dependencies 部分 dependencies { implementation com.squareup.okhttp3:okhttp:4.10.0 // 其他全局依赖... }5.5 构建速度变慢迁移到AAR并使用Gradle后首次构建或清洁构建时间显著变长。原因Gradle需要解析依赖关系、下载依赖如果未缓存、并执行一系列复杂的构建任务。Unity的Internal模式相对简单。解决方案启用Gradle守护进程和并行构建在Unity的Assets/Plugins/Android/mainTemplate.gradle文件顶部或gradle.properties文件中配置// gradle.properties org.gradle.daemontrue org.gradle.paralleltrue org.gradle.cachingtrue使用本地Maven仓库对于内部开发的AAR可以发布到本地Maven仓库mavenLocal()避免每次都从文件系统导入。耐心等待首次构建后的增量构建会快很多。这是转向现代构建工具必须接受的代价换来的的是更好的稳定性和灵活性。6. 高级技巧与最佳实践掌握了基本迁移和问题排查后下面这些技巧能让你的流程更专业、更高效。6.1 自动化AAR版本管理与集成手动复制AAR文件容易出错。可以搭建一个简单的自动化流程在Android Library项目中使用maven-publish插件将编译好的AAR发布到公司的私有Maven仓库如Nexus或本地目录。在Unity项目中通过修改mainTemplate.gradle或settingsTemplate.gradle声明对这个Maven仓库的依赖。// settingsTemplate.gradle dependencyResolutionManagement { repositories { maven { url https://your.company.com/repo } // 私有仓库 maven { url ${project.rootDir}/../local-aars } // 本地相对路径仓库 } } // mainTemplate.gradle dependencies { implementation com.yourcompany:unityandroidplugin:1.0.0 }这样Unity构建时会自动从指定仓库拉取指定版本的AAR实现了依赖管理的版本化。6.2 调试AAR中的代码直接使用AAR时调试原生Java代码是个问题。有两种方法使用“Exploded AAR”模式调试在开发阶段将AAR工程中的src/main目录直接以“Exploded AAR”形式链接到Unity的Plugins/Android下。修改代码后立即生效并可以附加Android Studio的调试器进行调试。发布带源码的AAR在Android Library的build.gradle中配置使其发布时包含源码。这样在Unity打包后你可以用Android Studio打开Unity生成的中间Gradle项目位于Temp/gradleOut并附加调试器。6.3 处理多个AAR的复杂依赖图当项目集成多个第三方SDK且它们各自依赖不同版本的同一库时如不同版本的Android Support库或AndroidX就会发生冲突。使用Gradle的依赖决议策略在Unity的mainTemplate.gradle中可以强制指定某个库的版本。configurations.all { resolutionStrategy { force androidx.appcompat:appcompat:1.6.1 // 强制所有依赖使用此版本 } }排查依赖树在Android Studio的终端里进入unityandroidplugin目录运行./gradlew :unityandroidplugin:dependencies可以打印出详细的依赖关系树帮助你定位冲突源头。迁移到AAR的过程本质上是从一种“野路子”的松散管理转向Android生态提倡的标准化、工程化管理。初期肯定会遇到阻力但一旦完成你会发现插件集成、版本控制和构建稳定性都得到了质的提升。这套流程不仅适用于Unity 2021也是未来Unity Android开发的推荐实践。希望这份结合了无数“踩坑”经验的指南能帮助你更平滑地完成这次升级。