使用ANT,实现一套代码生成多个不同的APK

521 查看

最近由于项目需要,要在自有平台上发布若干个代码一致,但包名,版本,引用资源都不同的App。结合了一下网上查到的资料,成功的使用ant实现。

主要需求

  1. 重用android项目生成的build.xml来生成apk,复用它的release,debug等target
  2. 操作前后不需要手工对源代码进行修改,保证源代码能正常运行
  3. 通过build参数的不同,实现一个代码树生成多个版本的apk。

步骤

思路:

使用根目录下的build.xml 来把代码和相关文件复制到临时文件内,并进行对应版本的处理,再在临时文件夹内使用来调用android生成的build.xml。实验相关代码如下:

<project name="main" default="subant">
    <target name="subant"> 
        <subant target="release"><!--subant相当于子调用-->
            <property name="basedir" value="./ant_temp/"/> <!--重要:这一行指定了subant执行时的工作目录-->
            <fileset dir="./ant_temp/" includes="build.xml"/><!--调用临时目录的build.xml,即项目原自带build.xml-->
        </subant>
    </target>
</project>

目录配置:

版本特定内容,分目录放到指定目录下:

custom_versions/{version}/yoyo_project.properties #配置的properties文件
custom_versions/{version}/res #资源目录    

文件复制:

在进行apk的build之前,将代码,资源以及manifest等需要的文件复制至临时目录:

<echo message="Create temp folder ./ant_temp ..."/>

<mkdir dir="./ant_temp"/>

<echo message="Copy files to ./ant_temp ..."/>

<copy todir="./ant_temp/assets">
    <fileset dir="./assets"/>
</copy>

...

<copy file="./android.keystore" tofile="./ant_temp/android.keystore"/>
<copy file="./android_build.xml" tofile="./ant_temp/build.xml"/>
<copy file="./AndroidManifest.xml" tofile="./ant_temp/AndroidManifest.xml"/>

...

对应项目的资源文件复制到临时文件来覆盖通用的资源:

<copy todir="./ant_temp/res" overwrite="true">
    <fileset dir="./custom_versions/${version}/res"/>
</copy>

读取配置

复制后还需要对配置文件的内容进行修改,替换到对应的版本。首先从资源文件读取项目属性:

 <loadproperties srcFile="custom_versions/${version}/yoyo_project.properties"/>

属性包括:

{u9.package}: 包名
{u9.versionName}: 版本号
{u9.versionCode}: 版本编号
{u9.channelName}: 渠道号

修改文件内容

替换文件内容目前使用的是正则来替换,如果有必要的话可以改成xpath。

对AndroidManifest.xml做制定,替换包名,版本号:

<replaceregexp
    byline="true"
    encoding="utf-8"
    file="./ant_temp/AndroidManifest.xml"
    match='package="([^"]+)"'
    replace='package="${u9.package}"'/>

<replaceregexp
    encoding="utf-8"
    flags="s"
    file="./ant_temp/AndroidManifest.xml"
    match='versionCode="[^"]+".+versionName="[^"]+"'
    replace='versionCode="${u9.versionCode}" versionName="${u9.versionName}"'/>


<replaceregexp
    byline="true"
    encoding="utf-8"
    file="./ant_temp/AndroidManifest.xml"
    match='android:authorities="([^"]+)"'
    replace='android:authorities="${u9.package}\.downloads"'/>

<replaceregexp
    encoding="utf-8"
    file="./ant_temp/AndroidManifest.xml"
    flags="s"
    match='android:name="UMENG_CHANNEL".+android:value="([^"]+)"'
    replace='android:name="UMENG_CHANNEL" android:value="${u9.channel}"'/>

由于包名换了,所以需要替换代码内的R文件引用路径:

<replaceregexp
    match='import (.+)\.R;'
    encoding="utf-8"
    replace='import ${u9.package}.R;'>
    <fileset dir="./ant_temp/src">
        <include name="**/*.java"/>
    </fileset>
</replaceregexp>

替换完成后调用之前的ant任务。

完整Ant文件:

<?xml version="1.0" encoding="UTF-8"?>
<project name="YoYoMain" >

    <echo message="Create temp folder ./ant_temp ..."/>

    <mkdir dir="./ant_temp"/>
    <mkdir dir="./build"/>

    <echo message="Copy files to ./ant_temp ..."/>

    <copy todir="./ant_temp/assets">
        <fileset dir="./assets"/>
    </copy>

    <copy todir="./ant_temp/jni">
        <fileset dir="./jni"/>
    </copy>

    <copy todir="./ant_temp/libs">
        <fileset dir="./libs"/>
    </copy>

    <copy todir="./ant_temp/obj">
        <fileset dir="./obj"/>
    </copy>

    <copy todir="./ant_temp/res">
        <fileset dir="./res"/>
    </copy>

    <copy todir="./ant_temp/src">
        <fileset dir="./src"/>
    </copy>
    <copy file="./android.keystore" tofile="./ant_temp/android.keystore"/>
    <copy file="./ant.properties" tofile="./ant_temp/ant.properties"/>
    <copy file="./android_build.xml" tofile="./ant_temp/build.xml"/>
    <copy file="./AndroidManifest.xml" tofile="./ant_temp/AndroidManifest.xml"/>
    <copy file="./lint.xml" tofile="./ant_temp/lint.xml"/>
    <copy file="./custom_versions/custom_rules.xml" tofile="./ant_temp/custom_rules.xml"/>
    <copy file="./project.properties" tofile="./ant_temp/project.properties"/>
    <copy file="./proguard-project.txt" tofile="./ant_temp/proguard-project.txt"/>

    <loadproperties srcFile="custom_versions/${version}/yoyo_project.properties"/>

    <copy todir="./ant_temp/res" overwrite="true">
        <fileset dir="./custom_versions/${version}/res"/>
    </copy>


    <echo message="Rewrite AndroidManifest.xml"/>

    <replaceregexp
        byline="true"
        encoding="utf-8"
        file="./ant_temp/AndroidManifest.xml"
        match='package="([^"]+)"'
        replace='package="${u9.package}"'/>

    <replaceregexp
        encoding="utf-8"
        flags="s"
        file="./ant_temp/AndroidManifest.xml"
        match='versionCode="[^"]+".+versionName="[^"]+"'
        replace='versionCode="${u9.versionCode}" versionName="${u9.versionName}"'/>


    <replaceregexp
        byline="true"
        encoding="utf-8"
        file="./ant_temp/AndroidManifest.xml"
        match='android:authorities="([^"]+)"'
        replace='android:authorities="${u9.package}\.downloads"'/>

    <replaceregexp
        encoding="utf-8"
        file="./ant_temp/AndroidManifest.xml"
        flags="s"
        match='android:name="UMENG_CHANNEL".+android:value="([^"]+)"'
        replace='android:name="UMENG_CHANNEL" android:value="${u9.channel}"'/>

    <replaceregexp
        match='import (.+)\.R;'
        encoding="utf-8"
        replace='import ${u9.package}.R;'>
        <fileset dir="./ant_temp/src">
            <include name="**/*.java"/>
        </fileset>
    </replaceregexp>

    <target name="subant">
        <subant target="release">
            <property name="basedir" value="./ant_temp/"/>
            <property name="final_name" value="YoYo-${u9.channel}-${u9.versionName}.apk"/>
            <fileset dir="./ant_temp/" includes="build.xml"/>
        </subant>
    </target>
</project>

调用:

build版本的时候如下:

ant -Dversion=official #版本所在的目录名称

即可。