记SDK发布之Demo打包

某些情况发布SDK时,需要提供Demo的代码,通常将其打包成zip文件(如腾讯X5)。右键压缩一个文件夹是我们平时最常用的方法,Demo代码文件夹也可以通过右键压缩。在Demo.zip只需要提供一次的情况下,右键压缩无疑是最好最快的方法,当需要经常改动代码且频繁发布Demo.zip时,右键压缩可能就不是最好的解决方案了。下面将介绍如何借助Gradle打包Demo代码的方法。


项目结构

项目目录结构

我的Android项目目录结构如下所示:
.
├── app
├── build.gradle
├── demo
├── sdk
└── settings.gradle
整个项目包含三个模块,sdk是可以对外发布的库模块;app为主要业务模块,是公司对外发布的产品,可以编译出对应的apk;demo作为使用sdk的样例模块,依赖于sdk,合作方接入sdk时可以作为参考。

demo模块目录结构

demo模块目录结构如下所示:
.
├── build
├── build.gradle
├── demo.iml
├── proguard-rules.pro
└── src
├── androidTest
│ └── java
├── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── res
└── test
└── java
对demo文件进行打包时,只需要包含build.gradle、proguard-rules.pro和src/main/这三个文件或目录,如果存在单元测试,可以把src/androidTest/和src/test/两个目录也包含进去。

build.gradle文件中有如下依赖:

1
2
3
4
5
dependencies {
... ...
implementation project(':sdk')
... ...
}

对demo文件进行打包时,需要将implementation project(':sdk')替换为implementation files('libs/sdk.aar')或者implementation 'com.xxx.xxx:sdk:1.0.0'

demo.zip结构

为了使demo.zip解压后,可以直接使用AndroidStudio打开demo项目,demo压缩包至少应该包含如下文件或目录:
.
├── README.md
├── build.gradle
├── demo
│ ├── build.gradle
│ ├── libs
│ │ └── sdk-R.1.0.0.aar
│ ├── proguard-rules.pro
│ └── src
│ └── main
└── settings.gradle
其中README.md为文档,帮助sdk使用者快速上手;build.gradle和settings.gradle是AndroidStudio导入demo时必需的gradle文件;demo/libs/sdk-R.1.0.0.aar是通过编译sdk模块时生成的;demo/目录下的文件都是从原来demo模块里面直接拷贝过来的。
build.gradle文件内容为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.1'

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}

allprojects {
repositories {
google()
jcenter()
}
}

task clean(type: Delete) {
delete rootProject.buildDir
}

和新建项目时自动生成的build.gradle一样。settings.gradle文件内容也可以认为是自动生成的:

1
include ':demo'

现在已经了解原来项目的目录结构,和生成demo.zip所需要包含的文件夹以及文件内容,接下来需要完成拷贝文件/文件夹、生成文件、修改文件和压缩demo包的工作。

打包demo.zip

现在需要依次完成拷贝文件/文件夹、生成文件、修改文件和压缩demo包。

拷贝demo模块

在原项目的根目录下新建genDemo.gradle文件,然后在原项目的根目录下的build.gradle文件中添加如下代码:

1
apply from: './genDemo.gradle'

临时目录为原项目根目录下的SDKDemo,每次打包时都要清理这个目录,如同make clean一样:

1
2
3
4
5
6
String sdkDemoName = 'SDKDemo'
String demoProjectRootDir = sdkDemoName

task cleanDemo {
delete demoProjectRootDir
}

将原项目的demo模块下的文件拷贝到SDKDemo/demo目录下:

1
2
3
4
5
6
7
8
9
10
11
12
13
task copyDemoSoruceToSDKDemo(type: Copy) {
from('demo') {
exclude '**/*.iml'
exclude '**/*.gitignore'
exclude '**/build'
exclude '**/androidTest'
exclude '**/test'
exclude '**/build.gradle'
}
into demoProjectRootDir + "/demo"

includeEmptyDirs false
}

