Android多渠道打包

真没想到我会搞apk打包,本以为做做APP就可以啦,人生处处是惊喜啊。今天想介绍一下Android的多渠道打包,自己实现了一个开发工具(暂不能分享),实际核心是一些打包的命令行,采用ANT的打包方式(现在流行的是gradle方式打包,可以搞一下),反编译修改AndroidManifest.xml,修改meta-data字段以做渠道区分。工具使用C#写的,当然也有很多渠道打包方式,比如以下两篇文章的介绍。

http://www.thinksaas.cn/topics/0/584/584606.html

http://tech.meituan.com/mt-apk-packaging.html

http://blog.csdn.net/kingdam578/article/details/8216614

当然,我使用的是第一篇博客的第二个方法。其中使用了大量的文件操作,可以参考C#常见文件操作,整体过程如下图:

反编译裸包

我们要求CP商在manifest中加入meta-data节点,用于我们处理渠道。

执行后会生成如下文件

apktool.yml文件中包含了打包的相关信息,版本号等等,此文件生成说明反编译成功。

主要使用命令行:

 

string cmdStr = Constant.APK_TOOLS_PATH + @"\apktool.bat"
        + " d -s -f " + pureApkPath
        + " -o " + outDecodePath;

也就是apktool.bat这个脚本。

 

获取裸包Manifest.xml相关信息

ManiFestXmlData xml_data = new ManiFestXmlData();//创建一个对象,将相关数据传给封装类ManiFestXmlData
XmlDocument xml = new XmlDocument();//新建xml文件对象

if (FileIOManager.isExistFile(mUrl)) {
    xml.Load(mUrl);
    XmlElement xml_ele = xml.DocumentElement;
    package=xml_ele.GetAttribute("package");//获取包名
    XmlNodeList xml_list = xml_ele.GetElementsByTagName("meta-data");//获取meta-data文件
    for (int i = 0; i < xml_list.Count; i++)//对meta-data文件进行遍历访问,取出元素
    {
        XmlElement x_ele = (XmlElement) xml_list[i];

        if (x_ele.GetAttribute("android:name") == "CHANNEL_CODE") {
            channel_code = x_ele.GetAttribute("android:value");
        }
    }
    xml_data.Package =package;//赋值
    xml_data.CHANNEL_CODE = channel_code;
}

同理可以获取版本号什么的相关信息。

合并常见的数据

class MergeCommonManifestData
{
    #region 将渠道包数据写入裸包中
    /// <summary>
    /// 将渠道包数据写入裸包中
    /// </summary>
    /// <param name="manifest_orig_url">传入的裸包的manifestxml地址</param>
    /// <param name="yml_url">apktools.yml_url</param>
    /// <returns></returns>
    public bool WriteWithVersiopnData(String manifest_orig_url, String yml_url, Controller data)
    {
        LogUtil.PtShowLog("==> Write Version/channelCode Start ...");
        String channelpck_url = manifest_orig_url;
        StringBuilder channelpckSb = FileIOManager.readFile(channelpck_url, Encoding.Default);
        ManiFestXmlData xml_data = new ManiFestXmlData();//创建一个对象,将相关数据传给封装类ManiFestXmlData

        String apktool_ymlUrl = yml_url;
        StringBuilder ymlSb = FileIOManager.readFile(apktool_ymlUrl, Encoding.Default);

        if (!ymlSb.ToString().Contains("versionCode:"))
        {
            LogUtil.showMessageBoxWarn("裸包中缺少versionCode");
            return false;
        }

        if (!ymlSb.ToString().Contains("versionName:"))
        {
            LogUtil.showMessageBoxWarn("裸包中缺少versionName");
            return false;
        }
        String versionCode = FindValue(ymlSb.ToString(), "versionCode:", "'");
        String versionName = FindValue(ymlSb.ToString(), "versionName:", "'");

        int channelpckSbStartIndex = channelpckSb.ToString().IndexOf("<manifest");
        int channelpckSbEndIndex = channelpckSb.ToString().IndexOf(">", channelpckSbStartIndex);

        int channelpckSbStartAppIndex = channelpckSb.ToString().IndexOf("<application");
        int channelpckSbEndAppIndex = channelpckSb.ToString().IndexOf(">", channelpckSbStartAppIndex);


        int channelpckSbStartMetaIndex = channelpckSb.ToString().IndexOf("<meta-data");
        int channelpckSbEndMetaIndex = channelpckSb.ToString().IndexOf(">", channelpckSbStartMetaIndex);

        StringBuilder addVersion = new StringBuilder();
        addVersion.Append(" ");
        addVersion.Append(String.Format("android:versionCode=\"{0}\"", versionCode));
        addVersion.Append(" ");
        addVersion.Append(String.Format("android:versionName=\"{0}\"", versionName));
        channelpckSb.Insert(channelpckSbEndIndex, addVersion.ToString());

        String minSdkVersion = FindValue(ymlSb.ToString(), "minSdkVersion", "'");
        String targetSdkVersion = FindValue(ymlSb.ToString(), "targetSdkVersion", "'");

        StringBuilder addChannel_code = new StringBuilder();

        if (minSdkVersion.Length > 0 || targetSdkVersion.Length > 0)
        {
            StringBuilder addSdkVersion = new StringBuilder();

            channelpckSbStartIndex = channelpckSb.ToString().IndexOf("<manifest");
            channelpckSbEndIndex = channelpckSb.ToString().IndexOf(">", channelpckSbStartIndex);

            addSdkVersion.Append("\r\n");
            addSdkVersion.Append("<uses-sdk ");

            if (minSdkVersion.Length > 0)
            {
                addSdkVersion.Append(String.Format("android:minSdkVersion=\"{0}\" ", minSdkVersion));
            }
            if (targetSdkVersion.Length > 0)
            {
                addSdkVersion.Append(String.Format("android:targetSdkVersion=\"{0}\"", targetSdkVersion));
            }
            addSdkVersion.Append(" />");
            channelpckSb.Insert(channelpckSbEndIndex + 1, addSdkVersion.ToString());

            FileIOManager.deleteFile(channelpck_url);
            FileIOManager.writeText2File(channelpck_url, channelpckSb.ToString());
        }
        return true;
    }
    #endregion

