尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Android AAR打包全指南:从Module创建到Maven发布实战

发布时间:2026/9/29 7:29:09

资讯中心
01
ARTICLE

Android AAR打包全指南:从Module创建到Maven发布实战

Android AAR打包全指南:从Module创建到Maven发布实战
1. 打包AAR前先搞清楚它到底是个什么玩意先说结论AARAndroid Archive是Android Library Module的最终交付格式本质上是一个zip包里面装着编译后的classes.jar、Android资源文件res、清单文件AndroidManifest.xml、so库jniLibs、R类、proguard规则、assets等。用Android Studio打包AAR就是把一个工程里的Library Module从源码形态编译成可被其他项目直接引用的二进制组件。很多新手容易把AAR和JAR搞混。JAR只包含Java字节码装不了Android资源但凡你的库里有布局、图片、字符串这些JAR就完全无能为力。AAR则相当于一个“带资源的JAR”它把Android工程所需的全部物料都塞进一个文件里。所以你会发现凡是做SDK、做组件化、做模块复用的团队最终交付物基本都是AAR而不是JAR。什么场景需要打包AAR我列几个典型的公司内部有多个App共用的基础库比如网络层、图片加载封装、崩溃上报打成AAR发给各业务线接入。给第三方客户提供SDK不想暴露源码交付AAR是行业惯例。组件化改造时把每个业务模块隔离成Library Module通过AAR或本地Maven仓库来集成。你自己写的工具库想在不同项目之间复用AAR比复制源码更干净。这篇文章我按自己的实操经验从创建Library Module开始到配置打包参数、生成AAR、上传本地Maven、对端引用再到常见问题排查完整走一遍。无论你是刚接触Android Studio的新人还是已经写过几个App但没碰过SDK开发的开发者照着做都能出活。2. 环境准备和Module创建这些基础不牢后面全是坑2.1 版本选型JDK、AGP、Gradle要匹配打包AAR用的是Android Studio里的Gradle构建系统所以环境的核心就是Android Studio版本、Android Gradle PluginAGP版本、Gradle版本、JDK版本必须匹配。我实际遇到过太多项目一上来就报“Unsupported class file major version”或者“Minimum supported Gradle version is X.X”基本都是版本没对上。以Android Studio最新稳定版来说AGP 8.x要求JDK 17Gradle 8.x也要求JDK 17。如果你的项目还在用JDK 11要么升级JDK要么把AGP降到7.x。说个经验AGP 7.4配JDK 11、Gradle 7.5是可以正常打包AAR的但2026年这个时间点新项目我建议直接上JDK 17 AGP 8.x少走弯路。你在Android Studio里打开File - Project Structure - SDK Location可以看JDK路径也可以在gradle/wrapper/gradle-wrapper.properties里指定Gradle版本在根目录的build.gradle或settings.gradle里声明AGP版本。检查这三个东西在一条链上是齐的再往下走。还有一个容易忽略的点SDK版本。Library Module的compileSdk决定了你用哪些API编译建议用你本机已下载的最新SDK但targetSdk在Library里其实不影响最终App真正生效的是App模块的配置。minSdk决定了你库能覆盖的最低系统版本比如你库用了java.time那minSdk至少要26或者你在代码里做兼容。2.2 新建Library Module的两种姿势在Android Studio里创建Library Module最直接的方式是File - New - New Module。选择Android Library注意别选成Phone Tablet Module后者创建的是Application。填好Module名称比如mylibrary、包名比如com.example.mylibrary、最低SDK版本。点Finish等Gradle同步完成。创建完你会在settings.gradle里看到一句include :mylibrary在项目左侧的mylibrary目录下会出现一个build.gradle注意这个文件的头部是plugins { id com.android.library }如果看到的是com.android.application那说明这个Module不是Library打包结果会变成APK而不是AAR。这是新手最常踩的坑之一。有时候你拿到的是别人给的一套源码里面已经有Library Module了那就不用新建。但你要检查它的包名、命名空间namespace是否正确命名空间在AGP 8.x时代是写在android块里的android { namespace com.example.mylibrary }这个namespace很重要它决定了R类、BuildConfig的包名路径如果和applicationId混在一起后面引用资源时就会找不到类。在Library Module里没有applicationId这个概念因为它不是独立运行的App。2.3 在Library里写代码时的几个特殊规矩Library Module和App Module写代码有个显著区别Library的AndroidManifest.xml是被合并到App里的所以你在Library清单里声明的Activity、权限、ContentProvider最终会合入宿主App。这是AAR“包含清单”的体现。但是Library的清单里有些属性会被忽略或覆盖主要是application标签下的属性比如label、icon、theme这些在合并时以App模块为准。另外如果Library里声明了权限uses-permission合并后App会统一申请但有个细节如果App没有声明Library声明了最终APK里会包含这个权限因为清单合并默认是叠加的。写代码时建议遵循几个原则不要依赖Application的初始化顺序。你库里的代码可能在Application.onCreate之前被调用所以要么提供显式init(Context)方法要么用ContentProvider做自动初始化但要注意启动耗时。不要直接用BuildConfig.DEBUG以外的BuildConfig字段除非你显式开启了buildConfig生成。现在AGP 8.x默认关闭BuildConfig生成你如果要用自定义字段必须在build.gradle里打开android { buildFeatures { buildConfig true } defaultConfig { buildConfigField String, API_BASE_URL, \https://api.example.com/\ } }这个细节我在实际交付SDK时经常被问到很多人以为Library里可以直接用BuildConfig结果编译报错找不到符号。3. 一步步把Module打包成AAR生产环境的完整操作3.1 最基础的构建命令和产物路径创建好Library Module之后打包AAR本身是非常简单的一件事。在Android Studio右侧的Gradle面板中展开mylibrary - Tasks - build双击assembleRelease或assembleDebug构建完成后在mylibrary/build/outputs/aar/目录下就能看到mylibrary-release.aar或mylibrary-debug.aar。如果你更习惯命令行在项目根目录执行./gradlew :mylibrary:assembleReleaseWindows下是gradlew.bat :mylibrary:assembleRelease。这条命令会编译Library的Release变体并生成AAR。为什么是assembleRelease因为Library的Release变体通常对应你最终要交付的版本Debug变体里可能带了调试日志或多出一些测试代码不适合直接发给外部。生成产物的路径和命名规则是可以改的。默认命名是模块名-变体名.aar比如mylibrary-release.aar。想自定义输出文件名可以在android块里配置android { libraryVariants.all { variant - variant.outputs.all { outputFileName mylibrary-${variant.name}-${variant.versionName}.aar } } }其中variant.versionName需要在defaultConfig里配置defaultConfig { versionCode 1 versionName 1.0.0 }这里说句实在话AAR这种东西在正式项目里一般不会直接拷贝文件发给对方因为依赖传递问题太麻烦了。后面我会专门讲如何用Maven仓库来管理AAR的发布和依赖。但“打包AAR文件”这个动作本身是后续所有发布流程的基础。3.2 带源码Jar、混淆、资源和so库的完整配置一个正经的AAR光有编译产物还不够。你还需要考虑源码Jar很多调用方在Android Studio里点进SDK方法时想看注释和签名没有源码Jar就会看到反编译后的class。建议在打包时同时生成-sources.jar。混淆如果你的Release AAR不做混淆那对方拿到的就是个透明的包方法名、类名全暴露。做SDK交付一般对自己代码做混淆但对外暴露的API要keep住。资源和so库这些默认会被打包进AAR不需要额外配置但要注意so库的ABI目录结构是否符合要求比如jniLibs/armeabi-v7a、jniLibs/arm64-v8a。proguard规则如果Library里用到了反射、Gson、注解等需要在Library的proguard-rules.pro里写好keep规则并在Library的build.gradle里配置android { defaultConfig { consumerProguardFiles consumer-rules.pro } }consumerProguardFiles和proguardFiles不同前者是发布给使用方的规则会在App打包时自动应用后者只作用于Library自身的构建。我见过有人把两者写反导致App在混淆时报错找不到Library里的类。生成源码Jar的标准做法是自定义一个Gradle Taskandroid.libraryVariants.all { variant - def sourcesJar tasks.register(${variant.name}SourcesJar, Jar) { archiveClassifier.set(sources) from android.sourceSets.main.java.srcDirs } artifacts.add(archives, sourcesJar) }配置好之后执行mylibrary:assembleRelease在build/outputs/aar目录下除了AAR文件还会生成对应的-sources.jar。如果你用maven-publish插件发布到Maven仓库源码Jar会被自动识别并上传。3.3 统一AAR中R类的资源名和BuildConfig字段多人合作或者对接方风格不一致时AAR里资源名的前缀规范很重要。Library的资源和App的资源在打包时是合并的如果Library的资源名太通用比如btn_ok、bg_white很容易和宿主App的资源冲突。虽然AGP在合并资源时以App为准不会编译报错但运行时的资源ID可能会被替换掉导致你的SDK界面显示错乱。解决方法是给Library资源配置resourcePrefix在build.gradle里加一行android { resourcePrefix mylib_ }加了这个配置之后Library里所有新增资源名必须以mylib_开头否则编译直接报错。不过要注意resourcePrefix只约束你自己写的资源AAR里如果引用了第三方库的资源那部分不受前缀约束。BuildConfig字段我在前面提到过如果你要在Library里区分不同环境推荐这么配置defaultConfig { buildConfigField boolean, LOG_DEBUG, true buildConfigField String, API_HOST, \https://release-api.com/\ }然后对外提供API时通过BuildConfig来开关日志。这里有个小技巧Debug变体打开日志Release变体关闭日志可以写死也可以用Gradle的buildTypes来差异化配置。buildTypes { release { buildConfigField boolean, LOG_DEBUG, false } debug { buildConfigField boolean, LOG_DEBUG, true } }这样对接方在用你的SDK时如果引入的是release AAR日志自然是关闭状态不用手动调接口。4. 打包时最容易翻车的五类问题附排查命令4.1 依赖没有被带进去调用方ClassNotFound这是AAR打包的经典问题。你的Library依赖了OkHttp、Gson等第三方库结果你把AAR发给对方对方运行时报NoClassDefFoundError。原因很直接AAR文件里并不包含你依赖的第三方库的代码AAR只记录依赖坐标比如com.squareup.okhttp3:okhttp:4.12.0需要由消费者自己去拉取这些依赖。如果你通过Gradle直接依赖AAR所在ModuleGradle会自动读取AAR的pom文件并传递依赖。但如果你只是手工复制一个AAR文件到libs目录然后用implementation files(libs/xxx.aar)这种方式引入那传递依赖就全断了所有依赖都要对方手工补。解决方案有两个方向一是走Maven仓库发布我后面会讲利用pom文件管理依赖传递。这是正经做法。 二是不走Maven但要把所有第三方依赖的class直接打进AAR里。业界有一种做法叫fat AAR用Gradle脚本把依赖的jar包解压合并到AAR的classes.jar里但资源文件、so库的合并更复杂我不建议新手这么干调试成本太高。判断一个AAR是否包含某依赖可以解压看里面的结构unzip mylibrary-release.aar -d aar_extract cd aar_extract jar tf classes.jar | grep okhttp3如果classes.jar里没有okhttp的类说明依赖是靠pom传递的发布到Maven后对方才能正常编译。4.2 清单合并冲突和资源重复多发场景是App项目本身引用了AndroidX库而你的AAR也依赖了AndroidX库的某个版本两边版本不一致导致清单合并冲突报错类似Manifest merger failed : Attribute applicationtheme value(xxx) from AndroidManifest.xml:xx这时候先在App的build.gradle里排查依赖树看冲突的具体是哪个库./gradlew :app:dependencies --configuration releaseRuntimeClasspath如果确实是版本不一致建议在App里用resolutionStrategy统一版本configurations.all { resolutionStrategy { force androidx.core:core:1.12.0 } }或者你的Library发布时不要把整个AndroidX依赖暴露出去改用compileOnly方式让宿主App自己决定AndroidX版本。但这个要谨慎如果你的库内部真的用到了某个新版本的APIcompileOnly会导致运行时崩溃。资源冲突的排查就简单些报错会提示具体资源名。如果你的库设置了resourcePrefix这类问题基本不会出现。R类冲突的另一个体现是引用了同一个库的重复R类这往往是依赖重复导致还是先去查依赖树。4.3 so库兼容性和minSdk不匹配如果你的AAR里含有so库那么打包时需要特别注意ABI目录。常见的错误是只放了arm64-v8a但对接方的App里还有armeabi-v7a在旧机器上运行就会UnsatisfiedLinkError。更坑的是App的高版本在安装时可以兼容x86模拟器但so库不支持直接崩。我的建议是Library的so库尽量覆盖主流ABIarm64-v8a、armeabi-v7a、x86_64三个x86可以考虑只在debug里打。如果你的so库体积太大也可以提供按abi拆分的AAR让App端用abiFilters选择。比如你的AAR里有多种ABI对接方在App的build.gradle里配置defaultConfig { ndk { abiFilters arm64-v8a, armeabi-v7a } }这样打包App时就只会保留这两个ABI的soAPK体积会小很多。但要注意如果App的minSdk 21那还用老一套的armeabiARMv5可能也需要2026年了我觉得真没这个必要官方和第三方库基本都放弃这玩意了。4.4 Release包被混淆后对外公开API失效我见过一个交付案例SDK打包时整个Library都做了混淆结果对接方在App里按文档调用SDK方法运行到一个回调接口时直接NullPointerException一看日志是SDK内部某个接口实现类被混淆成了a.b.c和回调方的调用路径对不上。问题的根子在于混淆不是不能做而是必须明确哪些类不能混淆。对外暴露的API类、回调接口、数据模型比如Json解析用的Bean、注解、反射相关的类都要在consumer-rules.pro里keep住。一个通用的保底配置是这样-keep public class com.example.mylibrary.** { *; } -keep interface com.example.mylibrary.** { *; } -keepclassmembers class com.example.mylibrary.** { public *; }-keep public class这一行会把包下所有public类及成员全部保留对于SDK交付是可以接受的。如果你还用了Gson那对应的Bean也要keep因为Gson通过反射创建实例和读写字段-keep class com.example.mylibrary.model.** { *; }验证混淆是否生效、keep是否完整可以打开AAR里的classes.jar用jadx或jd-gui反编译看看。或者直接用这个命令查看混淆映射unzip mylibrary-release.aar classes.jar -d temp cd temp jar xf classes.jar # 查看映射文件在library的build/outputs/mapping/release/mapping.txt如果mapping.txt里有你本想保留的类被重命名了说明keep规则没写对。4.5 Debug和Release产物不一致AAR在不同环境表现不同有时候Debug AAR能跑通Release AAR一接就崩排除混淆因素后要注意两点。一是Debug和Release的BuildConfig字段不同比如日志开关如果代码里依赖了BuildConfig来判断逻辑分支行为就可能变。二是Release默认开了代码优化minifyEnabled、shrinkResources如果优化器删掉了通过反射使用的类或方法也会崩。排查方法是对比两者的mapping和最终产物看Release的AAR里是否比Debug少了一些类。我在实际项目中遇到过findViewById反射获取view控件结果Release包把view字段优化掉了死活找不到。这种问题用keep规则解决也很简单在consumer-rules里加上对用反射的类做keep。5. 发布到本地Maven或远程仓库告别手发AAR文件的原始方式5.1 为什么要用Maven仓库手发AAR文件对方要把文件放进libs再自己补一堆依赖坐标版本升级时还要重新发文件、对方手动替换效率低且容易出错。如果你把AAR发布到一个Maven仓库不管是本地文件系统、公司内网Nexus还是JitPack对方只需要在build.gradle里写一行implementation com.example:mylibrary:1.0.0Gradle会自动下载AAR并通过pom文件把传递依赖一并拉下来。这也是为什么我强烈建议AAR的最终交付手段是Maven而不是拷贝文件。5.2 用maven-publish插件发布到本地仓库在Library模块的build.gradle里配置maven-publish插件plugins { id com.android.library id maven-publish } afterEvaluate { publishing { publications { release(MavenPublication) { from components.release groupId com.example artifactId mylibrary version 1.0.0 } } repositories { maven { // 本地仓库路径 url uri(${rootProject.buildDir}/repo) } } } }然后执行./gradlew :mylibrary:publishReleasePublicationToMavenRepository构建完成后在项目/build/repo/com/example/mylibrary/1.0.0/下会看到mylibrary-1.0.0.aar mylibrary-1.0.0.pom mylibrary-1.0.0-sources.jar注意要生成并上传sources.jar可以在publishing里加上publishing { publications { release(MavenPublication) { artifact androidSourcesJar } } }其中androidSourcesJar是前面定义的那个Task的名字需要保持一致。5.3 对端项目如何引用在App模块的build.gradle里添加仓库地址和依赖repositories { maven { url uri(${rootProject.buildDir}/repo) } // 如果是远程内网仓库写成 http://nexus.xxx.com/repository/android/ } dependencies { implementation com.example:mylibrary:1.0.0 }还有一点要提醒如果App项目里用的仓库地址写的是相对路径对端clone下来后路径可能对不上。所以本地仓库这种方式更适合自己单机复用团队协作还是建议部署一个正式的Maven仓库比如Nexus或者直接用JitPack。JitPack的用法更简单把Library代码推到GitHub在App的build.gradle里加maven { url https://jitpack.io }然后依赖com.github.用户名:仓库名:版本号。JitPack会自动拉取源码并在云端构建、发布AAR和pom。缺点是首次构建耗时且要求你的项目结构标准优点是省去了自建Nexus的运维成本。很多开源库都是这么发布的所以被广大开发者接受度很高。6. 集成到App工程做联调两种方式各自的特点6.1 方式一直接依赖Module源码在App的settings.gradle里把Library模块include进来然后在App的build.gradle里依赖implementation project(:mylibrary)这种方式的优点是改Library代码立即生效调试非常方便缺点是每次变动都要重新编译Library如果App很大构建时间会变长。团队内部做功能联调时这种方式它是效率最高的因为不需要重复走“改代码-打包AAR-发布Maven-App改版本号-同步”这个链路。6.2 方式二依赖Maven远端AAR联调阶段也可以直接用发布的AAR版本来验证尤其当Library是别的团队维护时用Maven坐标依赖可以保证拿到的是正式版本而不是某个开发中状态。缺点是Library每次改动需要先发布新版本App再升级版本号迭代节奏会慢一点。做对外SDK或跨团队交付时这种方式是必须的因为对方不会也不可能拿到你的源码去project依赖。这里我分享一个联调测试的小技巧在App的build.gradle里用变量控制依赖来源。def useLocalLib true dependencies { if (useLocalLib) { implementation project(:mylibrary) } else { implementation com.example:mylibrary:1.0.0 } }这样你在本地联调时可以切到源码依赖出了AAR包后切到Maven依赖做验收。两个模式都覆盖到避免发布后发现打包行为和源码行为不一致。6.3 用Maven坐标依赖时对方如何查看源码有经验的安卓开发者都会关注这一点依赖了Maven上的AAR后在Android Studio里能不能跳转到源码、看到注释和文档。前提是你的AAR发布到Maven时带了-sources.jar。如果带了Android Studio会自动下载并关联如果没带跳只能跳到反编译的class。所以发布时一定要生成sources.jar这既是给对接方看的“说明书”也是你SDK专业度的体现。另外一个细节是如果你的SDK还希望接管方看到文档注释建议给核心公开类写完整的Kotlin/JavaDoc生成javadoc jar一并发布。虽然现在很多团队不强制要求但外部对接时体验差距非常明显。7. AAR里嵌入第三方SDK时的特殊处理7.1 依赖冲突优先让给宿主App如果你的AAR是作为一个中间件SDK要同时对接支付、推送、地图等多个第三方SDK一定要控制好依赖的暴露面。最简单的判断标准是第三方SDK的版本会不会影响宿主App已有的功能。比如宿主App自己也在用支付SDK你的AAR也依赖了同一个支付SDK但版本不同Gradle会选择较高的版本但万一高版本API改了宿主App可能就编译不过。我的经验是第一原则能用compileOnly就尽量用compileOnly把第三方SDK留给宿主App自己声明非要用某个实现版本时在pom里明确写出来让宿主App有感知。7.2 多个AAR之间的内部依赖有些大SDK会拆成多个子AAR比如core.aar、ui.aar、network.aar它们之间有内部依赖。发布时要确保子AAR的pom文件里包含对core的依赖坐标各模块用同一个groupId和版本号方便统一升级。在打包和发布时可以用Gradle的maven-publish一次性发布多个Module。如果你的子AAR之间有api依赖那pom里会以compile作用域暴露出去如果用implementation则只在SDK内部可见不会暴露给App。做SDK设计时对外暴露的API模块推荐用api为了库内部使用的模块推荐用implementation这样既能保证对方调到自己需要的类又不至于被一堆内部实现类污染。7.3 动态库打包的后置处理如果你在AAR里集成了第三方预编译so更要注意命名空间冲突。Linux下so的符号是全局的如果两个库都包含了同一个so比如都集成了一份OpenSSL加载时可能符号互相覆盖导致严重崩溃。这时候的最好方案是自己编译so时把符号加前缀或者用dlopen动态加载并指定RTLD_LOCAL。但这些都属于高阶玩法了普通场景遇到这种问题的概率不高写在这里是提醒方向真遇到了别一根筋去排查Java层代码。8. 分享几个走了弯路才总结出来的小建议说点实在的。AAR打包本身不是高深技术但把AAR做得优雅、稳定、好接入才考验功底。以下是几条我个人的建议第一Library里尽量少用反射如果非用不可把所有反射入口类写进consumer-proguard文件的keep规则里。Release AAR默认会被使用者混淆你留着一个“看似没问题”的反射调用等到线上报错时排查成本极高。第二版本号管理要规范。发布AAR前检查versionCode和versionName不要在连调阶段用同一个版本号覆盖发布。很多团队出现“明明改了代码但对方说没效果”多半是版本号没变对方拉到了本地或Maven缓存中的旧包。第三Gradle缓存坑过不少人。如果你发布了一个同名同版本号的AAR对方依赖时会命中本地缓存导致永远拿不到新包。这时让对方在Android Studio里File - Invalidate Caches或者删除~/.gradle/caches/modules-2/files-2.1/下的对应目录再./gradlew build --refresh-dependencies。发布一个新版本号其实比清缓存简单得多所以养成习惯每次发布都用新版本号别覆盖旧包。第四AAR里的资源名、类名、方法名最好是加上模块前缀。不要觉得麻烦等第三方接入方的工程和你的库出现资源冲突时你就知道前缀的重要性了。第五对外交付时别只发一个AAR至少附上一份简单的接入文档、混淆规则说明、版本更新日志。哪怕只有几行字都比让对接方自己看反编译代码强得多。我自己的习惯是每次发版前先在本地走一遍“App依赖远程AAR”的完整流程确认能编译、能运行、混淆后功能正常再提交发布。这个流程看起来慢但能挡掉绝大部分线上问题。打包AAR这件事熟练后十分钟就能做完但把它打磨成一个别人用着省心的SDK才是真正的功夫所在。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。