文章

05.AAR

05.AAR

AAR

https://developer.android.com/studio/projects/android-library?hl=zh-cn#aar-contents

什么是 AAR?

AAR 文件(名字来源于 Android Archive,见名知义,是一个 Android 库项目的二进制归档文件),这个不仅可以把 Java 文件给打进去,还包含 AndroidManifest.xml 和资源文件等,使用的话,直接引入一个 AAR 文件就足够了,非常方便。

AAR 文件的文件扩展名为 .aar,Maven 工件类型应该也是 aar。此文件本身是一个 zip 文件。唯一的必需条目是 /AndroidManifest.xml

如何生成 AAR 文件?

Build > Make Project 的方式重新生成文件

Android Studio 生成路径:
项目名称/模块名称/build/outputs/aar/中找到这个库项目的 AAR 文件

其实这个过程,也生成了对应的 JAR 包,文件在项目名称/模块名称 /build/intermediates/bundles/debug(release)/classes.jar,classes.jar 这个 library 对应的 JAR 包。

命令行构建:

1
2
3
4
5
// debug aar
./gradlew :libcommon:bundleDebugAar

// release aar
./gradlew :libcommon:bundleReleaseAar

AAR 文件的结构

AAR 文件的文件扩展名为 .aar,Maven 工件类型应该也是 aar。此文件本身是一个 zip 文件。唯一的必需条目是 /AndroidManifest.xml。

此外,AAR 文件可能包含以下一个或多个可选条目:

1
2
3
4
5
6
7
8
9
10
11
/classes.jar
/res/
/R.txt
/public.txt
/assets/
/libs/name.jar
/jni/abi_name/name.so(其中 abi_name 是 Android 支持的 ABI 之一)
/proguard.txt
/lint.jar
/api.jar
/prefab/(用于导出原生库)

根据打包的 Library Module 情况不同,打包出来的 AAR 文件也可能包含以下内容:

1
2
3
4
5
/assets/
/libs/名称.jar
/jni/abi 名称/名称.so(其中 abi 名称 是 Android 支持的 ABI 之一)
/proguard.txt
/lint.jar

9licf

项目中使用 AAR 文件的两种方式

本地使用

  1. 把 aar 文件放在目标 module 一个文件目录内,比如就放在 libs 目录内。
  2. 在目标 module 的 build.gradle 文件添加如下内容:
1
implementation fileTree(dir: "libs", include: ["*.jar,*aar"])

放在其他目录

1
 api fileTree(include: ['*.aar'], dir: "${rootDir}/aar")

远程 AAR 使用

远程的 AAR 使用的前提是有一个远程的库链接,然后通过 Gradle 下载依赖

1
2
3
dependencies {
    implementation 'com.android.support:support-v4:25.3.1'
}

AAR 使用的注意事项

1. 应用模块的 minSdkVersion 必须大于或等于 Library Module 中定义的版本

AAR 作为相关应用模块的一部分编译,因此,库模块中使用的 API 必须与应用模块支持的平台版本兼容,所以 AAR 中的 minSdkVersion 要尽量的设置低一点。

2. 资源合并问题

如果 aar 中有资源文件,集成过程中,很容易出现资源冲突,例如两个相同名字的图片什么的。为了避免这个问题,有两种方法,一是制定一个不用冲突的命名规范,二是 library Module 的 build.gradle 中设置资源前缀。第一种,嗯,全靠自觉,维护起来很难,推荐第二种解决方法。

1
2
3
android {
  resourcePrefix "<前缀>"
}

3. aar 的混淆

通过对 library Module 的混淆,可以打出一个混淆后的 aar 包,这样的话,就不用要求在依赖此 aar 的同时,还要手动添加对应的混淆规则了。

1
2
3
4
5
6
android {
    defaultConfig {
        consumerProguardFiles 'lib-proguard-rules.txt'
    }
    // ...
}

以上是默认的构建类型,如果 library Module 需要对不同的 buildType 做特别的处理,还需要专门设置。

  1. library Module 的 build.gradle 中设置 publishNonDefault = true,请注意,设置 publishNonDefault 会增加构建时间。
