android应用上传时,需要区分开每个渠道。一般都会在配置文件中更改一个渠道id,如果有多个渠道,手动修改并生成apk的话会非常麻烦,而且增大出错概率。
在这分享一个打包工具类.
我们项目中使用的umeng做统计分析工具, umeng在分渠道打包的时候需要修改manifest.xml中的
<meta-data android:name="UMENG_CHANNEL" android:value="@string/channel_name" /> value值。
该值放到了strings.xml中<string name="channel_name">app_channel</string>,所以在编译的时候只需修改app_channel。
下面是工具类,直接运行即可.
package com.yooeee.packaging.utils;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
/**
* @description: android渠道打包工具类,确保路径填写无误,渠道名称填入channels中
* @author: yangyang@duitang.com
* @time: 2012-10-23下午5:17:47
*/
public class CompiledApkUpdate {
// D:\adt-bundle-windows-x86-20130219\sdk
private static final String androidSDK_PATH = "D:\\adt-bundle-windows-x86-20130219\\sdk\\"; //android SDK路径
public static final String APK_NAME = "Ticket.apk";
public static final String PROJECT_LIBARY = "";
//C:\Users\jiangbo\Desktop\Android
public static final String PROJECT_PATH = "C:\\Users\\jiangbo\\Desktop\\Android\\Tickets\\Ticket\\"; //要打包的工程路径
public static final String APK_PATH = "C:\\Users\\jiangbo\\Desktop\\apk\\ticket_"; //打包后存放apk的路径 duitang_是前缀
private static final String apk_PATH_keystore = "C:\\Users\\jiangbo\\Desktop\\ticket.keystore"; //apk签名文件路径
private static final String channelFlag = "app_channel";
// public static String[] channels = {"duitang"};
private static String currentChannelName = "";
public static String[] channels = {"andr-a1","andr-a2","andr-b1","andr-b2","andr-b3","andr-c1","andr-c2","andr-c3","andr-c4","andr-c5","andr-c6","andr-c7","andr-c8","andr-c9","andr-c10","andr-c11","andr-c12","andr-c13","andr-c14","andr-d1","andr-d2","andr-d3","andr-d4","andr-d5","andr-d6","andr-d7","andr-d8","andr-d9","andr-e1","andr-e2","andr-e3","andr-e4","andr-e5","andr-e6","andr-e7","andr-e8","andr-e9","andr-e10"};
public static void main(String[] args) {
replaceChannel();
}
/**
* 替换渠道名称
*/
public static void replaceChannel() {
try {
String outPath = PROJECT_PATH + "res\\values\\strings.xml"; // 输出文件位置
String content = read(outPath);
for(int channelid=0;channelid<channels.length;channelid++){
String tmpContent = content;
tmpContent = tmpContent.replaceFirst(channelFlag, channels[channelid]);
currentChannelName = channels[channelid];
write(tmpContent,outPath);
System.out.println("replace channel name over...");
packageRes(); // 一次渠道号的更改完成。可以进行打包了。
createUnsignedApk();
signedApk(channelid);
}
write(content,outPath); //完成后还原channel_name
System.out.println("execute over!");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* class文件打包成classes.dex
*/
public static void packageDex(){
try {
System.out.println("dx.bat start...");
Process process = Runtime.getRuntime().exec(androidSDK_PATH
+"platform-tools\\dx.bat --dex --output="
+PROJECT_PATH+"bin\\classes.dex "
+PROJECT_PATH+"bin\\classes "
+PROJECT_PATH+"libs\\*.jar");
new MyThread(process.getErrorStream()).start();
new MyThread(process.getInputStream()).start();
process.waitFor();
process.destroy();
System.out.println("dx.bat over...");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* res assets文件打包成res.zip
*/
public static void packageRes(){
try{
System.out.println(currentChannelName+" create resources.ap");
String library = "";
if(PROJECT_LIBARY!=null&&!PROJECT_LIBARY.equals("")){
library = "-S " + PROJECT_LIBARY + "res ";
}
Process process = null;
process = Runtime
.getRuntime()
.exec( androidSDK_PATH
+ "platform-tools\\aapt.exe package -f " +
"-M " + PROJECT_PATH + "AndroidManifest.xml " + //-M 指定配置文件
"-S " + PROJECT_PATH + "res " + //-S 指定资源文件
library +
"-A " + PROJECT_PATH + "assets " + //-A 指定assets
"-I " + androidSDK_PATH + "platforms\\android-10\\android.jar " + //-I 指定android类
"-F " + PROJECT_PATH + "bin\\resources.ap_ --auto-add-overlay"); // 将资源文件打包resources.ap_
new MyThread(process.getErrorStream()).start();
new MyThread(process.getInputStream()).start();
process.waitFor();
process.destroy();
System.out.println(currentChannelName+" resources.ap over...");
}catch(Exception e){
e.printStackTrace();
}
}
/**
* classes.dex res.zip AndroidManifest.xml组合成未签名的apk
*/
public static void createUnsignedApk(){
try{
System.out.println(currentChannelName+" createUnsignedApk start");
Process process = null;
process = Runtime.getRuntime().exec(
androidSDK_PATH+ "tools\\apkbuilder.bat "
+ PROJECT_PATH + "bin\\"+APK_NAME+" -u -z "
+ PROJECT_PATH + "bin\\resources.ap_ -f "
+ PROJECT_PATH + "bin\\classes.dex"
+ " -rj "+ PROJECT_PATH + "libs"
+" -nf "+ PROJECT_PATH + "libs"); // 生成未签名的apk
new MyThread(process.getErrorStream()).start();
new MyThread(process.getErrorStream()).start();
process.waitFor();
process.destroy();
System.out.println(currentChannelName+" createUnsignedApk over");
}catch(Exception e){
e.printStackTrace();
}
}
/**
* 签名apk
*/
public static void signedApk(int channelid){
try{
System.out.println(currentChannelName+" signed apk start");
Process process = null;
String jarsigner;
//-storepass获取keystore信息的密码 -keypass别名密码 ticket别名
jarsigner = "jarsigner -keystore "+apk_PATH_keystore+" -storepass 123456 -keypass 123456 " +
"-signedjar "
+ APK_PATH
+ channels[channelid]
+ ".apk "
+ PROJECT_PATH
+ "bin\\"+APK_NAME+" ticket"; //签名apk
process = Runtime
.getRuntime()
.exec(jarsigner); // 对apk进行签名
new MyThread(process.getErrorStream()).start();
new MyThread(process.getInputStream()).start();
process.waitFor();
process.destroy();
System.out.println(currentChannelName+" signed apk over"); // 一条渠道的打包完成。文件会输出到指定目录
}catch(Exception e){
e.printStackTrace();
}
}
public static String read(String path) {
StringBuffer res = new StringBuffer();
String line = null;
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(path),"UTF-8"));
while ((line = reader.readLine()) != null) {
res.append(line + "\n");
}
reader.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return res.toString();
}
public static boolean write(String cont, String dist) {
try {
OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(new File(dist)),"utf-8");
writer.write(cont);
writer.flush();
writer.close();
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
public static class MyThread extends Thread{
BufferedReader bf;
public MyThread(InputStream input) {
bf = new BufferedReader(new InputStreamReader(input));
}
public void run() {
String line;
try {
line = bf.readLine();
while (line != null) {
System.out.println(line);
line = bf.readLine();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
注意事项:
1. 正确设置各个path路径.
2. channelFlag的值要与 strings.xml 中channel_name的value一致.
3. 运行java文件是先运行一下android工程,确保bin目录下有最新的apk.
4. 运行过程中不要手动停止,如手动停止,再次运行时请检查strings.xml中channel_name的value值,如不为app_channel,请修改
5. 如有error: Error parsing XML: not well-formed (invalid token)类似错误,注意设置字符编码
相关推荐
Android多渠道打包,替换资源文件、logo、包名、项目名
android多渠道打包
通过python脚本写的多渠道打包工具,自测是没有bug的,如有问题可以留言,我会及时处理。
Android 多渠道打包demo,使用的是友盟的sdk
Android多渠道打包与升级.rar,太多无法一一验证是否可用,程序如果跑不起来需要自调,部分代码功能进行参考学习。
Android 多渠道打包方案,兼容Android P
一个 Mac App,用于 Apk 多渠道打包。 使用美团的方案,在 META-INF 目录写入空文件 支持 AES 加密,CBC 和 ECB 两种模式 可以自定义前缀 下载最新版本,有问题可以在 Issues 中反馈。 License GPLv3
Android 多渠道打包的 Android Studio / IDEA 插件.zip,[Deprecated] Android 多渠道打包的 Android Studio / IDEA 插件
Android-Package-Channel - 美团网做的把Android多渠道打包工具,打包时间缩短到一分钟,python脚本
Android 多渠道打包工具。使用文档http://blog.csdn.net/u013361668/article/details/51697713 github:https://github.com/GavinCT/AndroidMultiChannelBuildTool
eclipse插件进行android多渠道打包,简单实用,功能强大。
Android Walle 多渠道打包。详细使用Walle对AS的一个多渠道打包工具。
androidstudio工程已经实现从外部读取渠道号批量打包 外部library依赖
一行命令实现多渠道打包并自动进行资源混淆(Walle,AndResGuard)详情看博客:https://blog.csdn.net/yuzhiqiang_1993/article/details/83341254