『壹』 android多渠道快速打包實踐
參考資料:
美團Android自動化之旅—生成渠道包
Android批量打包提速
AndroidMultiChannelBuildTool
背景
隨著發版需要,每次發版所需渠道包越來越多(現在差不多有一百個左右了),正常gradle打包由於耗時效率過低已無法滿足需求,開始了android多渠道快速打包實踐。
方法
下面主要介紹兩種快速打包的方式:
1、類似美團的方式,在META-INF中寫入渠道名的空文件,用於讀取空文件。 美團Android自動化之旅—生成渠道包
2、在apk末尾動態寫入渠道信息。 一種動態為apk寫入信息的方案
其實這兩種方式都是同一個原理,替換以前從manifest中讀取渠道號的方式,而使用新的獲取方式(渠道號如何寫入就如何讀取)。
所以這首先需要客戶端(重要!):
1、統一應用中獲取渠道的方式並替換之前的(最好兼容)。
2、注意第三方SDK渠道號的傳入,比如友盟sdk,否則第三方會使用默認從manifest中讀取的方式。
下面介紹一種已經測試過的方法(git上開源項目 AndroidMultiChannelBuildTool )
1、安裝環境由於腳本環境是使用python語言,所以需要我們 安裝環境 。
2、導入項目導入開源項目 AndroidMultiChannelBuildTool ),並把想要批量打包的apk文件拷貝到PythonTool目錄下(與py同級),運行py腳本即可打包完成。
以上基本實現快速打包,經過測試一分鍾百十個無壓力。另外需要注意這種方式只適用於打包需求一致渠道號不同,不適用特殊定製渠道。
備註:9月21日補充快速打包java版本,詳見 AndroidMultiChannelBuildTool-Java-master
『貳』 android如何使用ant批量打包
ps :後期熟悉ant的話,可以使用純ant腳本或者使用另一種更好的自動化打包工具(maven)關鍵代碼如下:package com.cn.ant; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import org.apache.tools.ant.DefaultLogger; import org.apache.tools.ant.Project; import org.apache.tools.ant.ProjectHelper; public class AntTest { private Project project; public void init(String _buildFile, String _baseDir) throws Exception { project = new Project(); project.init(); DefaultLogger consoleLogger = new DefaultLogger(); consoleLogger.setErrorPrintStream(System.err); consoleLogger.setOutputPrintStream(System.out); consoleLogger.setMessageOutputLevel(Project.MSG_INFO); project.addBuildListener(consoleLogger); // Set the base directory. If none is given, "." is used. if (_baseDir == null) _baseDir = new String("."); project.setBasedir(_baseDir); if (_buildFile == null) _buildFile = new String(projectBasePath + File.separator + "build.xml"); //ProjectHelper.getProjectHelper().parse(project, new File(_buildFile)); // 關鍵代碼 ProjectHelper.configureProject(project, new File(_buildFile)); } public void runTarget(String _target) throws Exception { // Test if the project exists if (project == null) throw new Exception( "No target can be launched because the project has not been initialized. Please call the 'init' method first !"); // If no target is specified, run the default one. if (_target == null) _target = project.getDefaultTarget(); // Run the target project.executeTarget(_target); } // private final static ArrayList<String> flagList = new ArrayList<String>(); //也可以使用集合,不過需要手動添加項 private final static String[] flagList = new String[]{"htc","moto","oppo"};//此處初始化市場標識 private final static String projectBasePath = ""; //項目的根目錄,需要配置 private final static String ApkPath = ""; //要改名後拷貝的目錄,需要配置 private final static String placeHolder = ""; //佔位符,需要配置 public static void main(String args[]) { //flagList.add("htc"); //flagList.add("moto"); //flagList.add("oppo"); try { System.out.println("---------ant批量自動化打包開始----------"); for(String flag : flagList){ //先修改manifest文件:讀取臨時文件中的@market@修改為市場標識,然後寫入manifest.xml中 String tempFilePath = projectBasePath + File.separator + "AndroidManifest.xml.temp"; String filePath = projectBasePath + File.separator + "AndroidManifest.xml"; write(filePath,read(tempFilePath, flag)); //執行打包命令 AntTest mytest = new AntTest(); mytest.init( projectBasePath + File.separator + "build.xml", projectBasePath); mytest.runTarget("clean"); mytest.runTarget("release"); //打完包後執行重命名加拷貝操作 //String flag = "htc"; File file = new File(projectBasePath + File.separator +"bin"+File.pathSeparator+"MainActivity-release.apk");//bin目錄下簽名的apk文件 file.renameTo(new File(ApkPath + File.separator + "MainActivity_"+flag+".apk")); } System.out.println("---------ant批量自動化打包結束----------"); } catch (Exception e) { e.printStackTrace(); System.out.println("---------ant批量自動化打包中發生異常----------"); } } public static String read(String filePath,String replaceStr) { BufferedReader br = null; String line = null; StringBuffer buf = new StringBuffer(); try { // 根據文件路徑創建緩沖輸入流 br = new BufferedReader(new FileReader(filePath)); // 循環讀取文件的每一行, 對需要修改的行進行修改, 放入緩沖對象中 while ((line = br.readLine()) != null) { // 此處根據實際需要修改某些行的內容 if (line.contains(placeHolder)) { line = line.replace(placeHolder, replaceStr); buf.append(line); } else { buf.append(line); } buf.append(System.getProperty("line.separator")); } } catch (Exception e) { e.printStackTrace(); } finally { // 關閉流 if (br != null) { try { br.close(); } catch (IOException e) { br = null; } } } return buf.toString(); } /** * 將內容回寫到文件中 * * @param filePath * @param content */ public static void write(String filePath, String content) { BufferedWriter bw = null; try { // 根據文件路徑創建緩沖輸出流 bw = new BufferedWriter(new FileWriter(filePath)); // 將內容寫入文件中 bw.write(content); } catch (Exception e) { e.printStackTrace(); } finally { // 關閉流 if (bw != null) { try { bw.close(); } catch (IOException e) { bw = null; } } } } }
『叄』 新一代Android渠道打包工具:1000個渠道包只需要5秒
♥♥♥ 原文轉自 極分享 更多詳情及更新 查看原文 ♥♥♥
最新版本
v1.0.4 - 2016.01.19 - 完善獲取APK路徑的方法,增加MarketInfo
v1.0.3 - 2016.01.14 - 增加緩存,新增ResUtils,更有好的錯誤提示
v1.0.2 - 2015.12.04 - 兼容proctFlavors,完善異常處理
v1.0.1 - 2015.12.01 - 如果沒有讀取到渠道,默認返回空字元串
v1.0.0 - 2015.11.30 - 增加Java和Python打包腳本,增加文檔
v0.9.9 - 2015.11.26 - 測試版發布,支持全新的極速打包方式
源碼:https://github.com/mcxiaoke/packer-ng-plugin
項目介紹
packer-ng-plugin 是下一代Android渠道打包工具Gradle插件,支持極速打包,1000個渠道包只需要5秒鍾,速度是 gradle-packer-plugin 的1000倍以上,可方便的用於CI系統集成,支持自定義輸出目錄和最終APK文件名,依賴包:com.mcxiaoke.gradle:packer-ng:1.0.+ 簡短名:packer,可以在項目的 build.gradle 中指定使用,還提供了命令行獨立使用的Java和Python腳本。實現原理見本文末尾。
使用指南
Maven Central
.
.
.
.
.
實現原理
PackerNg原理
優點
使用APK注釋欄位保存渠道信息和MAGIC位元組,從文件末尾讀取渠道信息,速度快
實現為一個Gradle Plugin,支持定製輸出APK的文件名等信息,方便CI集成
提供Java版和Python的獨立命令行腳本,不依賴Gradle插件,支持獨立使用
由於打包速度極快,單個包只需要5毫秒左右,可用於網站後台動態生成渠道包
缺點
沒有使用Android的proctFlavors,無法利用flavors條件編譯的功能
文件格式
Android應用使用的APK文件就是一個帶簽名信息的ZIP文件,根據 ZIP文件格式規范,每個ZIP文件的最後都必須有一個叫Central Directory Record 的部分,這個CDR的最後部分叫"end of central directory record",這一部分包含一些元數據,它的末尾是ZIP文件的注釋。注釋包含Comment Length和File Comment兩個欄位,前者表示注釋內容的長度,後者是注釋的內容,正確修改這一部分不會對ZIP文件造成破壞,利用這個欄位,我們可以添加一些自定義的數據,PackerNg項目就是在這里添加和讀取渠道信息。
細節處理
原理很簡單,就是將渠道信息存放在APK文件的注釋欄位中,但是實現起來遇到不少坑,測試了好多次。
同類工具
gradle-packer-plugin - 舊版渠道打包工具,完全使用Gradle系統實現,能利用Android提供的proctFlavors系統的條件編譯功能,無任何兼容性問題,方便集成,但是由於每次都要重新打包,速度比較慢,不適合需要大量打包的情況。(性能:200個渠道包需要一到兩小時)
Meituan-MultiChannelTool - 使用美團方案的實現,在APK文件的META-INF目里增加渠道文件,打包速度也非常快,但讀取時需要遍歷APK文件的數據項,比較慢,而且以後可能遇到兼容性問題
MultiChannelPackageTool - 將渠道寫入APK文件的注釋,這個項目沒有提供Gradle插件,只有命令行工具,不方便CI集成,使用ZIP文件注釋的思路就是來自此項目
轉自 極分享 閱讀原文
『肆』 如何使用android自帶的ant
Ant是android的編譯打包工具,一個很好的跨平台構建工具,特別是對於Java項目,這里使用它對Android工程進行自動化構建可以得到非常大的便利。一般來說對Android工程進行構。
使用Ant搭建Android開發環境,建立android項目
配置Ant環境在windows上應該選擇zip壓縮包,將zip壓縮包解壓到一個目錄。
打開系統環境變數,在系統變數欄點擊新建,變數名輸入「ANT_HOME」,變數值為Ant的根目錄,如「D:\Android\apache-ant-1.9.0」,注意不要帶雙引號。
在系統變數中找到Path變數,點擊編輯,在變數值的最後添加「%ANT_HOME%\bin」,注意不要帶雙引號,並且要使用「;」和之前的變數值隔開。
打開一個cmd窗口,輸入「ant」,如果顯示一下信息,說明Ant的環境配置成功,如果顯示:'ant'
不是內部或外部命令,也不是可運行的程序或批處理文件。則要檢查一下路徑是否有問題。
在Eclipse中配置Ant
在eclipse中使用Ant之前,為了使Ant的build.xml文件能夠安裝制定的格式進行縮進和高亮顯示,並能夠進行代碼提示,首先要簡單的設置一下。
打開Windows - Preferences,依次展開General,Editors,選中File Associations,點擊Add...,在Add
File Type對話框中輸入build.xml,點擊Ok。
接下來在File type:欄選中build.xml,在Associated Editor:欄選中Ant
Editor,點擊Default,build.xml的圖標變成了一個小螞蟻,配置完畢。
使用Ant編譯Android的java代碼和native代碼
新建一個Android工程TestAnt,在工程的根目錄下新建一個build.xml文件
輸入以下內容:
<?xml version="1.0" encoding="UTF-8"?><project name="TestAnt" default="init"> <target name="init"> <fail message="Ant 1.7.0 or higher is required."> <condition> <not> <antversion property="ant.version" atleast="1.7.0" /> </not> </condition> </fail> </target> </project>
打開cmd,切換到工程根目錄,輸入ant init
編譯成功,來解析這個build.xml:
<project name="TestAnt" default="init">
project是Ant工程的根節點,name屬性是工程的名稱,default是默認執行的target,init為默認的target,當我們輸入Ant的時候和Ant
init是一樣的效果。
<target name="init"> <fail message="Ant 1.7.0 or higher is required."> <condition> <not> <antversion property="ant.version" atleast="1.7.0" /> </not> </condition> </fail> </target>
target指定了要執行的操作,init是我們為這個target所起的名字,也可以是build,clean等等。在這個target中,執行的是檢查Ant的版本,如個小於1.7.0的話會輸出報錯信息。
例如我們可以添加一個clean的target
<target name="clean"> <echo message="Deleting temporary files..." /> <delete dir="gen" /> <delete dir="bin" /> <delete dir="out" /> <delete dir="obj" /> <echo message="DONE (Deleting temporary files)" /> </target>
執行的操作是刪除所有臨時目錄,在cmd窗口中輸入Ant clean,這四個臨時目錄就會被刪除,和在eclipse中執行清理是一個效果。
使用Ant編譯Android工程
在SDK中,Google已經為我們寫好了一個build.xml文件,就是sdk根目錄\tools\ant\build.xml,所以我們只要把這個build.xml引入就可以編譯Android工程了。
在這之前首先要新建一個local.properties文件,引入sdk和ndk的路徑
輸入一下內容:
sdk.dir=D:\\Android\\android-sdk
ndk.dir=D:\\Android\\android-ndk
分別為sdk和ndk的路徑,要安裝自己的實際路徑進行配置。
在測試工程的build.xml中輸入一下代碼:
<?xml version="1.0" encoding="UTF-8"?><project name="TestAnt" default="release"> <loadproperties srcFile="local.properties" /> <loadproperties srcFile="project.properties" /> <fail message="sdk.dir is missing. Make sure to generate local.properties using 'android update project'" unless="sdk.dir" /> <fail message="ndk.dir is missing. Make sure to generate local.properties using 'android update project'" unless="ndk.dir" /> <import file="${sdk.dir}/tools/ant/build.xml" /></project>
在命令行中執行ant release或ant debug,就會執行對應的編譯。
以上只是進行Java代碼的編譯,如何編譯native代碼呢,在NDK中Google可沒有提夠build.xml,這就需要我們自己實現。在新版NDK中,我們只要在命令行中切換到工程的根目錄,然後執行
「D:\Android\android-ndk\ndk-build.cmd」(紅色部分要替換成自己的路徑)
就可以,例如,在TestAnt工程中添加native代碼
執行ndk-build
所以我們只要在Ant中執行一個cmd命令就可以了,在build.xml中添加
<target name="native"> <echo message="Building native libraries..." /> <exec executable="${ndk.dir}/ndk-build.cmd" failonerror="true" /> <echo message="DONE (Building native libraries)" /> </target>
然後在命令行中執行ant native
執行了ndk-build。
以上介紹的都是使用Ant進行進步的Android編譯操作,在此基礎上可以實現更復雜的操作,比如批量替換資源文件,批量打包等等。