1
2
3
4
android {
    // ...
    publishNonDefault true
}
  1. app 的 build.gradle 中设置不同构建类型的处理。
1
2
3
4
dependencies {
    debugCompile project(path: ':library', configuration: 'debug')
    releaseCompile project(path: ':library', configuration: 'release')
}

在依赖本身就带有混淆规则的 AAR 的时候,AAR 的 ProGuard 文件将附加至 app 的 ProGuard 配置文件 (proguard.txt)。所以,aar 的混淆规则可能会影响 app,因此,制定 aar 的混淆规则的时候,也要谨慎一点,只包括自己的类即可,不要把范围设置太大。

AAR 存在的问题

aar 依赖三方库 aar 的问题

问题

  1. 低版本 AGP 将 module 发布成 aar 不会报错

使用 Android Studio 打包出来的 AAR ,不会将其依赖的三方库打包进去。

  1. 高版本的 AGP 用 maven-publish 插件将 module 发布成 aar 直接报错
1
2
Execution failed for task ':mylibrary1:bundleReleaseAar'.
> Direct local .aar file dependencies are not supported when building an AAR. The resulting AAR would be broken because the classes and Android resources from any local .aar file dependencies would not be packaged in the resulting AAR. Previous versions of the Android Gradle Plugin produce broken AARs in this case too (despite not throwing this error). The following direct local .aar file dependencies of the :mylibrary1 project caused this error: /Users/zengfansheng/Hacket/Workspace/GradleTest/mylibrary1/libs/alipaysdk-15.8.03.aar, /Users/zengfansheng/Hacket/Workspace/GradleTest/mylibrary1/libs/shanyan_sdk_v2.3.3.9.aar

举个例子,library Test 依赖了 okhttp,打包成了 Test.aar ,app 使用本地方式引用了 Test.aar,但是无法使用 okhttp,为了不报错,app 还需要添加 okhttp 依赖。

Google Android Studio 的负责人在 stackoverflow 上解释了 为什么 Android Studio 不能将多个依赖打包进一个 AAR 文件的原因,是因为将不同的 library 打包在一起,涉及到资源和配置文件智能合并,所以是个比较复杂的问题,同时也容易造成相同的依赖冲突。

解决

  1. android-fat-aar 三方插件,谨慎使用
  2. 使用 Maven 依赖

因为 library Module 上传 Maven 后,会生成 一个 .pom 文件,记录 library Module 的依赖。当 Gradle 依赖 Maven 上的这个库时,会通过 pom 文件下载对应依赖。如果不想要对应依赖的话,可以通过下面的方法关闭 Gradle 的依赖传递。

1
2
3
4
5
6
7
8
9
10
//正常依赖
implementation 'com.chemao.android:chemao-sdk:1.2.3'

//关闭全部依赖传递-方法1
implementation 'com.chemao.android:chemao-sdk:1.2.3@aar'

//关闭全部依赖传递-方法2
implementation('com.chemao.android:chemao-sdk:1.2.3') {
        transitive = false
}
  1. 发布到 mavenLocal 或本地的一个 repo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apply plugin: 'maven-publish'

publishing {
    publications {
        shanYanSdk(MavenPublication) {
            artifact("./libs/shanyan_sdk_v2.3.3.9.aar") {
                groupId 'com.chuanglan.shanyan'
                artifactId 'sdk'
                version '2.3.3.9'
            }
        }
    }

    repositories {
        maven {
            url "${rootDir}/repo"
        }
    }
}

// 1. 去除要发布的moduleA的本地aar依赖(如api fileTree(dir: "libs", include: ["*.jar", "*.aar"])),不然执行gradle task publishShanYanSdkPublicationToMavenRepository会编译出错
// 2. 定义maven-publish的配置
// 3. 通过gradle task publishShanYanSdkPublicationToMavenRepository就其发布到本地repo
// 4. 和remote方式一样依赖这个aar

然后通过 api “com.chuanglan.shanyan:sdk:2.3.3.9” 引用,这样 module 被打成 aar 后也可以依赖传递了;module 中的 aar 就不要通过 api fileTree(dir: "libs", include: ["*.jar", "*.aar"]) 引入了,否则就会出现前面提到 maven-publish 上传 aar 时的问题

