C#实现APK自动打包
最近做了一个安卓项目,其中有一个自动打包的功能,要把供应商id写入APK后打包。
一、思路
在AndroidMinifest.xml中加入一个标识字段,如下配置
<meta-data android:name="Vendorid" android:value="xx" />,把每个供应商的Id值写到android:value这里,然后用命令行打包。
二、准备工作
1,安装JDK 1.7(默认安装在C:\Program Files\Java)。安装完成后,添加环境变量JAVA_HOME,值就是Java\jdk1.7.0_25\的实际路径(如果是默认安装的话,那就是 C:\Program Files\Java\jdk1.7.0_25\ )。
2,建一个文件夹(本文中是建在D盘,D:\apktools),然后把Android SDK目录下的platforms、platform-tools、tools这三个文件夹,以及解压后的Apache ant拷贝到D:\apktools。
3,APK源代码我是放在D:\src目录下。
三、测试 - 用命令行打包
做这一步是为了测试之前的准备工作是否做对了。
1,cd /d D:\src,cd到源代码的文件夹
2,D:\apktools\tools\android.bat update project -n ButtonDemo -t android-17 -p d:\src,这个批处理可以生成一个工程,工程里配置build.xml脚本。其中,-n ButtonDemo:项目名称、-t android-17:我这里使用android-17这个版本、-p d:\src:输出到代码目录下。
3,D:\apktools\apache-ant-1.9.1\bin\ant.bat debug
4,D:\apktools\apache-ant-1.9.1\bin\ant.bat release,编译成功后命令行会显示BUILD SUCCESSFUL。
四、代码实现
最初的代码如下:
private void Package()
{
Process pcmd = new Process();
pcmd.StartInfo.FileName = "cmd.exe";
pcmd.StartInfo.UseShellExecute = false;
pcmd.StartInfo.RedirectStandardInput = true;
pcmd.StartInfo.RedirectStandardOutput = true;
pcmd.StartInfo.CreateNoWindow = true;
pcmd.Start();
pcmd.StandardInput.Write("cd /d D:\src");
pcmd.StandardInput.WriteLine("");
pcmd.StandardInput.Write("D:\apktools\tools\android.bat update project -n ButtonDemo -t android-17 -p d:\src");
pcmd.StandardInput.WriteLine("");
pcmd.StandardInput.Write("D:\apktools\apache-ant-1.9.1\bin\ant.bat debug");
pcmd.StandardInput.WriteLine("");
pcmd.StandardInput.Write("D:\apktools\apache-ant-1.9.1\bin\ant.bat release");
pcmd.StandardInput.WriteLine("");
pcmd.StandardInput.WriteLine("exit");
string stroutput = pcmd.StandardOutput.ReadToEnd();
}
调试时发现,ant.bat debug这一步出错,说是JAVA_HOME配置的不对,提示的路径和我实际配置的不一样。必应了一把,看到这篇 和 这篇,大致上就是说UseShellExecute=false方式启动的进程不能访问环境变量,要改成UseShellExecute=true,但是在true的时候,不能重定向输入输出异常这几个了(RedirectStandardInput、RedirectStandardOutput、RedirectStandardError),原先启动cmd.exe,然后输入命令行的方式就行不通了。
调整了下思路,把这几个命令写成一个批处理,然后Process执行这个批处理就可以了。
cd /d D:\src
call D:\apktools\tools\android.bat update project -n ButtonDemo -t android-17 -p d:\src > {log}
call D:\apktools\apache-ant-1.9.1\bin\ant.bat debug >> {log}
call D:\apktools\apache-ant-1.9.1\bin\ant.bat release >> {log}
由于不能用RedirectStandardOutput=true来获取输出信息,所以这里我用了dos命令 >> {log}来输出。
最终的打包代码如下:
private bool Package()
{
//先删除编译后的文件,执行完批处理后,通过判断文件存在与否来确定打包是否成功
string releaseFile = "d:\src\ButtonDemo-release.apk";
if (File.Exists(releaseFile))
{
File.Delete(releaseFile);
}
//生成批处理命令D:\package.bat
CreateBatFile();
ProcessStartInfo pi = new ProcessStartInfo();
pi.FileName = "D:\package.bat";
pi.UseShellExecute = true;
pi.CreateNoWindow = true;
Process pcmd = Process.Start(pi);
//等待进程结束
while (pcmd.HasExited == false)
{
pcmd.WaitForExit(1000);
}
pcmd.Close();
pcmd.Dispose();
if(!File.Exists(releaseFile))
{
//打包失败
return false;
}
//打包成功
return true;
}
AndroidManifest.xml中添加meta-data节点的代码
private void InsertVendorXmlElememnt()
{
List<string> androidnames = new List<string>();
string xmlfile = @"AndroidManifest.xml";
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load(_androidSourcePath + xmlfile);
XmlNode xmlApp = xmldoc.SelectSingleNode("//application");
XmlNodeList xmlnodes = xmlApp.SelectNodes("//meta-data");
foreach (XmlNode item in xmlnodes)
{
if (!androidnames.Contains(item.Attributes["android:name"].Value))
{
androidnames.Add(item.Attributes["android:name"].Value);
}
}
if (!androidnames.Contains("vendorid"))
{
XmlElement xmlelemember = xmldoc.CreateElement("meta-data");
XmlAttribute memAttr = xmldoc.CreateAttribute("android", "name", "http://schemas.android.com/apk/res/android");
memAttr.Value = "vendorid";
xmlelemember.SetAttributeNode(memAttr);
XmlAttribute memValue = xmldoc.CreateAttribute("android", "value", "http://schemas.android.com/apk/res/android");
memValue.Value = "";
xmlelemember.SetAttributeNode(memValue);
xmlApp.AppendChild(xmlelemember);
}
xmldoc.Save("d:\src\AndroidManifest.xml");
}