引言

Android studio没有像Eclipse一样提供便捷的图形界面打包成Jar包,而且也没有默认的打包Jar包的gradle脚本,如果需要把一个库Module打包成Jar包,需要自己在对应的库Module下对应的gradle.build脚本编写相应的gradle脚本

一、jar包和aar包概述

本质上无论是jar或者aar包都是压缩文件,和普通的压缩文件无本质差别,至于后缀那是Windows系统下的设计为了便于找到指定的软件打开不同后缀的文件,这也是我们可以把jar、aar等这些后缀改成.zip或者rar也可以用压缩软件打开不报错的原因,也正是因为才有加密加固dex的需求。但是jar和aar来说又还是有一些细微的区别的。

1、jar

jar包是Java开发时期的产物,而在Android环境下主要是包含class字节码文件和清单文件没有res、asset等Android资源文件

androidstudio打包lib androidstudio打包zip_Android Studio

2、aar

Android 库在结构上与 Android 应用 模块相同,它可以提供构建应用所需的几乎一切内容包括:class字节码文件、libs库、jni库、aidl文件、res、assets资源文件和 Android 清单

androidstudio打包lib androidstudio打包zip_AAR_02

二、Android Studio库项目打包成jar

像平常一个样新建一个库项目,然后配置编写的Gradle脚本

1、添加上打包成Jar包的Task前的gradle

apply plugin: 'com.android.library'

android {
    compileSdkVersion 25
    buildToolsVersion '25.0.2'

    defaultConfig {
        minSdkVersion 9
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}
dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    compile files('libs/commons-codec-1.3.jar')
    compile files('libs/commons-lang-2.4.jar')
}

2、添加上打包成Jar包的Task后的gradle

apply plugin: 'com.android.library'