    #region 从字符串中找到相应的key的值
    /// <summary>
    /// 从字符串中找到相应的key的值
    /// </summary>
    /// <param name="orignal"></param>
    /// <param name="key"></param>
    /// <param name="valueFlag"></param>
    /// <returns></returns>
    public static String FindValue(String orignal, String key, String valueFlag)
    {
        int _keyIndex = orignal.IndexOf(key);
        if (_keyIndex < 0)
        {
            return "";
        }

        int _valueStartIndex = orignal.IndexOf(valueFlag, _keyIndex + key.Length);
        int _valueEndIndex = 0;
        if (_valueStartIndex < 0)
        {
            // 遇到apktool.yml文件中versionName没有引号标识,如:versionName: 1.0.0.10
            _valueStartIndex = _keyIndex + key.Length;
            _valueEndIndex = orignal.IndexOf("\r\n", _valueStartIndex + 1);
        }
        else
        {
            _valueEndIndex = orignal.IndexOf(valueFlag, _valueStartIndex + 1);
        }

        String _result = orignal.Substring(_valueStartIndex + 1, _valueEndIndex - _valueStartIndex - 1);

        return _result;
    }
    #endregion
}

写入渠道

if (FileIOManager.isExistFile(manifest_orig_url))
{

    StringBuilder sb = FileIOManager.readFile(manifest_orig_url, Encoding.Default);

    XmlDocument xml = new XmlDocument();
    xml.Load(manifest_orig_url);
    XmlNodeList nodeList = xml.GetElementsByTagName("meta-data");
    foreach (XmlNode xn in nodeList)
    {
        XmlElement xe = (XmlElement)xn;

        if (xe.GetAttribute("android:name") == "CHANNEL_CODE")
        {
            xe.SetAttribute("android:value", data.ChannelCode);//渠道标志
        }
    }
    xml.Save(manifest_orig_url);
}

合并渠道和裸包资源

