zoukankan      html  css  js  c++  java
  • Android自己主动化构建之Ant多渠道打包实践(下)

    前言

    上一篇(Android自己主动化构建之Ant多渠道打包实践(上))已经介绍了Android的apk是怎样构建的,本篇博客继续Ant打包的实践过程。

    集成友盟统计SDK

    这里以友盟统计为例,对各个渠道进行统计。我们须要先集成它的SDK

    配置权限

        <!-- 权限 -->
        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" >
        </uses-permission>
        <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
        <uses-permission android:name="android.permission.INTERNET" >
        </uses-permission>
        <uses-permission android:name="android.permission.READ_PHONE_STATE" >
        </uses-permission>
    

    渠道配置

     <!-- 友盟统计配置 -->
            <meta-data
                android:name="UMENG_APPKEY"
                android:value="56f0b1ef67e58eded700015b" >
            </meta-data>
            <meta-data android:name="UMENG_CHANNEL" android:value="Umeng" />

    使用Ant打包的时候替换的渠道号就是<meta-data android:name="UMENG_CHANNEL" android:value="Umeng" /> 将Umeng替换为详细的渠道。比方将Umeng替换为xiaomi。

    定义build.properties文件

    这个文件定义了Ant脚本要用到的一些參数值。我们的渠道也是定义在这里。详细看代码:

    
    #project name and version
        project.name=AntBuild
        project.version=4.1.4
    
    #android platform version
        android-platform=android-19
    
    #keysore file  
        ketstore.file=release.keystore
        key.alias=release.keystore
        key.alias.password=123456
        key.store.password=123456
    
    #publish channel
        channelname=Umeng
        channelkey=360,QQ,xiaomi,liangxiang
        key=360,QQ,xiaomi,liangxiang
    
    #library project
        library-dir=../Library
        library-dir2=../Library2
    # generate R.java for libraries. Separate libraries with ':'.
        extra-library-packages=
    
    #filnal out dir
        out.dir=publish
    

    完整的Ant脚本

    <?xml version="1.0" encoding="UTF-8"?

    > <project name="iReaderApp" default="deploy" > <!--打包配置 --> <property file="build.properties" /> <!-- ANT环境变量 --> <property environment="env" /> <!-- 版本号 --> <property name="version" value="${project.version}" /> <!-- 应用名称 --> <property name="appName" value="${project.name}" /> <!-- SDK文件夹(获取操作系统环境变量ANDROID_SDK_HOME的值) --> <property name="sdk-folder" value="${env.ANDROID_SDK_HOME}" /> <!-- SDK指定平台文件夹 --> <property name="sdk-platform-folder" value="${sdk-folder}/platforms/android-19"/> <!-- SDK中tools文件夹 --> <property name="sdk-tools" value="${sdk-folder}/tools" /> <!-- SDK指定平台中tools文件夹 --> <property name="sdk-platform-tools" value="${sdk-folder}/build-tools/android-4.4.2" /> <!-- 使用到的命令 --> <property name="aapt" value="${sdk-platform-tools}/aapt" /> <!-- 第三方library --> <property name="library-dir" value="${library-dir}" /> <property name="library-dir2" value="${library-dir2}" /> <!-- 使用到的命令(当前系统为windows,假设系统为linux,可将.bat文件替换成相相应的命令) --> <property name="aapt" value="${sdk-platform-tools}/aapt" /> <property name="aidl" value="${sdk-platform-tools}/aidl" /> <property name="dx" value="${sdk-platform-tools}/dx.bat" /> <property name="apkbuilder" value="${sdk-tools}/apkbuilder.bat" /> <property name="jarsigner" value="${env.JAVA_HOME}/bin/jarsigner" /> <property name="zipalign" value="${sdk-tools}/zipalign" /> <!-- 编译须要的jar; 假设项目使用到地图服务则须要maps.jar --> <property name="android-jar" value="${sdk-platform-folder}/android.jar" /> <property name="proguard-home" value="${sdk-tools}/proguard/lib" /> <!-- 编译aidl文件所需的预处理框架文件framework.aidl --> <property name="framework-aidl" value="${sdk-platform-folder}/framework.aidl" /> <!-- 清单文件 --> <property name="manifest-xml" value="AndroidManifest.xml" /> <!-- 源文件文件夹 --> <property name="resource-dir" value="res" /> <property name="asset-dir" value="assets" /> <!-- java源文件文件夹 --> <property name="srcdir" value="src" /> <property name="srcdir-ospath" value="${basedir}/${srcdir}" /> <!-- 外部类库所在文件夹 --> <property name="external-lib" value="libs" /> <property name="external-compile-lib" value="compile-libs" /> <property name="external-lib-ospath" value="${basedir}/${external-lib}" /> <property name="external-compile-lib-ospath" value="${basedir}/${external-compile-lib}" /> <property name="external-library-dir-lib-ospath" value="${library-dir}/${external-lib}" /> <property name="external-library-dir2-lib-ospath" value="${library-dir2}/${external-lib}" /> <!-- 使用第三方的ant包,使ant支持for循环--> <taskdef resource="net/sf/antcontrib/antcontrib.properties"> <classpath> <pathelement location="${external-lib-ospath}/ant-contrib-1.0b3.jar" /> </classpath> </taskdef> <property name="channelname" value="${channelname}" /> <property name="channelkey" value="${channelkey}" /> <!-- 渠道名:渠道号 --> <!-- <property name="key" value="UMENG_CHANNEL:goapk,UMENG_CHANNEL:QQ" /> --> <property name="key" value="${key}" /> <!--循环打包 --> <target name="deploy"> <foreach target="modify_manifest" list="${key}" param="nameandchannel" delimiter=","> </foreach> </target> <target name="modify_manifest"> <!-- 获取渠道名字 --> <!-- <propertyregex override="true" property="channelname" input="${nameandchannel}" regexp="(.*):" select="1" /> --> <!-- 获取渠道号码 --> <propertyregex override="true" property="channelkey" input="${nameandchannel}" regexp="(.*)" select="1" /> <!-- 正则匹配替换渠道号(这里pattern里的内容要与mainfest文件的内容一致,包含顺序,空格) --> <replaceregexp flags="g" byline="false" encoding="UTF-8"> <regexp pattern='meta-data android:name="UMENG_CHANNEL" android:value="(.*)"' /> <substitution expression='meta-data android:name="UMENG_CHANNEL" android:value="${channelkey}"' /> <fileset dir="" includes="AndroidManifest.xml" /> </replaceregexp> <antcall target="zipalign" /> </target> <!-- 初始化工作 --> <target name="init"> <echo>文件夹初始化....</echo> <!-- 生成R文件的相对文件夹 --> <var name="outdir-gen" value="gen" /> <!-- 编译后的文件放置文件夹 --> <var name="outdir-bin" value="${out.dir}/${channelkey}" /> <!-- 生成class文件夹 --> <var name="outdir-classes" value="${outdir-bin}/otherfile" /> <var name="outdir-classes-ospath" value="${basedir}/${outdir-classes}" /> <!-- classes.dex相关变量 --> <var name="dex-file" value="classes.dex" /> <var name="dex-path" value="${outdir-bin}/${dex-file}" /> <var name="dex-ospath" value="${basedir}/${dex-path}" /> <!-- 经过aapt生成的资源包文件 --> <var name="resources-package" value="${outdir-bin}/resources.ap_" /> <var name="resources-package-ospath" value="${basedir}/${resources-package}" /> <!-- 未认证apk包 --> <var name="out-unsigned-package" value="${outdir-bin}/${appName}-unsigned.apk" /> <var name="out-unsigned-package-ospath" value="${basedir}/${out-unsigned-package}" /> <!-- 证书文件 --> <var name="keystore-file" value="${basedir}/${ketstore.file}" /> <!-- <span style="white-space:pre"> </span> 当前时间 --> <!-- <span style="white-space:pre"> </span><tstamp> --> <!-- <span style="white-space:pre"> </span> <format property="nowtime" pattern="yyyyMMdd"></format>--> <!-- <span style="white-space:pre"> </span></tstamp> --> <!-- 已认证apk包 --> <var name="out-signed-package" value="${outdir-bin}/${appName}_${channelkey}_${version}.apk" /> <var name="out-signed-package-ospath" value="${basedir}/${out-signed-package}" /> <delete dir="${outdir-bin}" /> <mkdir dir="${outdir-bin}" /> <mkdir dir="${outdir-classes}" /> </target> <!-- 依据工程中的资源文件生成R.java文件 --> <target name="gen-R" depends="init"> <echo>生成R.java文件....</echo> <exec executable="${aapt}" failonerror="true"> <arg value="package" /> <arg value="-m" /> <arg value="--auto-add-overlay" /> <arg value="-J" /> <!--R.java文件的生成路径--> <arg value="${outdir-gen}" /> <!-- 指定清单文件 --> <arg value="-M" /> <arg value="${manifest-xml}" /> <!-- 指定资源文件夹 --> <arg value="-S" /> <arg value="${resource-dir}" /> <arg value="-S" /> <arg value="${library-dir}/res" /><!-- 注意点:同一时候须要调用Library的res--> <arg value="-S" /> <arg value="${library-dir2}/res" /><!-- 注意点:同一时候须要调用Library的res--> <!-- 导入类库 --> <arg value="-I" /> <arg value="${android-jar}" /> </exec> </target> <!-- 编译aidl文件 --> <target name="aidl" depends="gen-R"> <echo>编译aidl文件....</echo> <apply executable="${aidl}" failonerror="true"> <!-- 指定预处理文件 --> <arg value="-p${framework-aidl}" /> <!-- aidl声明的文件夹 --> <arg value="-I${srcdir}" /> <!-- 目标文件文件夹 --> <arg value="-o${outdir-gen}" /> <!-- 指定哪些文件须要编译 --> <fileset dir="${srcdir}"> <include name="**/*.aidl" /> </fileset> </apply> </target> <!-- 将工程中的java源文件编译成class文件 --> <target name="compile" depends="aidl"> <echo>java源文件编译成class文件....</echo> <!-- 库应用1编译class 生成的class文件所有保存到outdir-classes文件夹下--> <javac encoding="UTF-8" destdir="${outdir-classes}" bootclasspath="${android-jar}"> <src path="${library-dir}/src" /><!-- 库应用源代码 --> <src path="${outdir-gen}" /><!-- R.java 资源类的导入 --> <classpath> <fileset dir="${external-library-dir-lib-ospath}" includes="*.jar" /><!-- 第三方jar包须要引用,用于辅助编译 --> </classpath> </javac> <!-- 库应用2编译class --> <javac encoding="UTF-8" destdir="${outdir-classes}" bootclasspath="${android-jar}"> <src path="${library-dir2}/src" /><!-- 库应用源代码 --> <src path="${outdir-gen}" /><!--生成的class文件所有保存到bin/classes文件夹下 --> <classpath> <fileset dir="${external-library-dir2-lib-ospath}" includes="*.jar" /><!-- 第三方jar包须要引用,用于辅助编译 --> </classpath> </javac> <!-- 主应用编译class --> <javac encoding="UTF-8" destdir="${outdir-classes}" bootclasspath="${android-jar}" > <compilerarg line="-encoding UTF-8 " /> <!-- <compilerarg line="-encoding UTF-8 "/> --> <src path="${basedir}/src" /><!-- 工程源代码--> <src path="${outdir-gen}" /><!--R.java 资源类的导入 --> <!-- 编译java文件依赖jar包的位置 --> <classpath> <fileset dir="${external-lib}" includes="*.jar" /><!-- 第三方jar包须要引用,用于辅助编译 --> <!-- <fileset dir="${external-compile-lib}" includes="*.jar"/>第三方jar包须要引用,用于辅助编译 --> <fileset dir="${external-library-dir-lib-ospath}" includes="*.jar" /><!-- 第三方jar包须要引用,用于辅助编译 --> </classpath> </javac> </target> <!--运行代码混淆--> <!-- 将.class文件转化成.dex文件 --> <target name="dex" depends="compile" unless="do.not.compile"> <echo>将.class文件转化成.dex文件....</echo> <exec executable="${dx}" failonerror="true"> <arg value="--dex" /> <!-- 输出文件 --> <arg value="--output=${dex-ospath}" /> <!-- 要生成.dex文件的源classes和libraries --> <arg value="${outdir-classes-ospath}" /> <arg value="${external-lib-ospath}" /> <!-- <arg value="${external-library-dir-lib-ospath}" /> <arg value="${external-library-dir2-lib-ospath}" /> --> </exec> </target> <!-- 将资源文件放进输出文件夹 --> <target name="package-res-and-assets"> <echo>将资源文件放进输出文件夹....</echo> <exec executable="${aapt}" failonerror="true"> <arg value="package" /> <arg value="-f" /> <arg value="-M" /> <arg value="${manifest-xml}" /> <arg value="-S" /> <arg value="${resource-dir}" /> <arg value="-S"/> <arg value="${library-dir}/res"/> <arg value="-S"/> <arg value="${library-dir2}/res"/> <arg value="-A" /> <arg value="${asset-dir}" /> <arg value="-I" /> <arg value="${android-jar}" /> <arg value="-F" /> <arg value="${resources-package}" /> <arg value="--auto-add-overlay" /> </exec> </target> <!-- 打包成未签证的apk --> <target name="package" depends="dex,package-res-and-assets"> <echo>打包成未签证的apk....</echo> <java classpath="${sdk-tools}/lib/sdklib.jar" classname="com.android.sdklib.build.ApkBuilderMain"> <!-- 输出路径 --> <arg value="${out-unsigned-package-ospath}" /> <arg value="-u" /> <arg value="-z" /> <!-- 资源压缩包 --> <arg value="${resources-package-ospath}" /> <arg value="-f" /> <!-- dex压缩文件 --> <arg value="${dex-ospath}" /> <arg value="-rj" /> <arg value="${external-lib-ospath}"/> <!-- 将主项目libs以下的so库打包 --> <arg value="-nf" /> <arg value="${external-lib-ospath}" /> </java> </target> <!-- 对apk进行签证 --> <target name="jarsigner" depends="package"> <echo>Packaging signed apk for release...</echo> <exec executable="${jarsigner}" failonerror="true"> <arg value="-keystore" /> <arg value="${keystore-file}" /> <arg value="-storepass" /> <arg value="${key.store.password}" /> <arg value="-keypass" /> <arg value="${key.alias.password}" /> <arg value="-signedjar" /> <arg value="${out-signed-package-ospath}" /> <arg value="${out-unsigned-package-ospath}" /> <!-- 不要忘了证书的别名 --> <arg value="${key.alias}" /> </exec> </target> <!-- 公布 --> <target name="release" depends="jarsigner"> <!-- 删除未签证apk --> <delete file="${out-unsigned-package-ospath}" /> <echo>APK is released. path:${out-signed-package-ospath}</echo> <echo>删除其它文件,最后仅仅保留apk</echo> <delete dir="${outdir-classes}"/> <delete file="${dex-ospath}" /> <delete file="${resources-package-ospath}" /> <echo>生成apk完毕</echo> </target> <!-- 打包的应用程序进行优化 --> <target name="zipalign" depends="release"> <exec executable="${zipalign}" failonerror="true"> <arg value="-v" /> <arg value="4" /> <arg value="${out-signed-package-ospath}" /> <arg value="${out-signed-package-ospath}-zipaligned.apk" /> </exec> </target> </project>

    上面就是完整的Ant脚本。实现了自己主动化构建和多渠道的打包。笔者在实践的过程踩过不少坑才终于把apk包成功打出。

    这里总结下可能遇到的坑:
    - 生成R.java文件,一定要注意先后顺序。主项目之后才到关联项目
    - 编译生成class文件,可能会遇到找不到类,一定要依照加入库的顺序来编译class文件
    - 替换渠道号的时候,Ant中pattern里的内容要与mainfest文件的内容一致,包含顺序,空格),笔者试过格式化后代码之后就不能写入成功

    build.bat脚本

    
    @echo off
    call ant -buildfile "build.xml" deploy
    echo done
    pause
    exit

    測试结果

    我们能够在项目中的publish文件夹下生成不同渠道的apk文件:

    这里写图片描写叙述

    安装apk到设备,启动之后在友盟后台集成測试,看app公布的渠道:
    历史纪录

    Demo样例欢迎大家star

    https://github.com/devilWwj/Android-Tech/tree/master/AntBuildTest

    总结

    实现Ant多渠道打包整个过程还是比較繁琐的,主要在Ant脚本上。比較easy出错。须要对命令比較了解,但确实能够缩短我们打渠道包的时间。基于本次实践是基于Eclipse。眼下Android Studio使用gradle来实现多渠道打包。以后会把gradle进行多渠道打包的实现分享给大家,大家能够对照下这两种打包方式的差别,主要目的是更加深入的了解apk的构建过程。


    欢迎关注我的公众号:wwjblog

     wwjblog

  • 相关阅读:
    mac c++编译出现segmentation fault :11错误
    ssh 连接缓慢解决方法
    237. Delete Node in a Linked List
    203. Remove Linked List Elements
    Inversion of Control Containers and the Dependency Injection pattern
    82. Remove Duplicates from Sorted List II
    83. Remove Duplicates from Sorted List
    SxsTrace
    使用CCleaner卸载chrome
    decimal and double ToString problem
  • 原文地址:https://www.cnblogs.com/blfbuaa/p/7281381.html
Copyright © 2011-2022 走看看