首先说明一下,这个ant批量打包,这篇文章是我自己写的,但是里面的一些核心代码什么的,不是我原创的了,我现在写的这个只是简单的让大家知道怎么可以批量的导出不同关键代码的apk应用的.我这个批量导出apk是利用一个java代码运行的,而不是纯粹的运行ant自身的命令,配置那些繁琐的配置文件去导出apk的,因为我觉得那个麻烦,所以我就找了这个简单的导出apk应用的一个demo,但是其中也不是一帆风顺的,还是出现了很多的异常,在网上查的一些资料也是不全的,都是东拼西凑搞全的,我等会儿会上传一个正式的demo上去,废话不多说了,开始说正题了
1.首先我们要做的就是配置ant环境了,如果配置过了的话,就可以不用配置了
首先去这个下载链接去下载ant包,http://ant.apache.org/bindownload.cgi 下载过后解压即可,比如我解压至D:\Program Files\apache-ant-1.9.4,接下来就是配置环境变量了
再在path里配置这个%ANT_HOME%/bin; %ANT_HOME%/lib;
这样环境就配置好了,运行cmd命令
输入ant,有返回Build failed ,就说明你已经配置好环境了.
2.接下来就是把自己需要导出apk的项目加几个配置文件,有ant.properties,build.xml,local.properties,这几个重要的文件
andoid 项目结构
java 项目结构
其中ant.properties是要输入你的导出apk所需要的keystore的信息
# This file is used to override default values used by the Ant build system.
#
# This file must be checked into Version Control Systems, as it is
# integral to the build system of your project.
# This file is only used by the Ant script.
# You can use this to override default values such as
# 'source.dir' for the location of your java source folder and
# 'out.dir' for the location of your output folder.
# You can also use it define how the release builds are signed by declaring
# the following properties:
# 'key.store' for the location of your keystore and
# 'key.alias' for the name of the key to use.
# The password will be asked during the build when you use the 'release' target.
# the config file for batch package.
key.store=F:\\private_keystore\\demo.keystore
key.store.password=123456
key.alias=demo
key.alias.password=123456
</pre><p></p><p>build.xml</p><pre name="code" class="html"><?xml version="1.0" encoding="UTF-8"?>
<project name="<span style="color:#ff6666;">Project</span>" default="help">
<!-- 不知道怎么搞的,我这个project 的name 写的根本不跟我的项目名对应,它能导出apk ,我名字写跟项目相同却倒不出apk ,如果你们不同也倒不出apk的话,那你们写于项目名相同吧,
<span style="white-space:pre"> </span>反正我这个写不同是可以导出的 -->
<!-- The local.properties file is created and updated by the 'android' tool.
It contains the path to the SDK. It should *NOT* be checked into
Version Control Systems. -->
<property file="local.properties" />
<!-- The ant.properties file can be created by you. It is only edited by the
'android' tool to add properties to it.
This is the place to change some Ant specific build properties.
Here are some properties you may want to change/update:
source.dir
The name of the source directory. Default is 'src'.
out.dir
The name of the output directory. Default is 'bin'.
For other overridable properties, look at the beginning of the rules
files in the SDK, at tools/ant/build.xml
Properties related to the SDK location or the project target should
be updated using the 'android' tool with the 'update' action.
This file is an integral part of the build system for your
application and should be checked into Version Control Systems.
-->
<property file="ant.properties" />
<!-- if sdk.dir was not set from one of the property file, then
get it from the ANDROID_HOME env var.
This must be done before we load project.properties since
the proguard config can use sdk.dir -->
<property environment="env" />
<condition property="sdk.dir" value="${env.ANDROID_HOME}">
<isset property="env.ANDROID_HOME" />
</condition>
<!-- The project.properties file is created and updated by the 'android'
tool, as well as ADT.
This contains project specific properties such as project target, and library
dependencies. Lower level build properties are stored in ant.properties
(or in .classpath for Eclipse projects).
This file is an integral part of the build system for your
application and should be checked into Version Control Systems. -->
<loadproperties srcFile="project.properties" />
<!-- quick check on sdk.dir -->
<fail
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
unless="sdk.dir"
/>
<!--
Import per project custom build rules if present at the root of the project.
This is the place to put custom intermediary targets such as:
-pre-build
-pre-compile
-post-compile (This is typically used for code obfuscation.
Compiled code location: ${out.classes.absolute.dir}
If this is not done in place, override ${out.dex.input.absolute.dir})
-post-package
-post-build
-pre-clean
-->
<import file="custom_rules.xml" optional="true" />
<!-- Import the actual build file.
To customize existing targets, there are two options:
- Customize only one target:
- copy/paste the target into this file, *before* the
<import> task.
- customize it to your needs.
- Customize the whole content of build.xml
- copy/paste the content of the rules files (minus the top node)
into this file, replacing the <import> task.
- customize to your needs.
***********************
****** IMPORTANT ******
***********************
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
in order to avoid having your file be overridden by tools such as "android update project"
-->
<!-- version-tag: 1 -->
<import file="${sdk.dir}/tools/ant/build.xml" /> http://
<span style="white-space:pre"> </span>
</project>
local.properties
sdk.dir=D:\\Program Files\\android-sdk-windows
#这个就是你们的Android sdk的路径了
你们还需要复制清单文件AndroidManifest.xml,复制出一个AndroidManifest.xml.temp,在里面一个地方修改一下,如下图
把需要改变的值用占位符写一下
但是我们要改变我们的代码的话,我们就要通过这个键的名字获取这个值,通过如下代码即可
<pre name="code" class="java">public static String getChanel(Context ctx){
String CHANNELID="991";
try {
ApplicationInfo ai = ctx.getPackageManager().getApplicationInfo(
ctx.getPackageName(), PackageManager.GET_META_DATA);
Object value = ai.metaData.get("channel");
if (value != null) {
CHANNELID= value.toString();
}
} catch (Exception e) {
//
}
return CHANNELID;
}
</pre><p></p><p>然后就是要运行的java项目的配置了</p><p>AntTest.java</p><p></p><pre name="code" class="java">package com.cn.ant;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import org.apache.tools.ant.DefaultLogger;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.ProjectHelper;
/**
*
* @ClassName: AntTest
* @Description: Ant自动打包程序
* @author: jiyuren
* @time: 2014-7-24下午5:47:04
* @version: V1.0
*/
public class AntTest {
private Project project;
private final static String projectBasePath = "D:\\workspace\\workProject\\Jiyuren";// 要打包的项目根目录
private final static String copyApkPath = "D:\\apkout";// 保存打包之后的apk的根目录
private final static String signApk = "Project-release.apk";// 这里的文件名必须是准确的项目名!就是Project工程的bin目录下面的apk安装包的名字
private final static String reNameApk = "Jiyuren_";// 重命名之后的项目名称前缀(地图项目不用改)
private final static String placeHolder = "@market@";// 需要修改manifest文件的地方(占位符)
@SuppressWarnings("resource")
public static void main(String args[]) {
long startTime = 0L;
long endTime = 0L;
long totalTime = 0L;
Calendar date = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd:HH:mm:ss");
try {
System.out.println("---------ant批量自动化打包开始----------");
startTime = System.currentTimeMillis();
date.setTimeInMillis(startTime);
System.out.println("开始时间为:" + sdf.format(date.getTime()));
BufferedReader br = new BufferedReader(new FileReader("market.txt"));
String flag = null;
while ((flag = br.readLine()) != null) {
// 先修改manifest文件:读取临时文件中的@market@修改为市场标识,然后写入manifest.xml中
String tempFilePath = projectBasePath + File.separator
+ "AndroidManifest.xml.temp";
String filePath = projectBasePath + File.separator
+ "AndroidManifest.xml";
write(filePath, read(tempFilePath, flag.trim()));
// 执行打包命令
AntTest mytest = new AntTest();
mytest.init(projectBasePath + File.separator + "build.xml",
projectBasePath);
mytest.runTarget("clean");
mytest.runTarget("release");
// 打完包后执行重命名加拷贝操作
File file = new File(projectBasePath + File.separator + "bin"
+ File.separator + signApk);// bin目录下签名的apk文件
File renameFile = new File(copyApkPath + File.separator
+ reNameApk + flag + ".apk");
// 将打包好的apk重命名后移动到copyApkPath位置
boolean renametag = file.renameTo(renameFile);
System.out.println("rename------>" + renametag);
System.out.println("file ------>" + file.getAbsolutePath());
System.out.println("rename------>"
+ renameFile.getAbsolutePath());
}
System.out.println("---------ant批量自动化打包结束----------");
endTime = System.currentTimeMillis();
date.setTimeInMillis(endTime);
System.out.println("结束时间为:" + sdf.format(date.getTime()));
totalTime = endTime - startTime;
System.out.println("耗费时间为:" + getBeapartDate(totalTime));
} catch (Exception e) {
e.printStackTrace();
System.out.println("---------ant批量自动化打包中发生异常----------");
endTime = System.currentTimeMillis();
date.setTimeInMillis(endTime);
System.out.println("发生异常时间为:" + sdf.format(date.getTime()));
totalTime = endTime - startTime;
System.out.println("耗费时间为:" + getBeapartDate(totalTime));
}
}
public void init(String _buildFile, String _baseDir) throws Exception {
project = new Project();
project.init();
DefaultLogger consoleLogger = new DefaultLogger();
consoleLogger.setErrorPrintStream(System.err);
consoleLogger.setOutputPrintStream(System.out);
consoleLogger.setMessageOutputLevel(Project.MSG_INFO);
project.addBuildListener(consoleLogger);
if (_baseDir == null)
_baseDir = new String(".");
project.setBasedir(_baseDir);
if (_buildFile == null)
_buildFile = new String(projectBasePath + File.separator
+ "build.xml");
ProjectHelper.configureProject(project, new File(_buildFile));
}
public void runTarget(String _target) throws Exception {
if (project == null)
throw new Exception(
"No target can be launched because the project has not been initialized. Please call the 'init' method first !");
if (_target == null)
_target = project.getDefaultTarget();
project.executeTarget(_target);
}
/**
* 根据所秒数,计算相差的时间并以**时**分**秒返回
*
* @param d1
* @param d2
* @return
*/
public static String getBeapartDate(long m) {
m = m / 1000;
String beapartdate = "";
int nDay = (int) m / (24 * 60 * 60);
int nHour = (int) (m - nDay * 24 * 60 * 60) / (60 * 60);
int nMinute = (int) (m - nDay * 24 * 60 * 60 - nHour * 60 * 60) / 60;
int nSecond = (int) m - nDay * 24 * 60 * 60 - nHour * 60 * 60 - nMinute
* 60;
beapartdate = nDay + "天" + nHour + "小时" + nMinute + "分" + nSecond + "秒";
return beapartdate;
}
public static String read(String filePath, String replaceStr) {
BufferedReader br = null;
String line = null;
StringBuffer buf = new StringBuffer();
try {
// 根据文件路径创建缓冲输入流
br = new BufferedReader(new FileReader(filePath));
// 循环读取文件的每一行, 对需要修改的行进行修改, 放入缓冲对象中
while ((line = br.readLine()) != null) {
// 此处根据实际需要修改某些行的内容
if (line.contains(placeHolder)) {
line = line.replace(placeHolder, replaceStr);
buf.append(line);
} else {
buf.append(line);
}
buf.append(System.getProperty("line.separator"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
br = null;
}
}
}
return buf.toString();
}
/**
* 将内容回写到文件中
*
* @param filePath
* @param content
*/
public static void write(String filePath, String content) {
BufferedWriter bw = null;
try {
// 根据文件路径创建缓冲输出流
bw = new BufferedWriter(new FileWriter(filePath));
// 将内容写入文件中
bw.write(content);
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭流
if (bw != null) {
try {
bw.close();
} catch (IOException e) {
bw = null;
}
}
}
}
}
这个项目中需要导入这些包ant-launcher.jar,ant.jar,tools.jar,这个tools.jar是jdk里面的jar包,那两个包是你解压的ant里面lib里的jar包,不过不用担心,我会上传整个可运行的demo
tools.jar是jdk里面的jar包,因为之前的代码中没有用这个jar 包,我用到了,是因为我之前也是没有用tools.jar,结果我运行出错了,百度了一下,加上这个jar包,问题就解决了
在market.txt中就加上你需要修改的东西,像应用市http://场名字啊,目前就只是修改一处代码,同时修改多处的话,那就你们自己研究了
market.txt
11
21
....
3.配置好AntTest.java文件之后,运行这个文件
运行中没有报错误,也没有报警告的话,就说明打包完成我们去运行一下看看
成功调用不同的数值了
4.再来说说中间可能会遇到的一些错误
这个错误 D:\Program Files\android-sdk-windows\tools\ant\build.xml:713: The following error occurred while executing this line:
解决方案就是导入tools.jar包,add build path 就行了
android-sdk-windows\tools\ant\build.xml:691: null returned: 1
这个问题的解决方案就是
找到sdk/tools/ant/build.xml文件
找到如下一行,value设置为crunch 即可
<property name="aapt.ignore.assets" value="crunch" />
亲测有效
再说一下项目乱码的问题,如果你要导出的项目编码是gbk编码的话,那你在项目中所写的中文就会乱码
警告: 编码UTF-8的不可映射字符* ?????? 10s
如果项目是utf8的编码,那就不会出现这个警告了,不会乱码了,所以新建项目的时候最好是utf8编码好点,毕竟它是国际编码,gbk是中国的
解决方案是在build.xml中写入这些代码即可,具体可看项目中的源代码
<property name="src" location="src"/>
<property name="build" location="build"/>
<property name="dist" location="dist"/>
<target name="init">
<!-- Create the time stamp -->
<tstamp/>
<!-- Create the build directory structure used by compile -->
<mkdir dir="${build}"/>
</target>
<target name="compile" depends="init"
description="compile the source " >
<!-- Compile the java code from ${src} into ${build} -->
<javac srcdir="${src}" destdir="${build}"/>
</target>
<target name="dist" depends="compile"
description="generate the distribution" >
<!-- Create the distribution directory -->
<mkdir dir="${dist}/lib"/>
</target>
<target name="clean"
description="clean up" >
<!-- Delete the ${build} and ${dist} directory trees -->
<delete dir="${build}"/>
<delete dir="${dist}"/>
</target>
好了,就是这样了
写下demo下载