//copy org res to channel_out
FileIOManager.copyDirectory(orig_ROOT + assets, channel_OUT_ROOT + assets, false);
FileIOManager.copyDirectory(orig_ROOT + lib, channel_OUT_ROOT + libs, false);//.so文件也拷贝到libs目录下
FileIOManager.copyDirectory(orig_ROOT + libs, channel_OUT_ROOT + libs, false);
FileIOManager.copyDirectory(orig_ROOT + res, channel_OUT_ROOT + res, false);
//copy channel res to channel_out
FileIOManager.copyDirectory(channel_ROOT + @"\" + data.ChannelCode + @"\" + assets, channel_OUT_ROOT + assets, false);
FileIOManager.copyDirectory(channel_ROOT + @"\" + data.ChannelCode + @"\" + libs, channel_OUT_ROOT + libs, false);
FileIOManager.copyDirectory(channel_ROOT + @"\" + data.ChannelCode + @"\" + res, channel_OUT_ROOT + res, false);
if (FileIOManager.isExistDirectory(channel_ROOT + @"\" + data.ChannelCode + @"\" + lib))
FileIOManager.copyDirectory(channel_ROOT + @"\" + data.ChannelCode + @"\" + lib, channel_OUT_ROOT + libs, false);//.so文件也拷贝到libs目录下

获取裸包classes文件

dex2jar

主要使用命令行:

string cmdStr = Constant.APK_TOOLS_PATH + @"\dex2jar\d2j-dex2jar "
        + classesOutPath + @"\classes.dex"
        + " -o " + classesOutPath + Constant.Dex_Jar_Name;

然后对上步生成的jar解压j

string cmdStr = "jar -xvf " + "original.jar";

从而获取.class文件。

重新生成R文件

#region 重新生成指定R文件
/// <summary>
/// 重新生成指定R文件
/// </summary>
/// <param name="mPackageName">指定的R文件包名</param>
public void remakeR(String mPackageName)
{
    R_Name = mPackageName.Equals("") ? data.PackageName : mPackageName;

    LogUtil.PtShowLog("==> Start Creat R ...");
    LogUtil.d("CreatR", "Create R :" + R_Name);

    //生成 R.java 
    string cmdStr = Constant.APK_TOOLS_PATH + @"\aapt.exe package -f -m"
        + " -J " + Channel_Dir_ROOT + @"\gen"
        + " -S " + Channel_Dir_ROOT + @"\res"
        + " -I " + Constant.SDK_PLATFORMS_PATH + @"\android-20\android.jar"
        + " --custom-package " + R_Name
        + " -M " + Channel_Dir_ROOT + @"\AndroidManifest.xml";
    LogUtil.d("CreateR cmd", cmdStr);
    CmdUtil.doCmdCommand(cmdStr);

    String[] packageList = R_Name.Split('.');
    String _RPath = "";
    for (int i = 0; i < packageList.Length; i++)
    {
        _RPath = _RPath + "\\" + packageList[i];
    }
    // 
    string cmdJavacStr = "javac -encoding UTF-8 -source 1.6 -target 1.6"
            + " " + Channel_Dir_ROOT + @"\gen" + _RPath + @"\R.java";
    LogUtil.d("CreateR Javac cmd", cmdJavacStr);
    CmdUtil.doCmdCommand(cmdJavacStr);

    FileIOManager.deleteFile(Channel_Dir_ROOT + @"\gen" + _RPath + @"\R.java");
    FileIOManager.createDirectory(Channel_Dir_ROOT + @"\bin\classes");
    FileIOManager.copyDirectory(Channel_Dir_ROOT + @"\gen", Channel_Dir_ROOT + @"\bin\classes\", true);

    LogUtil.d("CreateR", "CreateR Successfully");
}
#endregion

合并渠道manifest要求

将合并好的manifest复制到渠道资源下,此时渠道资源基本已经就位,下面是对AndroidManifest.xml文件进行操作,将渠道要求的Activity、service、permission...填充到manifest中

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Threading.Tasks;
using YcPackageTool.Model;
using YcPackageTool.PTdata;
using YcPackageTool.Utils;

namespace YcPackageTool.PackageKits
{
    class MergeManifestXMLHelper
    {
        private Controller data;
        private StringBuilder manifestXmlSB;
        private string Orig_Application_Xml_Url = "";
        private string Orig_Permission_Xml_Url = "";
        private string OUT_Manifest_Xml_Url = "";
        private ManiFestXmlData maniFestXmlData;

        public MergeManifestXMLHelper(Controller data, ManiFestXmlData maniFestXmlData)
        {
            this.data = data;
            this.maniFestXmlData = maniFestXmlData;
            Orig_Application_Xml_Url = Constant.BIN_ROOT + Constant.UnZip_File_Name + @"\" + data.ChannelCode + @"\application.xml";
            Orig_Permission_Xml_Url = Constant.BIN_ROOT + Constant.UnZip_File_Name + @"\" + data.ChannelCode + @"\permission.xml";
            OUT_Manifest_Xml_Url = data.OutPut + @"\" + data.ChannelCode + "_youcai" + @"\AndroidManifest.xml";
        }

        #region 合并application.xml、permission.xml、meta.xml文件内容
        /// <summary>
        /// 合并application.xml、permission.xml、meta.xml文件内容
        /// </summary>
        public void mergeXML()
        {
            if (FileIOManager.isExistFile(Orig_Application_Xml_Url)
                    && FileIOManager.isExistFile(Orig_Permission_Xml_Url)
                    && FileIOManager.isExistFile(OUT_Manifest_Xml_Url))
            {
                manifestXmlSB = FileIOManager.readFile(OUT_Manifest_Xml_Url, Encoding.Default);
                mergeApplicationXML();
                mergePermissionXML();
            }

            else
            {
                LogUtil.showMessageBoxWarn("源/输出xml文件不存在 [method] mergeXML()");
            }

            if (data.IsMetadata.Equals("1"))
                mergeMetaXML();
            FileIOManager.deleteFile(OUT_Manifest_Xml_Url);
            string xml = manifestXmlSB.ToString();
            if(xml.Contains("package=" + "\"" + maniFestXmlData.Package + "\""))
                xml = manifestXmlSB.ToString()
                        .Replace("package=" + "\"" + maniFestXmlData.Package + "\"",
                                "package=" + "\"" + data.PackageName + "\"");
            FileIOManager.writeText2File(OUT_Manifest_Xml_Url, xml);
        }
        #endregion

        #region 合并sdkres中Application.xml
        /// <summary>
        /// 合并Application.xml
        /// </summary>
        private void mergeApplicationXML()
        {
            int index = manifestXmlSB.ToString().LastIndexOf("</application>");
            string activityContent = FileIOManager.fileToString(Orig_Application_Xml_Url);
            manifestXmlSB.Insert(index - 1, Environment.NewLine + activityContent + Environment.NewLine);
        }
        #endregion

        #region 合并sdkres中permission.xml
        /// <summary>
        /// 合并permission.xml
        /// </summary>
        private void mergePermissionXML()
        {
            int index = manifestXmlSB.ToString().LastIndexOf("<application");
            string permissionContent = FileIOManager.fileToString(Orig_Permission_Xml_Url);
            manifestXmlSB.Insert(index - 1, Environment.NewLine + permissionContent + Environment.NewLine + Environment.NewLine);
        }
        #endregion

        #region 合并sdkres中meta.xml
        /// <summary>
        /// 合并meta.xml
        /// </summary>
        private void mergeMetaXML()
        {
            int index = manifestXmlSB.ToString().LastIndexOf("</application>");
            string metaDataContent = getMetaXmlContent(data.MetaDataInfo);
            manifestXmlSB.Insert(index - 1, Environment.NewLine + metaDataContent + Environment.NewLine);
        }
        #endregion

        #region 获取Meta-data内容,不包括value真实值
        /// <summary>
        /// 获取Meta-data内容,不包括值
        /// </summary>
        /// <param name="keys"></param>
        /// <returns></returns>
        private string getMetaXmlContent(string keys)
        {
            string[] datas = keys.Split('|');
            int len = datas.Length;
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < len - 1; i++)
            {
                sb.Append(creatMetaRoot(datas[i]) + Environment.NewLine);
            }
            sb.Append(creatMetaRoot(datas[len - 1]));
            string temp = sb.ToString();
            string result = temp.Replace("__", ":");
            return result;
        }
        #endregion

        #region 创建meta-data节点
        /// <summary>
        /// 创建meta-data节点
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        private string creatMetaRoot(string data)
        {
            XmlDocument xmlDoc = new XmlDocument();
            XmlElement node = xmlDoc.CreateElement("meta-data");

            //XmlDocument 中出现“:”的话会被拆分,采用了“__”分隔 不知道此处会不会有坑(key会不会有特殊符号,360的是“_” meta-data的是“-”)TODO by lzy
            node.SetAttribute("android__name", data);
            node.SetAttribute("android__value", @"$" + data);
            xmlDoc.AppendChild(node);
            xmlDoc.Save(Constant.BIN_ROOT + @"\meta.xml");
            string temp = FileIOManager.fileToString(Constant.BIN_ROOT + @"\meta.xml");
            FileIOManager.deleteFile(Constant.BIN_ROOT + @"\meta.xml");
            return temp;
        }
        #endregion
    }
}

给渠道的meta-data添加值

有渠道有的有自己的meta-data,渠道多的话肯定不能单独一个个处理,所以要求渠道资源作以下格式处理,然后替换

<!--添加游龙SDK必须的meta-data: PID、PKEY。该值向游龙申请-->
<meta-data
    android:name="PID"
    android:value="$PID"/>
<meta-data
    android:name="PKEY"
    android:value="$PKEY"/>

 

#region 替换Manifest.xml中meta-data规定的字段
/// <summary>
/// 将获取到的meta-data参数替换AndroidManifest.xml规定的字段,使用方式是
/// <code>String _appId = (String)param["p1"];//p1~p10是规定的协议参数值
/// String _appKey = (String)param["p3"];
/// string[] values = formatValue(_appId, _appKey);
/// for (int i = 0; i < keys.Length; i++)
/// {
/// setMetaDataValue(keys[i], values[i], manifestContent);
/// }
/// return manifestContent.ToString();
/// </code>
/// </summary>
/// <param name="manifestContent">AndroidManifest.xml内容</param>
/// <param name="param">获取到的值 p1~p10</param>
/// <returns>new AndroidManifest.xml</returns>
public virtual string modifyMetaData(StringBuilder manifestContent, Controller data)
{
    keys = data.MetaDataInfo.Split('|');
    string[] values = data.MetaValue.Split('|');
    manifestContent = setData(values, manifestContent);
    return manifestContent.ToString();
}

private StringBuilder setData(string[] values, StringBuilder manifestContent)
{
    for (int i = 0; i < keys.Length; i++)
    {
        manifestContent = setMetaDataValue(keys[i], values[i], manifestContent);
    }
    return manifestContent;
}
#endregion

#region 添加新的meta的值
protected StringBuilder setMetaDataValue(String attr, String value, StringBuilder xmlContent)
{
    // find attr name index and insert value
    int _nameIndex = xmlContent.ToString().IndexOf(attr);
    if (_nameIndex < 0)
    {
        LogUtil.showException("Can't found " + attr);
    }
    if (xmlContent.ToString().IndexOf("$" + attr) > 0)
    {
        xmlContent.Replace("$" + attr, value);
    }
    else
    {
        int _vauleIndex = xmlContent.ToString().IndexOf("=", _nameIndex) + 2;
        xmlContent.Insert(_vauleIndex, value);
    }
    return xmlContent;
}
#endregion

合并classes

将解压的class拷贝到渠道目录下,这些所谓的目录你要自己定义,比如我把渠道定义为:

渠道特殊处理

// 各渠道特殊的处理整理
channelPkg.specialDeal(this, maniFestXmlData);

主要是对资源、manifest.xml处理,个别代码的处理,都可以进行事先的规定和处理,比如:包名的限定(百度的为例)

<provider
    android:name="glrecorder.Initializer"
    android:authorities="$PACKAGENAME.initializer"
    android:exported="false"
    android:initOrder="2147483647" />

然后对这个manifest.xml中进行字符串替换。

创建新的.dex文件

将所有代码合并

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YcPackageTool.Model;
using YcPackageTool.PTdata;
using YcPackageTool.Utils;

namespace YcPackageTool.PackageKits
{
    class CreateDexHelper
    {
        private Controller data;
        private string Dex_Out_File_Name = @"\classes.dex";
        private string Channel_Dir_ROOT = "";
        private string Orig_Classes_ROOT = "";
        private string DEX_TOOLS_ROOT = "";

        public CreateDexHelper(Controller data)
        {
            this.data = data;
            Channel_Dir_ROOT = data.OutPut + @"\" + data.ChannelCode + "_youcai";
            Orig_Classes_ROOT = Channel_Dir_ROOT + @"\bin\classes";
            DEX_TOOLS_ROOT = Constant.SDK_BUILD_TOOLS + @"\android-4.4W\dx.bat";
        }

        #region 把classes和jar转成classes.dex
        /// <summary>
        /// 把classes和jar转成classes.dex
        /// </summary>
        public void classAndJar2Dex()
        {
            LogUtil.PtShowLog("==> Start Create dex ...");
            LogUtil.d("CreateDex", "Start Create dex");

            //classes
            string cmdStr = DEX_TOOLS_ROOT
                    + " --dex --verbose --output=" + Channel_Dir_ROOT + Dex_Out_File_Name
                    + " " + Orig_Classes_ROOT;
            //jar
            String libsPath = Channel_Dir_ROOT + @"\libs";
            FileIOManager.createDirectory(libsPath);
            StringBuilder _sb = new StringBuilder();
            String[] _jarFiles = FileIOManager.getFileNames(libsPath, "*.jar", false);
            if (null != _jarFiles && _jarFiles.Length > 0)
            {
                foreach (String _jarFile in _jarFiles)
                {
                    _sb.Append(" " + _jarFile);
                }
            }
            LogUtil.d("JarList", _sb.ToString());
            LogUtil.d("CreateDexCmd", cmdStr + _sb.ToString());
            CmdUtil.doCmdCommand(cmdStr + _sb.ToString());

            if (FileIOManager.isExistFile(Channel_Dir_ROOT + @"\classes.dex")
            && FileIOManager.getFileSize(Channel_Dir_ROOT + @"\classes.dex") > 0)
            {
                LogUtil.PtShowLog("==> Create dex Successfully!!!");
                LogUtil.d("CreateDex", "Create dex Successfully");
            }
            else
            {
                LogUtil.showException(String.Format("classes.dex文件创建失败,请复制\r\n{0}\r\n在cmd命令行执行,查看资源问题", cmdStr + _sb.ToString()));
            }

        }
        #endregion
    }
}

重新生成渠道资源

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YcPackageTool.Model;
using YcPackageTool.PTdata;
using YcPackageTool.Utils;

namespace YcPackageTool.PackageKits
{
    class ReMakeResourcesHelper
    {
        private Controller data;
        private string Channel_Res_ROOT;
        private string Pack_Tools_ROOT;
        private string Android_Res = @"\res";
        private string Android_Assets = @"\assets";
        private string Android_Out_Resources = @"\resources";//生成Resources文件
        private string Android_Manifest = @"\AndroidManifest.xml";

        public ReMakeResourcesHelper(Controller data)
        {
            this.data = data;
            Pack_Tools_ROOT = Constant.APK_TOOLS_PATH;
            Channel_Res_ROOT = data.OutPut + @"\" + data.ChannelCode + "_youcai";
        }

        #region 重新生成配置好的渠道资源
        /// <summary>
        /// 重新生成配置好的渠道资源
        /// </summary>
        /// <returns></returns>
        public bool reMakeChannelRes()
        {
            LogUtil.PtShowLog("==> Start RemakeResources ...");
            LogUtil.d("RemakeResources", "Start RemakeResources");

            string cmdStr = Pack_Tools_ROOT + @"\aapt.exe package -f "
                + " -S " + Channel_Res_ROOT + Android_Res
                + " -A " + Channel_Res_ROOT + Android_Assets
                + " -M " + Channel_Res_ROOT + Android_Manifest
                + " -I " + Constant.SDK_PLATFORMS_PATH + @"\android-20\android.jar"
                + " -F " + Channel_Res_ROOT + Android_Out_Resources;

            LogUtil.d("ReMakeRessources", cmdStr);
            LogUtil.PtShowLog(cmdStr);

            CmdUtil.doCmdCommand(cmdStr);
            string _resourcesPath = Channel_Res_ROOT + Android_Out_Resources;

            if (!FileIOManager.isExistFile(_resourcesPath))
            {
                LogUtil.showException(String.Format("Resources创建失败,请复制\r\n{0}\r\n在cmd命令行执行,查看资源问题", cmdStr));
                return false;
            }

            LogUtil.PtShowLog("==> RemakeResources Successfully!!!");
            LogUtil.d("RemakeResources", "RemakeResources Successfully");
            return true;
        }
        #endregion
    }
}

打未签名的APK包

string cmdStr = "java -cp " + Constant.SDK_TOOLS_PATH + @"\lib\sdklib.jar"
        + " com.android.sdklib.build.ApkBuilderMain "
        + Out_UnSign_APK_PATH
        + " -v -u -z " + Channel_ROOT + @"\resources"
        + " -f " + Channel_ROOT + @"\classes.dex"
        + " -nf " + Channel_ROOT + @"\libs";

对APK进行签名

string cmdStr = "jarsigner -verbose -keystore " + data.KeyPath
        + " -storepass " + data.KeyPass
        + " -signedjar " + OUT_Sign_APK_PATH
        + " " + Orig_UNSigned_APK_PATH
        + " " + data.KeyAlis
        + " -digestalg SHA1 -sigalg MD5withRSA -tsa http://timestamp.digicert.com";

打ZIP包,对签名包进行优化

string cmdStr = Pack_Tools_ROOT + @"\zipalign.exe"
        + " -f -v 4 " + Orig_Signed_APK_PATH
        + " " + OUT_ZipAlign_APK_PATH;

ok,渠道打包过程结束。

 

另赠C#中运行cmd命令源码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Diagnostics;
using System.Windows.Forms;

namespace YcPackageTool.Utils
{
    class CmdUtil
    {
        private static Process pro = new Process();

        #region cmd 命令
        /// <summary>
        /// cmd文件命令
        /// </summary>
        /// <param name="commandStr">指令字符串</param>
        public static string doCmdCommand(String commandStr)
        {
            return doCommand(commandStr, 0, "cmd.exe", false);
        }
        #endregion

        #region 执行指定文件路径
        /// <summary>
        /// 执行指定文件命令
        /// </summary>
        /// <param name="commandStr">指令字符串</param>
        /// <param name="seconds">延迟执行毫秒数</param>
        /// <param name="fileName">要执行的命令集 路径+名称</param>
        /// <param name="isLog">是否打印执行信息</param>
        public static string doCommand(String commandStr, int seconds, String fileName, Boolean isLog)
        {
            string outStr = "";//输出执行信息
            pro.StartInfo.FileName = fileName;
            pro.StartInfo.Arguments = "/C " + commandStr;     //“/C”表示执行完命令后马上退出
            pro.StartInfo.UseShellExecute = false;          //不使用系统外壳程序启动
            pro.StartInfo.RedirectStandardInput = false;    //不重定向输入
            pro.StartInfo.RedirectStandardOutput = true;    //重定向输出
            pro.StartInfo.CreateNoWindow = true;            //不创建窗口
            try
            {
                if (pro.Start())                            //开始进程
                {
                    StreamReader reader = pro.StandardOutput;//截取输出流  
                    do
                    {
                        string line = reader.ReadLine();        //每次读取一行
                        outStr += line;
                        LogUtil.d("CMD----", line);
                        if (isLog)
                        {
                            LogUtil.PtShowLog(line);
                        }
                    } while (!reader.EndOfStream);

                    if (seconds == 0)
                    {
                        pro.WaitForExit();                  //这里无限等待进程结束
                    }
                    else
                    {
                        pro.WaitForExit(seconds);          //这里等待进程结束,等待时间为指定的毫秒
                    }
                }
            }
            catch(Exception ex)
            {
                LogUtil.showException(ex.Message + Environment.NewLine + ex.StackTrace);//将异常信息放入异常捕获的对话框中
            }
            finally
            {
                if (pro != null)
                {
                    pro.Close();
                }
            }
            return outStr;
        }

        #endregion
    }
}

渠道下载帮助工具

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace YcPackageTool.Utils
{
    class DownLoadFile
    {
        #region 下载更新包
        /// <summary>
        /// 下载更新包
        /// </summary>
        /// <param name="url"></param>
        /// <param name="path"></param>
        /// <param name="channgleCode"></param>
        public static void HttpDownloadFile(string url, String path, String channgleCode)
        {
            try
            {
                String filePath = path + @"\" + channgleCode + ".zip";
                // 设置参数
                HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
                //发送请求并获取相应回应数据
                HttpWebResponse response = request.GetResponse() as HttpWebResponse;
                //直到request.GetResponse()程序才开始向目标网页发送Post请求
                Stream responseStream = response.GetResponseStream();
                //创建本地文件写入流
                Stream stream = new FileStream(filePath, FileMode.Create);
                byte[] bArr = new byte[1024];
                int size = responseStream.Read(bArr, 0, (int)bArr.Length);
                while (size > 0)
                {
                    stream.Write(bArr, 0, size);
                    size = responseStream.Read(bArr, 0, (int)bArr.Length);
                }
                stream.Close();
                responseStream.Close();
                FileIOManager.deleteDirectory(path + "\\" + channgleCode);
                System.IO.Compression.ZipFile.ExtractToDirectory(filePath, path);
                FileIOManager.deleteFile(filePath);
            }
            catch
            {
                LogUtil.d("Download", channgleCode + " --- 资源下载失败");
            }
        
        }
        #endregion
    }
   
}


 

【欢迎上码】

【微信公众号搜索 h2o2s2】

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值