采用Ant打包的完美实现:编译、合并、签名、混淆、优化、自动运行对Android App快速实现自动打包进行了详细的说明,本篇在此基础上对APP的批量打包、发布进行简要的实现说明,方便进行以下场景的APP打包:

  • 同一个APP,不同版本、不同内容的APK包
  • 不同的APP,相应的APK包

       通常可能会想到依旧采用ANT、BAT等方式来进行批量的打包处理,但事实上此类方式多数情况下打包的不够灵活(比如BAT的跨平台、打包过程中的一些特殊事务处理等),因此有必要进行自定义Application的方式来完成,总体事务处理结构如下:
       -build 输出目录
       -conf  打包配置目录
         --weixin 某个APK待打包的资源信息目录
             --res  以res为例,可放置任何其他需要的打包资源
                --drawable-hdpi
                   --xxx
               --drawable-mdpi
                   --xxx
          --alipay 
       -build.xml 该文件仅包含当次需打包的所有APK配置信息
       -template 模板目录
         --weixin 
         --alipay 
       -complier.jar 为本文事务处理的可执行JAR包

     实现思路:

  • 读取用户输入指令 --> 用以进行交互性处理,比如编译完成后自动安装、自动运行等。
  • 启动异步处理线程
  • 读取批量的打包配置信息 --> 以JSON格式存储
  • 反序列化配置信息
  • 进行打包前事务处理 --> 删除编译历史、创建编译输出目录等
  • 遍历打包配置项     ---foreach开始---
  • 解析打包配置项
  • 拷贝模板到build目录
  • 替换或合并build目录中当前APK的资源
  • 替换APK中某类或所有类型文件的模板变量信息 --> 如项目名、包名、版本信息、action等。
  • 运行编译指令 --> linux、windows差别仅在于此
  • 将打包后的APK文件复制到build主目录
  • 显示编译打包时间
  • 根据指令决定是否安装、运行APK
  • 进行下一个编译事项 ---foreach结束---
  • 显示事务整体处理耗时

 

读取用户输入:

 

	/*
	 * 读取用户输入 
	 * @param prompt 提示文字 
	 * @return 用户输入 
	 */
	private static String readUserInput(String prompt) {
		try {
			// 先定义接受用户输入的变量
			String result;
			do {
				// 输出提示文字
				System.out.print(prompt);
				InputStreamReader is_reader = new InputStreamReader(System.in);
				result = new BufferedReader(is_reader).readLine();
			} while (isInvalid(result)); // 当用户输入无效的时候,反复提示要求用户输入
			return result;
		} catch (IOException e) {
			e.printStackTrace();
		}
		return "";
	}
	private static boolean isInvalid(String str) {
		return str.equals("");
	}