ZipException, message: invalid entry size

本地打 debug aar 后,在其他机器出现下面问题,打 release aar 解决:

1
2
3
4
5
6
7
8
9
10
Caused by: java.lang.RuntimeException: Failed to transform '/Users/hacket/Learning/Workspaces/TheMonkeyKingAssistant/aar/libwidget__debug_a4ba4b8e-2021-02-02-10-23-20.aar' using Jetifier. Reason: ZipException, message: invalid entry size (expected 303262 but got 303367 bytes). (Run with --stacktrace for more details.)
Please file a bug at http://issuetracker.google.com/issues/new?component=460323.
        at com.android.build.gradle.internal.dependency.JetifyTransform.transform(JetifyTransform.kt:163)
        at org.gradle.api.internal.artifacts.transform.DefaultTransformer.transform(DefaultTransformer.java:189)
        at org.gradle.api.internal.artifacts.transform.DefaultTransformerInvocationFactory$TransformerExecution.execute(DefaultTransformerInvocationFactory.java:332)
        at org.gradle.internal.execution.steps.ExecuteStep.lambda$execute$1(ExecuteStep.java:33)
        at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:33)
        at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:26)
        at org.gradle.internal.execution.steps.CleanupOutputsStep.execute(CleanupOutputsStep.java:63)
        at org.gradle.internal.execution.steps.CleanupOutputsStep.execute(CleanupOutputsStep.java:35)

导入 AAR 报错 Failed to transform file ‘xxx.aar’ to match attributes {artifactType=jar}

1
2
Failed to transform file 'xxx.aar' to match attributes {artifactType=jar}
Failed to transform file 'xxx.aar' to match attributes {artifactType=android-manifest}

AAR 的工程有错误,一般不会,但是我就遇到了源码依赖可以正常跑起来,但是打包 AAR 就是跑不起来。经过一天的折腾,采用排除法从最小的项目结构开始构建然后最终定位到是由于asset 目录下存在中文名的文件导致工程在依赖 AAR 时解析失败了。

依赖问题 找不到 module

1
2
3
4
5
6
7
8
9
10
11
12
13
14
* What went wrong:
Could not determine the dependencies of task ':m_room:compileProductReleaseAidl'.
> Could not resolve all task dependencies for configuration ':m_room:productReleaseCompileClasspath'.
   > Could not resolve qsbk.app.libs:lib_common:1.0.0.
     Required by:
         project :m_room > qsbk.app.libs:lib_audioplayer:1.0.0
      > No matching configuration of project :lib_common was found. The consumer was configured to find an API of a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'release', attribute 'app' with value 'product', attribute 'org.jetbrains.kotlin.platform.type' with value 'androidJvm' but:
          - None of the consumable configurations have attributes.
   > Could not resolve project :lib_common.
     Required by:
         project :m_room > project :m_core
         project :m_room > project :m_core > project :lib_ui
      > No matching configuration of project :lib_common was found. The consumer was configured to find an API of a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'release', attribute 'app' with value 'product', attribute 'org.jetbrains.kotlin.platform.type' with value 'androidJvm' but:
          - None of the consumable configurations have attributes.

依赖情况:

1
2
api lib_audioplayer
implementation m_core, lib_strings

依赖树:

1
2
3
4
5
6
7
8
18:17:11 +--- project :m_room
18:17:11 |    +--- qsbk.app.libs:lib_audioplayer:1.0.0
18:17:11 |    |    +--- com.alibaba:arouter-api:1.5.0 (*)
18:17:11 |    |    +--- com.growingio.android:vds-android-agent:autotrack-2.9.4 (*)
18:17:11 |    |    +--- qsbk.app.libs:lib_common:1.0.0 -> project :lib_common (*)
18:17:11 |    |    \--- org.jetbrains.kotlin:kotlin-stdlib:1.4.10 (*)
18:17:11 |    +--- com.google.android:flexbox:2.0.1
18:17:11 |    \--- org.jetbrains.kotlin:kotlin-stdlib:1.4.10 (*)

解决:

1
2
api lib_audioplayer
implementation m_core, lib_strings, lib_common
本文由作者按照 CC BY 4.0 进行授权