如何构建带有Android Library Project的工程
来源:互联网 发布:鹊桥采集软件免费 编辑:程序博客网 时间:2024/06/06 02:47
随着工程越来越复杂,项目越来越多,以及平台的迁移(我最近就迁了2回),还有各大市场的发布,自动化编译android项目的需求越来越强烈,后面如果考虑做持续集成的话,会更加强烈。
经过不断的尝试,在ubuntu环境下,以花界为例,我将一步一步演示如何使用命令行,使用ant编译android项目,打包多渠道APK。
要点:
(1). 编译android的命令使用
(2). ant基本应用
(3). 多项目如何编译(包含android library)
(4). 如何多渠道打包
ps:我将以最原始的方式来实现,而不是使用android自带的ant编译方式,并尽量详细解释,这样有益于我们彻底搞懂android打包的基本原理。
1. Android编译打包的整体过程
使用ant,ant的参考文档:http://ant.apache.org/manual/index.html
首先,假设现在已经有这样的一个项目(多工程的,简单的单工程就更简单了):
world
├── baseworld //android library,基础类库,共享于其他主应用
├── floworld //android project,花界应用
├── healthworld //android project,健康视线应用
├── speciality //android project,其它应用
├── starworld //android project,其它应用
├── build.xml //ant编译脚本,可用于整个项目的编译,也可只编译某个工程
├── code_checks.xml
├── kaiyuanxiangmu_world.keystore //密钥
└── README.md
一个大的项目world,下面有1个基础Android Library和4个Android Project。我们要做的就是编译这4个人project成对应的一系列各市场APK。
那么我们在来看看baseworld和floworld的工程结构:
Android Library,baseworld:
baseworld
├── assets //assets目录,其中文件可能会被主应用覆盖
├── libs //存放第三方jar库
├── res //类库资源,其中文件可能会被主应用覆盖
├── src //源码,可直接供主应用使用
├── AndroidManifest.xml
├── lint.xml
├── proguard.cfg
├── project.properties
└── README.md
和Android Project,floworld:
floworld/
├── assets //assets目录,主应用优先级高
├── build
├── data
├── libs //存放第三方jar库
├── res //主应用资源,主应用优先级高
├── src //源码,可直接供主应用使用
├── AndroidManifest.xml
├── build.xml //ant编译脚本,可用于整个项目的编译,也可只编译某个工程
├── default.properties
├── lint.xml
├── proguard.cfg
├── project.properties
└── README.md
结构已经出来了,那么android打包主要是在做什么?
说白了,先编译java成class,再把class和jar转化成dex,接着打包aaset和res等资源文件为res.zip(以res.zip示例),再把dex和res.zip合并为一个未签名apk,再对它签名,最终是一个带签名的apk文件。
当然这么说忽略了很多细节。
下面我把这些步骤用一句话分别列举如下,脑子里先有一个整体的流程,后续再结合ant详细展开:
(1). 生成用于主应用的R.java;
(2). 生成用于库应用的R.java(如果有库应用);
(3). 编译所有java文件为class文件;
(4). 打包class文件和jar包为classes.dex;
(5). 打包assets和res资源为资源压缩包(如res.zip,名字可以自己定义);
(6). 组合classes.dex和res.zip生成未签名的APK;
(7). 生成有签名的APK;
针对多项目同步发布和多渠道打包问题,我们需要额外增加三个处理:
(1). 各个工程下建立一个build.xml,然后在整个项目的根目录下建立一个build.xml,用于统一编译各个工程的;
(2). 各个工程的build.xml,通过传入市场ID和应用Version参数生成对应的版本
(3). 针对(1),(2)问题,建立一个批处理支持一键生成所有版本
大概流程即是如此。
2. 建立各个工程的ant脚本文件build.xml(位置:floworld/build.xml)
因为需要创建一些基本的文件目录和清理上次生成的文件,所以我们简单的定义一下几个目标吧:init,main,clean。
代码模板如下:
<
project
default
=
"main"
basedir
=
"."
>
<!-- 初始化:创建目录,清理目录等 -->
<
target
name
=
"init"
>
<
echo
>start initing ... </
echo
>
<!-- ... ... -->
<
echo
>finish initing. </
echo
>
</
target
>
<!-- 打包过程,默认值 -->
<
target
name
=
"main"
depends
=
"init"
>
</
target
>
<!-- 清理不需要的生成文件等-->
<
target
name
=
"clean"
>
</
target
>
</
project
>
3. 初始化
在正式打包之前,有必要说明一下可能需要用到的初始化变量和操作。
前面已经讲述了打包的大概流程,现在,第一, 打包需要你使用哪个版本android.jar; 第二, 生成的R文件放到gen目录下; 第三, 生成的classes文件放到bin目录下; 第四, 生成的打包文件放到out目录下; 第五, 生成的各市场版本放到build目录下。目录完全可以自定义。
所以,如下的初始化必须先要做好,不然后面会提示找不到目录:
<
project
default
=
"main"
basedir
=
"."
>
<!-- 这个是android.jar路径,具体情况具体配置 -->
<
property
name
=
"android-jar"
value
=
"/usr/lib/android-sdk/platforms/android-10/android.jar"
/>
<!-- 用于生成多渠道版本的APK文件名,提供了默认值,后面会讲到 -->
<
property
name
=
"apk-name"
value
=
"product"
/>
<
property
name
=
"apk-version"
value
=
"latest"
/>
<
property
name
=
"apk-market"
value
=
"dev"
/>
<
target
name
=
"init"
>
<
echo
>start initing ... </
echo
>
<
mkdir
dir
=
"out"
/>
<
delete
>
<
fileset
dir
=
"out"
></
fileset
>
</
delete
>
<
mkdir
dir
=
"gen"
/>
<
delete
>
<
fileset
dir
=
"gen"
></
fileset
>
</
delete
>
<
mkdir
dir
=
"bin/classes"
/>
<
delete
>
<
fileset
dir
=
"bin/classes"
></
fileset
>
</
delete
>
<!-- ${apk-version}表示版本,后面会详细讲到 -->
<
mkdir
dir
=
"build/${apk-version}"
/>
<
echo
>finish initing. </
echo
>
</
target
>
... ...
</
project
>
4. 生成R.java
Android Library和Android Project应用的R.java是来自不同的package的。比如:
(1). baseworld中导入的包是import com.tianxia.lib.baseworld.R;
(2). floworld中导入的包是import com.tianxia.lib.baseworld.R;
但是他们最终是调用统一的资源,所以这两个R.java文件必须一致。
下面是主应用的R.java的生成脚本:
<
echo
>generating R.java for project to dir gen (using aapt) ... </
echo
>
<
exec
executable
=
"aapt"
>
<
arg
value
=
"package"
/>
<!-- package表示打包-->
<
arg
value
=
"-m"
/>
<!--m,J,gen表示创建包名的目录和R.java到gen目录下 -->
<
arg
value
=
"-J"
/>
<
arg
value
=
"gen"
/>
<
arg
value
=
"-M"
/>
<!-- M指定AndroidManifest.xml文件-->
<
arg
value
=
"AndroidManifest.xml"
/>
<
arg
value
=
"-S"
/>
<!-- S指定res目录,生成对应的ID,可多个-->
<
arg
value
=
"res"
/>
<
arg
value
=
"-S"
/>
<
arg
value
=
"../baseworld/res"
/>
<!-- 注意点:同时需要调用Library的res-->
<
arg
value
=
"-I"
/>
<!-- I指定android包的位置-->
<
arg
value
=
"${android-jar}"
/>
<
arg
value
=
"--auto-add-overlay"
/>
<!-- 这个重要,覆盖资源,不然报错-->
</
exec
>
注意res和../baseworld/res两个顺序不能搞反,写在前面具有高优先级,我们当然优先使用主应用的资源了,这样就能正确覆盖库应用的资源,实现重写。
库应用的R.java的生成脚本差不多,区别是指定库应用的AndroidManifest.xml,以用于生成的是不同的包和目录。
另外,aapt的使用中特别说明了,为了库应用的资源更好的可重用,库应用生成的R.java字段不需要修饰为final,加上参数--non-constant-id即可。
<
echo
>generating R.java for library to dir gen (using aapt) ... </
echo
>
<
exec
executable
=
"aapt"
>
<
arg
value
=
"package"
/>
<
arg
value
=
"-m"
/>
<
arg
value
=
"--non-constant-id"
/>
<!-- 加了这个参数-->
<
arg
value
=
"--auto-add-overlay"
/>
<
arg
value
=
"-J"
/>
<
arg
value
=
"gen"
/>
<
arg
value
=
"-M"
/>
<
arg
value
=
"../baseworld/AndroidManifest.xml"
/>
<!-- 库应用的manifest-->
<
arg
value
=
"-S"
/>
<
arg
value
=
"res"
/>
<
arg
value
=
"-S"
/>
<
arg
value
=
"../baseworld/res"
/>
<
arg
value
=
"-I"
/>
<
arg
value
=
"${android-jar}"
/>
</
exec
>
这样的话就可以生成2个正确的R.java文件了(如果你引用了两个库,则需要生成3个R.java,以此类推)。
结果如下:
gen
└── com
└── tianxia
├── app
│ └── floworld
│ └── R.java
└── lib
└── baseworld
└── R.java
5. 编译java文件为class文件
使用javac命令把src目录,baseworld/src目录,gen/*/R.java这些java编译成class文件:
命令原型是:
//示例
javac -bootclasspath <android.jar> -s <src> -s <src> -s <gen> -d bin/classes *.jar
转化成ant脚本为:
<!-- 第三方jar包需要引用,用于辅助编译 -->
<
path
id
=
"project.libs"
>
<
fileset
dir
=
"libs"
>
<
include
name
=
"*.jar"
/>
</
fileset
>
</
path
>
<
echo
>compiling java files to class files (include R.java, library and the third-party jars) ... </
echo
>
<!-- 生成的class文件全部保存到bin/classes目录下 -->
<
javac
destdir
=
"bin/classes"
bootclasspath
=
"${android-jar}"
>
<
src
path
=
"../baseworld/src"
/>
<
src
path
=
"src"
/>
<
src
path
=
"gen"
/>
<
classpath
refid
=
"project.libs"
/>
</
javac
>
6. 打包class文件为classes.dex
这步简单,用dx命令把上步生成的classes和第三方jar包打包成一个classes.dex。
命令原型是:
//示例
//后面可以接任意个第三方jar路径
dx --dex --output=out/classes.dex bin/classes libs/
1
.jar libs/
2
.jar
转化成ant脚本为:
<
echo
>packaging class files (include the third-party jars) to calsses.dex ... </
echo
>
<
exec
executable
=
"dx"
>
<
arg
value
=
"--dex"
/>
<
arg
value
=
"--output=out/classes.dex"
/>
<!-- 输出 -->
<
arg
value
=
"bin/classes"
/>
<!-- classes文件位置 -->
<
arg
value
=
"libs"
/>
<!-- 把libs下所有jar打包 -->
</
exec
>
7. 打包res,assets为资源压缩包(暂且命名为res.zip)
还是使用aapt命令,如生成R.java最大的不同是参数-F,意思是生成res.zip文件。
命令原型和ant脚本差不多:
<
echo
>packaging resource (include res, assets, AndroidManifest.xml, etc.) to res.zip ... </
echo
>
<
exec
executable
=
"aapt"
>
<
arg
value
=
"package"
/>
<
arg
value
=
"-f"
/>
<!-- 资源覆盖重写 -->
<
arg
value
=
"-M"
/>
<
arg
value
=
"AndroidManifest.xml"
/>
<
arg
value
=
"-S"
/>
<
arg
value
=
"res"
/>
<
arg
value
=
"-S"
/>
<
arg
value
=
"../baseworld/res"
/>
<
arg
value
=
"-A"
/>
<!-- 与R.java不同,需要asset目录也打包 -->
<
arg
value
=
"assets"
/>
<
arg
value
=
"-I"
/>
<
arg
value
=
"${android-jar}"
/>
<
arg
value
=
"-F"
/>
<!-- 输出资源压缩包 -->
<
arg
value
=
"out/res.zip"
/>
<
arg
value
=
"--auto-add-overlay"
/>
</
exec
>
8. 使用apkbuilder命令组合classes.dex,res.zip和AndroidManifest.xml为未签名的apk
apkbuilder命令能把class类,资源等文件打包成一个未签名的apk,原型命令和ant脚本类似:
<
echo
>building unsigned.apk ... </
echo
>
<
exec
executable
=
"apkbuilder"
>
<
arg
value
=
"out/unsigned.apk"
/>
<!-- 输出 -->
<
arg
value
=
"-u"
/>
<!-- u指创建未签名的包-->
<
arg
value
=
"-z"
/>
<!-- 资源压缩包 -->
<
arg
value
=
"out/res.zip"
/>
<
arg
value
=
"-f"
/>
<!-- dex文件 -->
<
arg
value
=
"out/classes.dex"
/>
</
exec
>
这个命令比较简单。
9. 签名未签名的apk
使用jarsigner命令对上步中产生的apk签名。这是个传统的java命令,非android专用。
原型命令和ant脚本差不多:
<!-- 生成apk文件到build目录下 -->
<!-- 其中${apk-version/name/market}用户多渠道打包,后面会讲到 -->
<
echo
>signing the unsigned apk to final product apk ... </
echo
>
<
exec
executable
=
"jarsigner"
>
<
arg
value
=
"-keystore"
/>
<
arg
value
=
"../xxx.keystore"
/>
<
arg
value
=
"-storepass"
/>
<
arg
value
=
"xxx"
/> <-- 验证密钥完整性的口令,创建时建立的 -->
<
arg
value
=
"-keypass"
/>
<
arg
value
=
"xxx"
/> <-- 专用密钥的口令,就是key密码 -->
<
arg
value
=
"-signedjar"
/>
<
arg
value
=
"build/${apk-version}/${apk-name}_${apk-version}_${apk-market}.apk"
/>
<!-- 输出 -->
<
arg
value
=
"out/unsigned.apk"
/>
<!-- 未签名的apk -->
<
arg
value
=
"xxx"
/>
<!-- 别名,创建时建立的 -->
</
exec
>
至此,完整具有打包功能了,最后的build.xml为:
<
project
default
=
"main"
basedir
=
"."
>
<
property
name
=
"apk-name"
value
=
"product"
/>
<
property
name
=
"apk-version"
value
=
"latest"
/>
<
property
name
=
"apk-market"
value
=
"dev"
/>
<
property
name
=
"android-jar"
value
=
"/usr/lib/android-sdk/platforms/android-10/android.jar"
/>
<
target
name
=
"init"
>
<
echo
>start initing ... </
echo
>
<
mkdir
dir
=
"out"
/>
<
delete
>
<
fileset
dir
=
"out"
></
fileset
>
</
delete
>
<
mkdir
dir
=
"gen"
/>
<
delete
>
<
fileset
dir
=
"gen"
></
fileset
>
</
delete
>
<
mkdir
dir
=
"bin/classes"
/>
<
delete
>
<
fileset
dir
=
"bin/classes"
></
fileset
>
</
delete
>
<
mkdir
dir
=
"build/${apk-version}"
/>
<
echo
>finish initing. </
echo
>
</
target
>
<
target
name
=
"main"
depends
=
"init"
>
<
echo
>generating R.java for project to dir gen (using aapt) ... </
echo
>
<
exec
executable
=
"aapt"
>
<
arg
value
=
"package"
/>
<
arg
value
=
"-m"
/>
<
arg
value
=
"-J"
/>
<
arg
value
=
"gen"
/>
<
arg
value
=
"-M"
/>
<
arg
value
=
"AndroidManifest.xml"
/>
<
arg
value
=
"-S"
/>
<
arg
value
=
"res"
/>
<
arg
value
=
"-S"
/>
<
arg
value
=
"../baseworld/res"
/>
<
arg
value
=
"-I"
/>
<
arg
value
=
"${android-jar}"
/>
<
arg
value
=
"--auto-add-overlay"
/>
</
exec
>
<
echo
>generating R.java for library to dir gen (using aapt) ... </
echo
>
<
exec
executable
=
"aapt"
>
<
arg
value
=
"package"
/>
<
arg
value
=
"-m"
/>
<
arg
value
=
"--non-constant-id"
/>
<
arg
value
=
"--auto-add-overlay"
/>
<
arg
value
=
"-J"
/>
<
arg
value
=
"gen"
/>
<
arg
value
=
"-M"
/>
<
arg
value
=
"../baseworld/AndroidManifest.xml"
/>
<
arg
value
=
"-S"
/>
<
arg
value
=
"res"
/>
<
arg
value
=
"-S"
/>
<
arg
value
=
"../baseworld/res"
/>
<
arg
value
=
"-I"
/>
<
arg
value
=
"${android-jar}"
/>
</
exec
>
<
path
id
=
"project.libs"
>
<
fileset
dir
=
"libs"
>
<
include
name
=
"*.jar"
/>
</
fileset
>
</
path
>
<
echo
>compiling java files to class files (include R.java, library and the third-party jars) ... </
echo
>
<
javac
destdir
=
"bin/classes"
bootclasspath
=
"${android-jar}"
>
<
src
path
=
"../baseworld/src"
/>
<
src
path
=
"src"
/>
<
src
path
=
"gen"
/>
<
classpath
refid
=
"project.libs"
/>
</
javac
>
<
echo
>packaging class files (include the third-party jars) to calsses.dex ... </
echo
>
<
apply
executable
=
"dx"
>
<
arg
value
=
"--dex"
/>
<
arg
value
=
"--output=out/classes.dex"
/>
<
arg
value
=
"bin/classes"
/>
<
arg
value
=
"libs"
/>
</
apply
>
<
echo
>packaging resource (include res, assets, AndroidManifest.xml, etc.) to res.zip ... </
echo
>
<
exec
executable
=
"aapt"
>
<
arg
value
=
"package"
/>
<
arg
value
=
"-f"
/>
<
arg
value
=
"-M"
/>
<
arg
value
=
"AndroidManifest.xml"
/>
<
arg
value
=
"-S"
/>
<
arg
value
=
"res"
/>
<
arg
value
=
"-S"
/>
<
arg
value
=
"../baseworld/res"
/>
<
arg
value
=
"-A"
/>
<
arg
value
=
"assets"
/>
<
arg
value
=
"-I"
/>
<
arg
value
=
"${android-jar}"
/>
<
arg
value
=
"-F"
/>
<
arg
value
=
"out/res.zip"
/>
<
arg
value
=
"--auto-add-overlay"
/>
</
exec
>
<
echo
>building unsigned.apk ... </
echo
>
<
exec
executable
=
"apkbuilder"
>
<
arg
value
=
"out/unsigned.apk"
/>
<
arg
value
=
"-u"
/>
<
arg
value
=
"-z"
/>
<
arg
value
=
"out/res.zip"
/>
<
arg
value
=
"-f"
/>
<
arg
value
=
"out/classes.dex"
/>
</
exec
>
<
echo
>signing the unsigned apk to final product apk ... </
echo
>
<
exec
executable
=
"jarsigner"
>
<
arg
value
=
"-keystore"
/>
<
arg
value
=
"xxx.keystore"
/>
<
arg
value
=
"-storepass"
/>
<
arg
value
=
"xxxx"
/>
<
arg
value
=
"-keypass"
/>
<
arg
value
=
"xxx"
/>
<
arg
value
=
"-signedjar"
/>
<
arg
value
=
"build/${apk-version}/${apk-name}_${apk-version}_${apk-market}.apk"
/>
<
arg
value
=
"out/unsigned.apk"
/>
<
arg
value
=
"xxx"
/>
</
exec
>
<
echo
>done.</
echo
>
</
target
>
</
project
>
在工程目录下运行ant:
$ant
Buildfile: build.xml
init:
[echo] start initing ...
[mkdir] Created dir: /home/openproject/world/floworld/build/latest
[echo] finish initing.
main:
[echo] generating R.java for project to dir gen (using aapt) ...
[echo] generating R.java for library to dir gen (using aapt) ...
[echo] compiling java files to class files (include R.java, library and the third-party jars) ...
[javac] Compiling 75 source files to /home/openproject/world/floworld/bin/classes
[javac] 注意:某些输入文件使用或覆盖了已过时的 API。
[javac] 注意:要了解详细信息,请使用 -Xlint:deprecation 重新编译。
[echo] packaging class files (include the third-party jars) to calsses.dex ...
[echo] packaging resource (include res, assets, AndroidManifest.xml, etc.) to res.zip ...
[echo] building unsigned.apk ...
[exec]
[exec] THIS TOOL IS DEPRECATED. See --help for more information.
[exec]
[echo] signing the unsigned apk to final product apk ...
[echo] done.
BUILD SUCCESSFUL
Total time: 28 seconds
成功的在build/latest目录下生成一个product_latest_dev.apk,这就是默认的生成的最终的APK,可以导入到手机上运行。
- 如何构建带有Android Library Project的工程
- android通过ant打包带有Library project和第三方jar包 的脚本实现
- android:关于主工程和library project
- android:关于主工程和library project
- android:关于主工程和library project
- android:关于主工程和library project
- android:关于主工程和library project
- android:关于主工程和library project
- 如何在Android Studio中导入eclipse的library project
- 如何在Android Studio中导入eclipse的library project
- Android Studio如何添加工程(project)为library(针对非gradle)
- Android Studio如何添加工程(project)为library(针对非gradle)
- Android Studio如何添加工程(project)为library(针对非gradle)
- Android Studio如何添加工程(project)为library(针对非gradle)
- (4.5.2.9)Android Studio如何添加工程(project)为library(针对非gradle)
- Android的Library工程
- Android的Library工程
- Android如何引用library工程
- 第二节:监督学习应用,梯度下降
- PHPExcel 导出
- MyBatis学习总结(五)——实现关联表查询
- jboss7下 Cipher 报异常JCE cannot authenticate the provider BC的解决方法
- Hibernate中的SQL查询
- 如何构建带有Android Library Project的工程
- 建立数据库链接 create database link
- eclipse集成tomcat
- 深入浅出 Java 8 Lambda 表达式
- jquery 根据id动态分配change,click事件等
- 腾讯-云CDN介绍
- 怎么解决eclipse报PermGen space异常的问题
- 在linux下,去除^M,将windows格式文件(dos文件)改为unix格式文件
- oracle 中文截断 '\0' 报错 ORA-29275