这里没有拷贝demo/build.gradle文件,因为要对它进行特殊处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
task genModuleBuildGradle {
File buildGradleFile = new File(demoProjectRootDir + '/demo/build.gradle')
if (!buildGradleFile.exists()) {
buildGradleFile.getParentFile().mkdirs()
buildGradleFile.createNewFile()
}
buildGradleFile.withWriter('UTF-8') { writer ->
new File(projectDir.absolutePath + '/demo/build.gradle').withReader('UTF-8') { reader ->
reader.eachLine {
if (it.contains("implementation project(':sdk')")) {
writer.writeLine(''' File[] libFiles = new File('demo/libs').listFiles()
for (File file : libFiles) {
implementation files("libs/${file.name}")
}'''
)
} else {
writer.writeLine(it)
}
}
}
}
}

这一步主要是将implementation project(':sdk')这一句替换为:

1
2
3
4
File[] libFiles = new File('demo/libs').listFiles()
for (File file : libFiles) {
implementation files("libs/${file.name}")
}'

拷贝sdk-1.0.0.aar

现在需要将sdk模块生成的aar文件拷贝到SDKDemo/demo/libs/目录下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ext {
sdkVersionName = '1.0.0'
sdkArtifactId = 'sdk'
}

task copySDKLibToSDKDemoLibs(type: Copy) {
from('sdk/build/outputs/aar') {
include '**/*.aar'
}
eachFile {
it.path = sdkArtifactId + '-' + sdkVersionName + ".aar"
}
into demoProjectRootDir + "/demo/libs"
}

当编译sdk模块时,生成的aar名字为sdk-release.aar,需要根据版本号重新命名。

生成项目gradle文件

如果要让AndroidStudio直接打开SDKDemo就可以运行,至少需要在SDKDemo目录下生成build.gradle和settings.gradle两个文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
task genSettingsGradle {
File settingsGradleFile = new File(demoProjectRootDir + '/settings.gradle')
if (!settingsGradleFile.exists()) {
settingsGradleFile.getParentFile().mkdirs()
settingsGradleFile.createNewFile()
}
settingsGradleFile.withWriter('UTF-8') { writer ->
writer.writeLine('''include ':demo' ''')
}
}

task genBuildGradle {
File buildGradleFile = new File(demoProjectRootDir + '/build.gradle')
if (!buildGradleFile.exists()) {
buildGradleFile.getParentFile().mkdirs()
buildGradleFile.createNewFile()
}
buildGradleFile.withWriter('UTF-8') { writer ->
writer.writeLine('''// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.1\'

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}

allprojects {
repositories {
google()
jcenter()
}
}

task clean(type: Delete) {
delete rootProject.buildDir
}
''')
}
}

这里生成的build.gradle和settings.gradle文件内容和新建项目时自动生成的两个文件内容基本一致。

打包SDKDemo

目前为止,SDKDemo目录下已经有了所有需要打包的文件:
.
├── build.gradle
├── demo
│ ├── build.gradle
│ ├── libs
│ │ └── sdk-1.0.0.aar
│ ├── proguard-rules.pro
│ └── src
│ └── main
└── settings.gradle
现在要做的是配置打包任务:

1
2
3
4
5
6
task zipDemo(type: Zip) {
from demoProjectRootDir
into sdkDemoName
baseName = sdkDemoName
destinationDir file('.')
}

为了避免多次使用gradlew执行前面定义的任务,现在把这些任务集合在一起:

1
2
3
4
5
6
7
8
9
10
tasks.create(name: "genDemo", dependsOn: [
cleanDemo,
copyReadMeToSDKDemo,
copyDemoSoruceToSDKDemo,
copySDKLibToSDKDemoLibs,
genModuleBuildGradle,
genSettingsGradle,
genBuildGradle,
zipDemo
])

最后只需要执行命令:

1
./gradlew sdk:assemble genDemo

就会在原项目根目录下生成SDKDemo.zip文件了,可以将SDKDemo.zip文件上传到服务器或者直接发给用户了。

如果到这里还无法理解或运用此方法,可以在这里查看完整的项目代码

总结

这篇文章主要介绍如何利用gradle在发布sdk时,如何方便快捷的打包demo模块的代码。学会如何使用gradle,在以后的工作学习中可以节省大量的开发时间来提升工作效率。

参考文档

[1] 记SDK发布