android {
    compileSdkVersion 25
    buildToolsVersion '25.0.2'

    defaultConfig {
        minSdkVersion 9
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

task clearJar(type: Delete) {
    delete "$rootProject.projectDir/demousingsdk/libs/"
    delete "$rootProject.projectDir/sdk_demo_online/libs/"
}
task copyFileToProject1(type: Copy) {
    from('build/intermediates/bundles/debug/')
    into("$rootProject.projectDir/demousingsdk/libs/")
    include('classes.jar')
    rename('classes.jar', 'xiaoi_sdk.jar')
}
task copyFileToProject2(type: Copy) {
    from('build/intermediates/bundles/debug/')
    into("$rootProject.projectDir/sdk_demo_online/libs/")
    include('classes.jar')
    rename('classes.jar', 'xiaoi_sdk.jar')
}
dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    compile files('libs/commons-codec-1.3.jar')
    compile files('libs/commons-lang-2.4.jar')
}
//定义makeJar任务打包jar包 start
task makeJar(type: Copy){
    delete 'build/libs/xiaoi_sdk.jar'//先删除存在的jar包
    from('build/intermediates/bundles/release/')//
    into('build/libs/')//打包后生成的jar包的存放目录
    include('classes.jar')//include、exclude参数过滤,这里只关注classes.jar
    rename ('classes.jar', 'xiaoi_sdk.jar')
}
//定义makeJar任务打包jar包 end
makeJar.dependsOn(clearJar, build, copyFileToProject1, copyFileToProject2)

三、Android Studio库项目打包成aar

Android 库将编译到您可以用作 Android 应用模块依赖项的 Android 归档 (aar) 文件,而不是在设备上运行的 APK。而且库模块非常适合以下情形:

  • 构建使用某些相同组件(例如 Activity、服务或 UI 布局)的多个应用。
  • 构建存在多个 APK 变体(例如免费版本和付费版本)的应用并且需要在两种版本中使用相同的核心组件。

处理以上情形只需要将您希望重用的文件移动到库模块中,然后以依赖项的形式为每个应用模块添加库即可。

话说回来,aar是针对库模块的,所以打包aar的第一步就是要创建一个库模块 File > New > New Module>
在出现的 Create New Module 窗口中>依次点击 Android Library 和 Next
或者通过把应用模块下的 build.gradle 文件中的顶部apply plugin: ‘com.android.application’ 改为 apply plugin: 'com.android.library’把应用模块转为库模块最后点击 Sync Project with Gradle Files,就会在对应Module下的build文件夹下生成aar,当然也可以通过手动执行gradle task的方式打包生成aar如下:

androidstudio打包lib androidstudio打包zip_androidstudio打包lib_03

android {
    compileSdkVersion 26

    defaultConfig {
        minSdkVersion 19
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    task copyLibs(type: Copy) {
        from configurations.compile
        into 'libs'
    }

}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'com.android.support:appcompat-v7:26.1.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    implementation 'org.greenrobot:greendao:3.2.0'
    implementation files('libs/pinyin4j-2.5.0.jar')
    ///compile 'zhe.pinyin4j:2.5.1'
}

四、Android Studio库项目打包注意事项

在开发库模块和相关应用时,将库模块引用添加至 Android 应用模块后,可以通过设置它们的相对优先级来指定库按照一次一个的方式与应用按照从低到高的优先级顺序合并,除此之外,还需要注意以下事项:

1、资源合并冲突

构建工具会将库模块中的资源与相关应用模块的资源合并。但是如果在两个模块中均定义了相同的资源 ID,将会使用应用中的资源。如果多个 aar库之间发生冲突,将使用依赖项列表首先列出(位于 dependencies 块顶部)的库中的资源。所以为了避免常用资源 ID 的资源冲突,请使用在模块(或在所有项目模块)中具有唯一性的前缀或其他一致的命名方案。

2、库模块可以包含 jar库

您可以开发一个自身包含 jar库的库模块,只需要像普通应用模块一样引入jar即可。

3、库模块可以依赖外部 jar库

库模块也可以依赖于外部库,相关应用必须针对包含外部库的目标构建且库模块和相关应用都必须在其清单文件的 < uses- library > 元素中声明外部库。

4、库模块不得包含原始资源

不支持在库模块中使用原始资源文件(保存在 assets/ 目录中),所以应用使用的任何原始资源都必须存储在应用模块自身的 assets/ 目录中。

5、应用模块的 minSdkVersion 必须大于或等于库定义的版本

库作为相关应用模块的一部分编译,因此,库模块中使用的 API 必须与应用模块支持的平台版本兼容。

6、每个库模块都会创建自己的 R 类

在构建相关应用模块时,库模块将先编译到 aar 文件中,然后再添加到应用模块中。因此,每个库都有其自己的 R 类,并根据库的软件包名称命名。从主模块和库模块生成的 R 类会在所需的所有软件包(包括主模块的软件包和库的软件包)中创建。

7、库模块可能包含自己的 ProGuard 配置文件

库模块也是支持ProGuard的,你只需要把ProGuard配置文件添加到库的构建脚本中,打包编译的时候Gradle插件会自动应用你指定的ProGuard规则并嵌入到aar中,在将库添加到app模块时,库的ProGuard文件就会附加到app模块的ProGuard配置文件(proguard.txt)中,这样app模块在使用的时候就无须手动编写库的ProGuard文件就可以使用库了。自然当 ProGuard 在 Android 应用模块上运行时,它会同时使用来自应用模块和库的指令,因此不应当只在库上单独运行 ProGuard,还要指定您的库的配置文件名称,并将其添加到 consumerProguardFiles 方法中(位于您的库的 build.gradle 文件的 defaultConfig 块内)比如以下片段会将 lib-proguard-rules.txt 设置为库的 ProGuard 配置文件:

android {
    defaultConfig {
        consumerProguardFiles 'lib-proguard-rules.txt'
    }
    ...
}

但是,如果您的库模块是编译为APK且不生成aar的多模块构建的一部分,则应仅在使用库的app模块上运行ProGuard。而且合并多个清单文件时,Gradle将遵循默认优先级顺序,并将库的清单合并到测试APK的主清单中。

8、Gradle 目前并不支持打包静态库(.a)到aar中

9、flatDir repo导入aar文件,则还必须在项目中指定依赖项。

aar文件不包含传递依赖项,并且没有描述库使用的依赖项的pom文件。这意味着,如果使用flatDir repo导入aar文件,则还必须在项目app中指定依赖项。所以The best way to achieve it is to distribute the aar with a maven repo———解决aar 一劳永逸的最佳办法只有一个托管到远程代码Maven仓库。

五、引入aar 过程中遇到的坑小结

1、升级更新Android stdio的exploded-aar不见了的问题

前面一篇文章引入aar的正确姿势中介绍成功引入了会在 {module name}/build/intermediates/exploded-aar/ 生成对应的文件(如下图)但是在Android Studio 2.3 后,Gradle Plugin 也升级到 2.3.0,对应推荐使用的 Gradle 版本是 3.3。这时候会发现项目目录下 {module name}/build/intermediates/exploded-aar/ 目录没了,原来是官方以在 {user name}/.android/build-cache 下生成一部分缓存文件来代替 exploded-aar ,所以如果需要生成它,需要在项目目录下的 gradle.properties 配置android.enableBuildCache=false一行内容关闭BuildCache功能,然后重建项目即可在 {module name}/build/intermediates/ 看到 exploded-aar 目录了,如果想重新开启 BuildCache 功能,可以修改为 android.enableBuildCache=true 或者直接删除本行内容即可

androidstudio打包lib androidstudio打包zip_androidstudio打包lib_04

2、引入需要依赖第三方库的arr引起的NoClassDefFoundErro

前面所介绍aar集合可以包含所有源文件、jar包、libs、资源res和assets等,但请注意是几乎,因为Module中这些文件会随着项目的编译被打包进aar文件中,但是build.gradle中的引用库不会打包进aar文件中,如果你忽略了这个Module的build.gradle中的引用库,很可能引发NoClassDefFoundErro异常。

androidstudio打包lib androidstudio打包zip_打包Jar_05


那么要使用这样子的aar的话,你也需要在使用的时候手动进行对应的配置,比如说aar里引入了greeDAO那么你在使用的时候需要像自己引用的时候进行配置。

NoClassDefFoundError错误的发生,是因为Java虚拟机在编译时能找到合适的类,而在运行时不能找到合适的类导致的错误。例如在运行时我们想调用某个类的方法或者访问这个类的静态成员的时候,发现这个类不可用,此时Java虚拟机就会抛出NoClassDefFoundError错误。而与ClassNotFoundException的不同在于,这个错误发生只在运行时需要加载对应的类不成功,而不是编译时发生。简而言之就是NoClassDefFoundError发生在编译时对应的类可用,而运行时在Java的classpath路径中,对应的类不可用导致的错误

3、aar自身依赖复杂外部jar导致使用aar时候不能正确运行

所谓复杂jar指的是jar包里不仅仅有class文件还有aar 不支持的文件类型,导致在打包aar过程中会忽略掉不支持的文件类型,比如说xml,如下图

androidstudio打包lib androidstudio打包zip_AAR_06


所以The best way to achieve it is to distribute the aar with a maven repo———解决aar 一劳永逸的最佳办法只有一个托管到远程代码Maven仓库,尽量通过远程代码仓库引入第三方库。