[时间:2017-09] [状态:Open]
[关键词:Android,Android Studio,gradle,jar,aar,library]
0 引言
最近在工作中遇到了升级Android Studio 2.3.3稳定版之后,无法编译jar包的问题。之后寻找AS文档-探索 Android Studio发现。可以通过AS创建和编译jar包,顺便看到支持原生开发,可以直接在AS中调试c/c++代码,这是非常不错的功能。终于可以摆脱纯打日志的开发环境了。
本系列文章也就因此而出现。我希望阅读文本文之后,大家能够基本了解如何使用新版的Android Studio开发原生引用。
这里说明一点,本文是参考谷歌的Android Studio官网来的,也可以认为是一种翻译版。可以访问的话你可以直接查看对应内容。
这是第三篇:使用Android Studio创建库文件(Java)。(严格来说这跟原生开发没什么关系,但是为了完整的完成我最初的目标,还是整理下此文吧。)
1 环境准备
请参考本系列的第一篇:原生库示例创建及验证中的要求,安装和更新Android Studio。
2 Android库简介
Android库在结构上与app module等价。它可以包含所有用于构建app的东西,包括源码、资源文件和Android manifest。Android库不是直接编译成可在设备上执行的APK文件,而是编译为AAR(Android Archive)文件,你可以将该文件作为app module的依赖项。和JAR包不同的是,AAR文件可以包含Android资源和manifest文件,这就是说除了Java类和方法之外,AAr还支持打包诸如layouts和drawables之类的共享资源。
Android库模块在以下情况下很有用:
- 当你在构建多个使用相同组件的app时,比如Activity、service或UI layout。
- 当你基于多个APK变种构建app时,例如免费版和付费版,他们共享同样的核心组件。
在以上了两种情况中,只要简单的把需要重用的文件放到库模块中,然后在app module中添加该库作为依赖项就可以了。本文将告诉你如何实现这些。
3 新建库模块
AS中新建一个库模块,步骤如下:
- 点击菜单中的 File > New > New Module
- 在Create New Module窗口中,选择Android Library,点击Next。
- 这里也可以选择新建Java库,也就是构建传统的JAR包。
- 给你的库选择一个名字,并选择一个最低SDK版本,之后点击Finish。
当Gradle完成工程同步,库模块就会显示在工程面板中。
4 将现在app module转为库模块
可以参考下列步骤:
-
打开module层的build.gradle文件
-
删除applicationId这一行,这个标志只能在Android app module中定义。
-
在该文件的开头,你应该能看到下面内容:
apply plugin: 'com.android.application'
将其更改为:
apply plugin: 'com.android.library'
-
保存该文件,并点击菜单中的Tools > Android > Sync Project with Gradle Files。
这就搞定了。整个模块的结构是不变的,但现在它是以Android库运行的,构建时会新建一个AAR文件,而不是APK。
5 配置AAR作为依赖项
在其他app module中使用Android库时,需要按照下面方式配置:
- 使用以下两种方式将库添加到你的工程中(如果你在同一个工程中新建了库模块,你可以跳过此步骤):
- 添加编译好的AAR/JAR文件(该库必须已经构建好)
- 点击菜单中的File > New > New Module。
- 点击Import .JAR/.AAR Package,并点击Next。
- 输入编译好的AAR/JAR文件的路径,并点击Finish。
- 将库模块导入到你的工程(库源代码将成为你工程的一部分)
- 点击菜单中的File > New > Import Module.
- 输入库模块的目录,并点击Finish。
The library module is copied to your project, so you can actually edit the library code. If you want to maintain a single version of the library code, then this is probably not what you want and you should instead add the compiled AAR file as described above.
- 添加编译好的AAR/JAR文件(该库必须已经构建好)
- 检查下你的settings.gradle文件中新加入的库是否在其中,如下面所示添加了"my-library-module":
include ':app', ':my-library-module'
- 打开app module的build.gradle文件,并在dependencies块中添加一行,如下代码片段所示:
dependencies { compile project(":my-library-module") }
- 点击Sync Project with Gradle Files
在上面示例中,编译配置中添加了一个名为my-library-module的库作为整个app module的构建依赖项。如果你希望在特定构建变量上需要该库,不要使用compile,而是使用buildVariantNameCompile命令。例如,如果你只想在"pro"产品中包含该库,你需要这样编写:
productFlavors {
pro { ... }
}
dependencies {
proCompile project(":my-library-module")
}
通过上述设置之后,Android库中所有的代码和资源都可以在app module中访问了,在构建时,对应库的AAR文件会被打包到APK中。
如果你需要单独共享AAR文件,你可以在project-name/module-name/build/outputs/aar/中找到它,你可以通过菜单中的Build > Make Project重新构建它。
7 在库中发布非默认的variant
默认情况下,库模块仅发布和暴露"release"构建variant下的资源给其他Android工程/module。也就是说,如果app module将该库作为依赖项,即使在debug版本下的app也只能访问该库的"release"variant。然后,你可以通过在库的build.gradle文件中添加下面命令使得Gradle可以访问"debug"构建variant下的资源:
android {
...
// Sets the "debug" build variant as the default variant
// of the library that Gradle should publish.
defaultPublishConfig "debug"
}
这样配置之后,Gradle将发布"debug"构建variant下的资源。然而,如果库模块使用product flavors,你就需要配defaultPublishConfig属性,并使用全配置名来指定构建variant(否则,Gradle将不会发布你的库,因为传统的"release"及"debug" variant已经不存在了)。下面示例展示了如何组合使用 "demo" product flavor和"debug"构建类型的导出构建variant:
android {
...
defaultPublishConfig "demoDebug"
}
谨记:如果你正在构建用于发布app的release版本,你需要修改defaultPublishConfig以使用库的release variant对应的资源。同样的,你可以设置Gradle来发布和暴露库中所有可用的variant,同时每个app variant仅使用其需要variant的资源。在库的build.gradle文件中添加以下行可以让Gradle发布库中所有的variants:
android {
...
// Tells Gradle to build all variants of the library. Note that this
// may increase build times because Gradle must build multiple AARs,
// instead of only one.
publishNonDefault true
}
现在你可以通过修改你的app的build.gradle文件的dependencies块来访问库中所有可见的variants。下面位于app module的build.gradle文件中的代码片段指示Gradle在构建app的"demoDebug"版本时使用库的"demoDebug" variant,并在构建app的"fullRelease"版本时使用"fullRelease" variant。
android {...}
...
// Creates Gradle dependency configurations to use in the dependencies block.
configurations {
// Initializes placeholder configurations that the Android plugin can use when targeting
// the corresponding variant of the app.
demoDebugCompile {}
fullReleaseCompile {}
...
}
dependencies {
// If the library configures multiple build variants using product flavors,
// you must target one of the library's variants using its full configuration name.
demoDebugCompile project(path: ':my-library-module', configuration: 'demoDebug')
fullReleaseCompile project(path: ':my-library-module', configuration: 'fullRelease')
...
}
8 选择作为public的资源
默认情况下库中所有的资源都是public的。为了将所有资源默认设置为private,你需要至少定义一个特殊属性作为public。资源包括你的工程下的res目录中的所有文件。为了阻止库的用户访问仅供内部使用的资源,你需要使用自动private设计机制,声明一个或多个public资源。同时,你可以把所有资源设置为private,只要添加一个空的<public />
标签即可,这个标签主要是标记不存在public资源,也就是所有资源都是private的。
为了声明一个public资源,在库的public.xml中添加一个
下面示例代码创建了两个public字符串:mylib_app_name和mylib_public_string:
<resources>
<public name="mylib_app_name" type="string"/>
<public name="mylib_public_string" type="string"/>
</resources>
你需要保证中那些使用你的库的开发者可以访问的资源设置为public。例如,v7 appcompat库中大多数资源是private的,但是控制Toolbar widget的属性是public的,以支持material design。
隐式地将属性设置为private不仅可以防止库的用户得到内部库资源的代码自动补全提示,而且允许你在不破坏客户端的前提下重命名或删除private资源。private资源会被代码自动补全和theme editor自动过滤掉,并且Lint在你引用private资源时会给出警告信息。
在构建库时,Android Gradle插件将会获取public资源定义,并将其提取到public.txt文件中,此文件会被打包到AAR文件中。
9 开发考量
在你开发库模块和依赖库的app时,请留心以下行为和限制。
一旦在Android app module中添加了对库模块的引用,你就可以设置其相对优先级。在编译时,库模块会一个个合并到app中,按照优先级的高低顺序合并。
- 资源合并冲突
构建工具会将库模块的资源和其所依附的app module进行合并。如果给定的资源ID在两个module中都有定义,则使用app中的资源。
如果在多个AAR库中发生冲突,那么在dependencies列表中第一个出现(在dependencies块的顶部)的资源会被使用。
为了避免通用资源ID的冲突,可以考虑使用前缀或者统一命名方案,以保证每个module的名字都是唯一的(或在整个工程module中是唯一的)。 - 库模块可以包含JAR包
你可以开发一个本身包含JAR包的库模块;然而,你需要对依赖该库的app module进行手工编辑构建路径,并添加该JAR包所在路径。 - 库模块可以依赖外部的JAR包
你可以开发一个依赖外部库的库模块。(例如,Maps外部库)。在这种情况下,依赖该库的app在构建时必须使用一个包含该外部库(例如,Google API Add-on)的target。需要注意的是,在库模块和app module的manifest文件中必须声明该外部库,使用元素。 - 库模块不能包含原始asset
在库模块中,工具并不支持使用原始asset文件。app中使用的任何asset资源必须保存在其assets/目录下。 - app module的minSdkVersion必须大于等于库中定义的版本。
一个库被编译为app module的一部分,所以在库中使用的API必须与app module支持的平台版本兼容。 - 每个库模块都可以创建单独的R类
在你构建有库依赖的app module时,库模块首先被编译到AAR文件中,然后添加到app module中。因此,每个库都有其自己的R类,其命名是按照库的包名确定的。从主模块和库模块生成的R类在所有包中创建,这些都是会包含在主模块包中和库的包中。 - 库模块可以包含自己的ProGuard配置文件
你可以在库上实现代码缩减,通过在你的库中添加ProGuard配置文件,其中包含了ProGuard指令。构建工具会把这个文件嵌入到生成的AAR文件中。当你把该库添加到app module之后,库的ProGuard文件会被添加到app Module的ProGuard配置文件(proguard.txt)中。
通过在你的库模块中嵌入一个ProGuard文件,你可以确保那些依赖于该库的app module在使用你的库是无需手动更新他们的ProGuard文件。当ProGuard在Android app module上执行时,它会自动使用来自app module和库中的指令,这样你就不用单独执行库中的配置文件了。
为了指定你的库配置文件的名字,在consumerProguardFiles方法中添加下参数,该方法在你的库的build.gradle文件中的defaultConfig块中。例如,下面代码段将lib-proguard-rules.txt作为该库的ProGuard配置文件:
android {
defaultConfig {
consumerProguardFiles 'lib-proguard-rules.txt'
}
...
}
为了保证你的库中的ProGuard规则不会对app module中的代码缩减产生副作用,只包含那些在你的库中不工作的禁用ProGuard功能的规则。那些用于辅助开发者的规则可能会与app module或其他库有冲突,因此不可以包含类似规则。例如,你库的ProGuard文件可以指定在app module最小化时那些代码可以保留。
- 测试库模块和测试app一样
主要区别在于库和它的依赖项是自动作为测试APK的依赖项的。这意味着,测试APK包含了不仅仅是它自身的代码,还有库的AAR及库的依赖项。因为并不存在独立的“测试下的app”,androidTest任务仅安装(和卸载)测试APK。
在合并多个manifest文件时,Gradle将遵循默认优先级规则,并将库的manifest依次合并到测试APK的主manifest中。
10 AAR文件剖析
AAR文件的文件扩展名是.aar,对应的Maven生成的类型也应该是aar。该文件本身是一个zip文件,包含下列必须条目:
/AndroidManifest.xml
/classes.jar
/res/
/R.txt
/public.txt
另外,AAR文件可以包含一个或多个下列可选条目:
/assets/
/libs/name.jar
/jni/abi_name/name.so (where abi_name is one of the Android supported ABIs)
/proguard.txt
/lint.jar
11 小结及后续
本文作为Android创建的第三篇,整体比较简单,内容主要是翻译部分,整理并介绍了如何使用AS构建JAR包。仅供后续参考。