本文我將給大家介紹一個 apk 打包工具 VasDolly 的使用介紹、原理以及如何在服務端接入 VasDolly 進行服務端打渠道包操作。 # 使用介紹 
}
- 通過 Gradle 生成多渠道包。你可以選擇直接編譯生成多渠道包,或者根據已有基礎包重新生成多渠道包。
如果你選擇直接編譯生成多渠道包,你需要配置渠道文件、渠道包的輸出目錄和渠道包的命名規則。例如:
channel {
//指定渠道文件
channelFile = file("/Users/leon/Downloads/testChannel.txt")
//多渠道包的輸出目錄,預設為new File (project.buildDir,"channel")
outputDir = new File(project.buildDir,"xxx")
//多渠道包的命名規則,預設為:$ {appName}-$ {versionName}-$ {versionCode}-$ {flavorName}-$ {buildType}-$ {buildTime}
apkNameFormat ='$ {appName}-$ {versionName}-$ {versionCode}-$ {flavorName}-$ {buildType}'//快速模式:生成渠道包時不進行校驗(速度可以提升10倍以上,預設為false)
fastMode = false//buildTime的時間格式,預設格式:yyyyMMdd-HHmmss
buildTimeDateFormat = 'yyyyMMdd-HH:mm:ss'//低記憶體模式(僅針對V2簽名,預設為false):只把簽名塊、中央目錄和EOCD讀取到記憶體,不把最大頭的內容塊讀取到記憶體,在手機上合成APK時,可以使用該模式
lowMemory = false
}
然後,通過 gradle channelDebug 或 gradle channelRelease 命令分別生成 Debug 和 Release 的多渠道包。
如果你選擇根據已有基礎包重新生成多渠道包,你需要配置渠道文件、基礎包的路徑和渠道包的輸出目錄。例如:
rebuildChannel {
//指定渠道文件
channelFile = file("/Users/leon/Downloads/testReChannel.txt")
// 已有APK文件地址(必填),如new File (project.rootDir, "/baseApk/app_base.apk"),文件名中的base將被替換為渠道名
baseApk = new File (project.rootDir, "/baseApk/app_base.apk")
//預設為new File (project.buildDir, "rebuildChannel")
outputDir = new File(project.buildDir,"yyy")
//快速模式:生成渠道包時不進行校驗(速度可以提升10倍以上,預設為false)
fastMode = false//低記憶體模式(僅針對V2簽名,預設為false):只把簽名塊、中央目錄和EOCD讀取到記憶體,不把最大頭的內容塊讀取到記憶體,在手機上合成APK時,可以使用該模式
lowMemory = false
}
然後,通過 gradle rebuildChannel 命令生成多渠道包。
原理
VasDolly 實現原理官方講解地址:https://github.com/Tencent/VasDolly/wiki/VasDolly實現原理
VasDolly 的原理是利用 APK 文件的特殊結構,將渠道信息寫入到 APK 文件的空白區域,從而實現無損的多渠道打包。具體來說,VasDolly 根據 APK 文件使用的簽名類別,選擇不同的多渠道打包方式。
如果 APK 文件使用的是 V1 簽名,那麼 VasDolly 會將渠道信息寫入到 APK 文件的 Zip Comment 區域。Zip Comment 是 Zip 文件格式中的一個欄位,用於存儲一些註釋信息,通常不會被解壓縮工具或者系統解析。因此,將渠道信息寫入到 Zip Comment 區域,不會影響 APK 文件的完整性和安全性。同時,由於 Zip Comment 區域位於 APK 文件的末尾,所以寫入渠道信息的速度非常快,只需要修改一個位元組的偏移量即可。
如果 APK 文件使用的是 V2 或者 V3 簽名,那麼 VasDolly 會將渠道信息寫入到 APK Signing Block 區域。APK Signing Block 是 V2 或者 V3 簽名引入的一個新區域,用於存儲簽名相關的數據。每個數據都有一個 ID 來標識其類型,例如 0x7109871a 表示 V2 簽名數據。VasDolly 會使用一個自定義的 ID(0x71777777)來標識渠道信息,並將其寫入到 APK Signing Block 區域。由於這個區域不會被系統解析,所以不會影響 APK 文件的安全性。同時,由於這個區域位於中央目錄和 EOCD 之前,所以寫入渠道信息的速度也很快,只需要修改兩個位元組的偏移量即可。
通過這種方式,VasDolly 可以實現在不重新簽名和對齊的情況下,快速生成多個渠道包。在應用運行時,可以通過 VasDolly 提供的 helper 類庫來讀取渠道信息,併進行相應的處理。
服務端接入 VasDolly 教程
安卓接入了 VasDolly 之後,就該我們服務端出手了,服務端如果能實現渠道打包的操作,運營每次上新渠道就不需要再找安卓進行新渠道打包,運營直接在後臺上傳母包,選定渠道後即可獲取對應的渠道包,可以節約大家的時間,避免耗費人力在渠道打包這一步。由此可見,服務端進行渠道打包操作還是有必要的。
意外發現
一開始我是想用官方提供的 jar 包工具,通過命令行調用的方式來實現服務端打渠道包的,官方提供的 readme 文檔如下,
但是由於博主項目是使用容器環境部署,要是用命令行打包的話,需要引入 VasDolly jar 包並且掛載都容器中,感覺比較麻煩。就想能不能直接引入 VasDolly 依賴來實現打包操作。
於是我在 maven 中央倉庫搜索了 com.tencent.vasdolly 關鍵字後,有瞭如下發現,
可以看到 VasDolly 雖然是一個 Gradle 項目,但是官方也提供了部分模塊的在 Maven 中的 pom 坐標。
查看 VasDolly 倉庫代碼,發現官方定義了 write 模塊,
進入其中,發現了 readme 內容如下,
Ok,到這裡,我們發現其實官方提供了 pom 依賴接入,直接使用 ChannelWriter 即可實現渠道打包的操作。
具體教程
- 在後端 maven 項目中引入 VasDolly 的 pom 依賴,當前最新依賴版本如下,
<dependency>
<groupId>com.tencent.vasdolly</groupId>
<artifactId>writer</artifactId>
<version>3.0.6</version>
</dependency>
<dependency>
<groupId>com.tencent.vasdolly</groupId>
<artifactId>common</artifactId>
<version>3.0.6</version>
</dependency>
<dependency>
<groupId>com.tencent.vasdolly</groupId>
<artifactId>reader</artifactId>
<version>3.0.6</version>
</dependency>
- 然後我們就可以利用 ChannelWriter 類實現渠道打包操作,ChannelWriter 類提供的 V2 簽名打渠道包方法如下:
public static void addChannelByV2(File apkFile, String channel, boolean lowMemory) throws IOException, SignatureNotFoundException {
addChannelByV2(apkFile, apkFile, channel, lowMemory);
}
addChannelByV2 方法的 apkFile 參數是母包文件,channel 參數是需要打包的渠道名稱,lowMemory 參數是 V2 簽名打渠道包提供的參數,預設為 false。該方法會直接將我們傳入的 apkFile 母包修改成 V2 簽名的渠道包。如此一來,我們就獲得了我們需要的渠道包了。
- 除了利用 ChannelWriter 類實現打渠道包操作,我們還可以利用 ChannelReader 類來實現讀取渠道包的渠道參數,ChannelReader 類提供的 V2 簽名渠道包參數讀取方法如下:
public static String getChannelByV2(File channelFile) {
System.out.println("try to read channel info from apk : " + channelFile.getAbsolutePath());
return IdValueReader.getStringValueById(channelFile, -2012129793);
}
getChannelByV2 方法的 channelFile 參數就是打包後渠道包,該方法會返回渠道包中的渠道信息。
最後
感謝您的閱讀,希望本文能對您有所幫助。
關註公眾號【waynblog】每周分享技術乾貨、開源項目、實戰經驗、高效開發工具等,您的關註將是我的更新動力!