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
總結
實現Ant多渠道打包整個過程還是比較繁瑣的,主要在Ant指令碼上,比較容易出錯,需要對命令比較瞭解,但確實能夠縮短我們打渠道包的時間,基於本次實踐是基於Eclipse,目前Android Studio使用gradle來實現多渠道打包,以後會把gradle進行多渠道打包的實現分享給大家,大家可以對比下這兩種打包方式的區別,主要目的是更加深入的瞭解apk的構建過程。
歡迎關注我的公眾號:wwjblog
相關推薦
Android自動化構建之Ant多渠道打包實踐(下)
前言 上一篇(Android自動化構建之Ant多渠道打包實踐(上))已經介紹了Android的apk是如何構建的,本篇部落格繼續Ant打包的實踐過程。 整合友盟統計SDK 這裡以友盟統計為例,對各個渠道進行統計,我們需要先整合它的SDK 配置許可權
Android自動化構建之Ant多渠道打包實踐(上)
前言 Ant是歷史比較悠久的一個自動化構建工具,Android開發者可以通過它來實現自動化構建,也可以實現多渠道打包,關於apk打包的方式一般有Ant、Python、Gradle三種,這三種打包方式都各自有優點和缺點,本篇博文先給大家介紹如何使用Ant來實現自
Android學習探索之App多渠道打包及動態添加修改資源屬性
Android App 前言: 關於Android渠道打包是一個比較老的話題,今天主要記錄總結一下多渠道打包以及如果動態配置修改一些資源屬性。今天以公司實際需求為例進行演示,由於項目復用很多公共的業務組件,而且業務組件之間的跳轉采用Scheme協議,每個業務組
Android 自動化構建之Jenkins配置
需求: 前面實現了通過python來實現自動化構建上傳加固,但是隻能滿足開發人員來實現此操作,近期公司需要配置Jenkins來完成這項這項操作,以滿足測試人員自由構建獲取apk。 Jenkins的下載安裝啟動以及下載外掛不在此文章之列! 一,登入 二,管理Je
Android多渠道打包彙總(六)—— 美團的多渠道打包方式
1. 原理 大家都知道,apk檔案其實可以看做是一個壓縮包,我們把一個Android應用包當作zip檔案包進行解壓,然後發現在簽名生成的目錄下(META-INF)新增一個空檔案不需要重新簽名。利用這個機制,該檔案的檔名就是渠道名。這種方式不需要重新簽名等步驟,
Android多渠道打包彙總(四)—— 為什麼要進行多渠道打包
1. 什麼是多渠道包? 渠道包就是要在安裝包中新增渠道資訊,也就是channel,對應不同的渠道,例如:小米市場、360市場、應用寶市場等 2. 為什麼要提供多渠道包? 我們要在安裝包中新增不同的標識,應用在請求網路的時候攜帶渠道資訊,方便後臺做運營統
Android開發技巧之:QQ第三方登入(二)
接 android QQ第三方登入(一) 獲取登入使用者名稱資訊,這邊先抱怨一下,官方API有點坑 Constants原始碼類下就是找不到GRAPH_SIMPLE_USER_INFO這個屬性!無語! 根據官方提供返回的的JSONObject資訊解析:
Android開發技巧之:QQ第三方登入(一)
使用的是Android_SDK_V2.9.1,建議使用最新版; 官方下載:SDK下載 Android studio 中新增到 然後在點選build.gradle檔案新增 配置AndroidManifest 在應用的Andr
Android網路程式設計之傳遞資料給伺服器(二)
我曾在《Android網路程式設計之傳遞資料給伺服器(一) 》一文中介紹瞭如何通過GET方式傳遞資料給伺服器,通過GET方式傳遞資料主要適用於資料大小不超過2KB,且對安全性要求不高的情況下。下面就介紹通過POST方式傳遞資料主到伺服器。 一、通過P
ant自動打包指令碼(二)
融合SDK ant自動打包指令碼,可以迴圈打包,採用源工程(主工程)和依賴庫的形式 <?xml version="1.0" encoding="UTF-8"?> <project name="AndroidProjectBuild" default="bu
Android-PickerView系列之介紹與使用篇(一)
宣告:本文為博主原創文章,轉載請註明出處:小嵩的部落格 一、介紹 Android-PickerView是一款仿iOS的PickerView控制元件,並封裝了時間選擇和選項選擇這兩種選擇器,詳細特性如下: WheelView —— 基礎控制元件
[Android] Android開發優化之——對介面UI的優化(2)
在一個應用程式中,一般都會存在多個Activity,每個Activity對應著一個UI佈局檔案。一般來說,為了保持不同視窗之間的風格統一,在這些UI佈局檔案中,幾乎肯定會用到很多相同的佈局。如果我們在每個xml檔案中都把相同的佈局都重寫一遍,一個是程式碼冗餘,可讀性
Android硬體操作之低功耗藍芽(一) 硬體篇
一、綜述 我入手的藍芽模組是HC08模組,屬於低功耗藍芽的一種,廠家已經做好了一些封裝,直接使用串列埠 就能通訊。我挺喜歡這種傻瓜式的操作方式的,我和同學一起買了4塊藍芽模組,HC08和HC06個一塊
android原始碼分析之View的事件分發(上)
1、View的繼承關係圖 View的繼承關係圖如下: 其中最重要的子類為ViewGroup,View是所有UI元件的基類,而ViewGroup是容納這些元件的容器,同時它也是繼承於View類。而UI元件的繼承關係如上圖,比較常用的元件類用紅色字型標出
Android開發優化之——對介面UI的優化(3)
本篇博文主要討論一下複雜介面中常用的一種技術——介面延遲載入技術。 有時候,我們的頁面中可能會包含一些佈局,這些佈局預設是隱藏的,當用戶觸發了一定的操作之後,隱藏的佈局才會顯示出來。比如,我們有一個Activity用來顯示好友的列表,當用戶點選Menu中的“匯入”以後
支援資原始檔替換的多渠道打包外掛(四)
一、寫在前面 經過前面幾篇文章的學習(什麼?你還沒看,趕緊去補補!),對gradle已經有了大致的瞭解了,當學習完後一定需要碼程式碼來鞏固一下,所謂:talk is cheap show me the code!今天就帶大家來一場gradle實戰,
修羅場第二天:C#之面向對象基礎(下)
dog 主函數 div 接口 對象 blank 返回值 情況 抽象 ------------接(上)http://www.cnblogs.com/HoloSherry/p/7100795.html 抽象類 抽象類也可以實現多態,使用關鍵字abstract。那麽什
《Flask Web開發——基於Python的Web應用開發實踐》一字一句上機實踐(下)
屬性 一個用戶 臨時 target 說明 實戰 分享圖片 ace 庫文件 目錄 前言 第8章 用戶認證 第9章 用戶角色 第10章 用戶資料 第11章 博客文章 第12章 關註者 第13章 用戶評論 第14章 應用編程接口 前言
vSphere虛擬化之外部存儲部署(下)
自動 我的博客 sqlserve vpd windows7 water 完成後 圖片 完成 vClient工具操作vCenter連接外部共享存儲環境:vCenter、SQLServer數據庫服務器、DC\DNS服務器各一臺;windows7辦公機安裝vClient連接上vC
2018年Android面試題含答案--適合中高級(下)
所在 sync gpo 成了 廠商 end 解釋 同步 變換 1、Activity生命周期? onCreate() -> onStart() -> onResume() -> onPause() -> onStop() -> onDetroy(