运行编译指令:ant -f .../build.xml clean release

 

	public static void runbatwithmsg(String batName) {
		try {
			System.out.println("batName:" + batName);
			Process ps = Runtime.getRuntime().exec(batName);
			InputStream in = ps.getInputStream();
			int c;
			while ((c = in.read()) != -1) {
				if (((char) c) != '\r') {
					System.out.print((char) c);
				}
			}
			in.close();
			ps.waitFor();
		} catch (IOException ioe) {
			ioe.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("child thread done");
	}

文件、目录类相关:

 

public class FileUtils {
	/**
	 * 复制单个文件
	 * 
	 * @param oldPath
	 *            String 原文件路径 如:c:/fqf.txt
	 * @param newPath
	 *            String 复制后路径 如:f:/fqf.txt
	 * @return boolean
	 */
	public static void copyFile(String oldPath, String newPath) {
		try {
			int byteread = 0;
			File oldfile = new File(oldPath);
			if (oldfile.exists()) { // 文件存在时
				InputStream inStream = new FileInputStream(oldPath); // 读入原文件
				FileOutputStream fs = new FileOutputStream(newPath);
				byte[] buffer = new byte[1024 * 10];
				while ((byteread = inStream.read(buffer)) != -1) {
					fs.write(buffer, 0, byteread);
				}
				fs.flush();
				fs.close();
				inStream.close();
			}
		} catch (Exception e) {
			System.out.println("复制单个文件操作出错");
			e.printStackTrace();
		}
	}

	/**
	 * 复制整个文件夹内容
	 * 
	 * @param oldPath
	 *            String 原文件路径 如:c:/fqf
	 * @param newPath
	 *            String 复制后路径 如:f:/fqf/ff
	 * @return boolean
	 */
	public static void copyFolder(String oldPath, String newPath) {
		try {
			(new File(newPath)).mkdirs(); // 如果文件夹不存在 则建立新文件夹
			File a = new File(oldPath);
			String[] file = a.list();
			File temp = null;
			for (int i = 0; i < file.length; i++) {
				if (oldPath.endsWith(File.separator)) {
					temp = new File(oldPath + file[i]);
				} else {
					temp = new File(oldPath + File.separator + file[i]);
				}
				if (temp.isFile()) {
					FileInputStream input = new FileInputStream(temp);
					FileOutputStream output = new FileOutputStream(newPath
							+ "/" + (temp.getName()).toString());
					byte[] b = new byte[1024 * 5];
					int len;
					while ((len = input.read(b)) != -1) {
						output.write(b, 0, len);
					}
					output.flush();
					output.close();
					input.close();
				}
				if (temp.isDirectory()) {// 如果是子文件夹
					copyFolder(oldPath + "/" + file[i], newPath + "/" + file[i]);
				}
			}
		} catch (Exception e) {
			System.out.println("复制整个文件夹内容操作出错");
			e.printStackTrace();
		}
	}

	/**
	 * 获得可编辑的文件列表
	 * 
	 * @param files
	 * @param path
	 */
	public static void getName(List<String> files, String path) {
		File file = new File(path);
		if (file.isDirectory()) {
			File[] dirFile = file.listFiles();
			for (File f : dirFile) {
				if (f.isDirectory())
					getName(files, f.getAbsolutePath());
				else {
					if (f.getName().endsWith(".java")
							|| f.getName().endsWith(".xml")
							|| f.getName().endsWith(".bat"))
						files.add(f.getAbsolutePath());
				}
			}
		}
	}

	/**
	 * 读取文件中内容
	 * 
	 * @param path
	 * @return
	 * @throws IOException
	 */
	public static String readFileToString(String path) throws IOException {
		String resultStr = null;
		FileInputStream fis = null;
		BufferedReader buf = null;
		try {
			fis = new FileInputStream(path);
			String line = null;
			buf = new BufferedReader(new InputStreamReader(fis, "UTF-8"));
			StringBuilder sb = new StringBuilder();
			while ((line = buf.readLine()) != null) {
				sb.append(line);
			}
			return sb.toString();
		} finally {
			if (fis != null)
				fis.close();
		}
	}

	public static String ReadTxtFile(String FileName) {
		try {
			BufferedInputStream bufferedInputStream = new BufferedInputStream(
					new FileInputStream(FileName));
			ByteArrayOutputStream memStream = new ByteArrayOutputStream();
			byte[] buffer = new byte[1024];
			int len = 0;
			while ((len = bufferedInputStream.read(buffer)) != -1) {
				memStream.write(buffer, 0, len);
			}
			byte[] data = memStream.toByteArray();
			bufferedInputStream.close();
			memStream.close();
			bufferedInputStream.close();
			return new String(data, "GBK");
		} catch (Exception e) {
			e.printStackTrace();
		}
		return "";
	}

	public static void deleteDir(String path, boolean bDelRoot) {
		File dir = new File(path);
		if (dir.exists() && dir.isDirectory()) {
			File[] tmp = dir.listFiles();
			if (null != tmp) {
				for (int i = 0; i < tmp.length; i++) {
					if (tmp[i].isDirectory()) {
						deleteDir(path + "/" + tmp[i].getName(), true);
					} else {
						tmp[i].delete();
					}
				}
			}
			if (bDelRoot) {
				dir.delete();
			}
		}
	}

	public static void deleteFile(String file) {
		File f = new File(file);
		f.delete();
	}

	public static BufferedOutputStream getOutputStream(File file)
			throws IOException {
		return new BufferedOutputStream(new FileOutputStream(file), 64 * 1024);
	}

	public static BufferedOutputStream getOutputStream(String file)
			throws IOException {
		return getOutputStream(new File(file));
	}

	public static void close(OutputStream out) {
		if (null != out) {
			try {
				out.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			out = null;
		}
	}

	public static void close(InputStream in) {
		if (null != in) {
			try {
				in.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			in = null;
		}
	}

	private static void createDirIfNotExist(File file) {
		if (null == file)
			return;
		if (!file.exists()) {
			File parentFile = file.getParentFile();
			if (!parentFile.exists()) {
				parentFile.mkdirs();
			}
		}
	}

	public static void createDirIfNotExist(String filePath) {
		File file = new File(filePath);
		createDirIfNotExist(file);
	}

	public static void writeFile(String str, String descFile, boolean append)
			throws Exception {
		if ((null == str) || (null == descFile)) {
			return;
		}
		createDirIfNotExist(descFile);
		BufferedOutputStream out = null;
		try {
			byte[] src = str.getBytes("UTF-8");
			out = new BufferedOutputStream(new FileOutputStream(descFile,
					append), 1024 * 64);
			out.write(src);
			out.flush();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			close(out);
		}
	}

	public static void appendFile(String str, String descFile) throws Exception {
		writeFile(str, descFile, true);
	}
}