Android市場的渠道分散已不是什麼新鮮事,但如何高效打包還是令許多開發者頭疼的問題。本篇文章着重介紹了目前最新的三種打包方案,而且從安全方面對這三種方案進行點評,相信會給開發者帶來新的助力。html
通常需求的打包,一條行命令就出來了。複雜一些的話,也就是一個簡單的開源工具,或是一段小配置代碼就搞定了。既然如此,爲何我還要來寫Android打包相關內容?主要有如下兩個方面的緣由:java
因此,當Android開發團隊有必定規模時,須要一種優雅而且高效安全的打包方式來銜接從代碼到應用程序包的這個過程。python
大概在3-4年前,國內的移動開發者都會使用友盟來作Crash收集和一些用戶的行爲數據收集。因此,友盟定義渠道的方式基本定義了數據收集工具的渠道定義方式。友盟默認的渠道直接從AndroidManifest文件中的meta-data裏面讀取。如如下代碼所示:android
<meta-data android:name="UMENG_APPKEY" android:value="4ee5c714ee5c714ee5c71"> </meta-data> <meta-data android:name="UMENG_CHANNEL" android:value="wandoujia"> </meta-data>
在示例中,UMENG_APPKEY是帳戶的惟一標示,UMENG_CHANNEL的內容爲wandoujia,即表示這時渠道是wandoujia。這裏再多說一句渠道的概念,通常的渠道可能會表示具體是哪一個應用市場。好比上傳豌豆莢和上傳360手機助手的渠道標示不同,固然,若是有一些線下推廣的話,也須要區分渠道。隨着數據統計愈來愈細,渠道也會更加的層出不窮。如今一個日活在百萬的應用,Android的渠道在幾千個是很是正常的現象。git
在AndroidManifest定義渠道的年代,多渠道打包無非如下兩種方案:github
如今看來,這兩種方式效率都不高。方案一基本上毫無效率可言,只能支持20個之內的渠道規模。方案二效率高一些,若是再引入多線程驅動來進行打包,支持200之內的渠道打包毫無壓力。可是隨着Android的版本升級,代碼混淆和加固工具的引入,這種方案已經不能正常運行了。因此,須要一種兼容性好而且高效的多渠道打包方式。web
Android項目自己是開源的,你們均可以去研究任何實現,基於這個特性,一種叫作「Android黑科技」的學科迅速誕生。如下介紹的多渠道打包方式都應該屬於其範疇。api
添加comments多渠道打包安全
首先解釋什麼是comments(註釋或評論)的打包方式?bash
Android應用使用的APK文件就是一個帶簽名信息的zip文件,根據zip文件格式規範(請參見:https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT),每一個文件的最後都必須有一個叫Central Directory Record(https://users.cs.jmu.edu/buchhofp/forensics/formats/pkzip.html)的部分,這個CDR的最後部分叫「End of Central Directory Record」,這一部分包含一些元數據,它的末尾是zip文件的註釋。註釋包含Comment Length和File Comment兩個字段。前者表示註釋的長度,後者是註釋的內容,正確修改這一部分不會對zip文件形成破壞,利用這個字段,咱們能夠添加一些自定義數據。
簡單來講就是:咱們利用的是zip文件「能夠添加comment(摘要)」的數據結構特色,在文件的末尾寫入任意數據,而不用從新解壓zip文件(apk文件就是zip文件格式);因此該工具不須要對APK文件解壓縮和從新簽名便可完成多渠道自動打包,可謂是高效、速度快,無兼容問題。
這種高效的方式是奇虎360的一位工程師公佈的,他已經把相關工具的代碼開放在GitHub上面了,地址爲https://github.com/seven456/MultiChannelPackageTool。工具自己爲命令行形式,一條命令便可生成一個多渠道包,而且很是高效。做者本人公佈的性能數據是:5M的APK,1秒種能打300個。
就是由於速度很是快,如今一些廠商用這種方案來作安裝包的動態生成。在用戶下載以前渠道是沒有添加進去的,用戶選擇下載之後經過渠道來源判斷渠道標示,而且寫入。過程瞬間完成,用戶下載時毫無感知。
固然,利用工具把渠道寫入應用程序包內,還應該提供一種在APK內部讀取渠道的方式。讀取時方法也很是簡單,首先經過反射方法找到APK在手機中的位置,而後經過讀zip comment的方式獲取渠道內容。讀取comment和找APK位置的代碼都能在這個急速添加渠道的工具中找到,在這裏再也不詳細介紹。須要注意的是,在Android中使用Java反射的性能很是差,須要作一些優化處理。
若是你以爲這種方式的安全比較差,也能夠本身從新實現一個小工具,而且對comment的內容加密。固然,加密使用的密鑰須要在APK代碼中體現,這種加密方案也只是提升了一點破譯的門檻而已。
美團的Android多渠道打包
美團點評技術團隊(原美團技術團隊)有一個公開的博客,每篇文章都在領域內有必定的影響。其中有一篇就是討論「如何高效地進行Android多渠道打包?」,可參見:http://tech.meituan.com/mt-apk-packaging.html。
文章中首先提到了ApkTool的多渠道打包方式,而後講到美團的業務發展已經有900多個渠道,ApkTool的方式已經不能支持這種規模的多渠道打包。因此,美團點評技術團隊開始探索一種更加高效的多渠道打包方式。
美團的作法是把一個Android應用包看成zip文件包進行解壓,而後發如今簽名生成的目錄下添加一個空文件不須要從新簽名。利用這個機制,該文件的文件名就是渠道名。這種方式不須要從新簽名等步驟,很是高效。
下面咱們來詳細講解一下這個過程。首先把一個已經簽名好的Android應用程序包解壓縮,簽名文件的目錄爲:META-INF,如圖1所示。
圖1 美團Android多渠道打包壓縮文件目錄
圖中有兩個框選的部分,分別爲:assets目錄和META-INF目錄。assets等下文介紹豌豆莢高效打包方式的時候再說。咱們這裏先來看看META-INF目錄。在META-INF目錄下的三個文件,就是一個Android應用程序包被正確簽名以後的生成文件,當這個程序包中的文件被修改之後,驗證信息就須要從新生成,不然驗證的簽名信息就不正確。這套簽名機制也是從Java的jar包方式繼承過來的。
美團多渠道打包方式,正是利用簽名的驗證方式,巧妙地放入了一個空文件,利用文件名來表示渠道方式的完成。若是放入了一個非空的文件,這時簽名驗證的機制就會被破壞。美團的具體作法是經過一段簡單的Python腳原本完成。代碼以下:
import zipfile zipped = zipfile.ZipFile(your_apk, 'a', zipfile.ZIP_DEFLATED) empty_channel_file = "META-INF/mtchannel_{channel}".format(channel=your_channel) zipped.write(your_empty_file, empty_channel_file)
經過運行Python腳本,Android應用程序包中便會多出一個文件,如圖2所示。
圖2 經過運行Python腳本,Android應用程序包中多出來的一個文件
渠道添加成功後,能夠參照美團點評技術團隊的博客,在Android程序中添加讀取渠道的代碼,便可擁有高效的多渠道打包方式。
固然,這個技術實現自己能夠有更加優雅和高效的方式——添加渠道。原理不變,添加方式能夠進行改變。添加文件可使用AAPT這個工具。AAPT自己是Android平臺自帶的一個命令行工具,能夠實現從Android程序包中刪除和添加任意文件。固然,若是添加文件破壞了簽名驗證信息,則是另一回事了。關於AAPT的使用和其在多渠道簽名打包時的用法,會在下文進行講解。
若是如今想把美團的渠道刪除,使用AAPT輸入一條簡單的命令行便可(能刪除就能添加,添加部分仍是在豌豆莢多渠道打包方案中介紹吧):
aapt remove meituan.apk META-INF/mtchannel_meituan
豌豆莢Android多渠道打包
豌豆莢的多渠道打包方案和美團的所用的方式同樣,都是添加一個文件,文件自己會帶入渠道消息。但不一樣點在於它添加的是一個不爲空的文件。以前已經提到過了,若是添加一個非空文件,就會破壞簽名校驗,須要從新簽名。
從這個方案能夠看出,豌豆莢多渠道打包和美團多渠道打包正好是利用了一個特性的兩個方面。固然,若是須要從新簽名,效率會差一些。在失去效率的同時,擁有了更加安全的渠道管理。下面具體講解,豌豆莢多渠道打包管理方案。
首先,若是把渠道文件還放在META-INF目錄下的話,從新簽名時渠道文件會被刪掉。因此,渠道文件須要從新找一個合理的地方存放。以前的文章也提到過,還有一個assets目錄。assets目錄和META-INF目錄同樣,能夠隨便放入小文件。若是是空文件不須要從新簽名
加入渠道時主要有如下步驟。
aapt add wandoujia.apk asserts/channel.txt
圖3 豌豆莢打包方案處理一個包須要大約4秒
說完了豌豆莢多渠道打包的實現部分之後,須要來談一下效率。以前的兩種多渠道打包方案效率都很高,處理一個包都能在1秒以內完成。但豌豆莢的多渠道打包方案因爲必須從新簽名,會慢一些(但也沒有慢多少)。在屏蔽了一些關鍵信息以後,從圖3中能夠看到,一次處理大概須要4秒的時間。如今一臺通常配置的計算機,開8個線程處理,速度徹底能夠接受。
三種打包方案都是高效的實現,固然也有各自的側重點。如下,從安全的角度來分析。
對於Android應用包的安全問題來講,主要須要考慮渠道被惡意篡改的可能性和成本。若是一個渠道商,經過網絡劫持和篡改渠道的組合方式來獲取暴利,對於程序開發者來講可能會存在着巨大的經濟損失。
在介紹篡改渠道以前,仍是先簡單介紹AAPT這個工具的通常使用方法。AAPT是在AndroidSDK內的一個命令行工具,具體位置在AndroidSDK目錄下的build-tools/19.0.0或者其餘版本下,AAPT並不存在於每一個發行版本中,因此通常使用的AAPT是19.0.0或19.0.1版本。
使用AAPT主要有如下4種場景:
INPUT apt dump badging weixin.apk OUTPUT package: name='com.tencent.mm' versionCode='740' versionName='6.3.13.49_r4080b63' uses-permission:'com.tencent.mm.plugin.permission.READ' uses-permission:'com.tencent.mm.plugin.permission.WRITE' uses-permission:'com.tencent.mm.permission.MM_MESSAGE' uses-permission:'com.huawei.authentication.HW_ACCESS_AUTH_SERVICE' sdkVersion:'15' targetSdkVersion:'23' uses-feature-not-required:'android.hardware.camera' uses-feature-not-required:'android.hardware.camera.autofocus' uses-feature-not-required:'android.hardware.bluetooth' uses-feature-not-required:'android.hardware.location' uses-feature-not-required:'android.hardware.location.gps' uses-feature-not-required:'android.hardware.location.network' uses-feature-not-required:'android.hardware.microphone' uses-feature-not-required:'android.hardware.telephony' uses-feature-not-required:'android.hardware.touchscreen' uses-feature-not-required:'android.hardware.wifi' uses-permission:'android.permission.ACCESS_NETWORK_STATE' uses-permission:'android.permission.ACCESS_COARSE_LOCATION' uses-permission:'android.permission.ACCESS_FINE_LOCATION' uses-permission:'android.permission.CAMERA' .....
從以上代碼能夠看到這個微信的版本信息,而且知道了微信的包名是:com.tencent.mm,還能看到微信這個應用使用到了哪些權限等信息(更多的信息仍是交給讀者本身仔細研究吧)。
使用AAPT查看Android開發包的目錄結構以下:
INPUT aapt list weixin.apk OUTPUT META-INF/MANIFEST.MF META-INF/COM_TENC.SF META-INF/COM_TENC.RSA assets/jsapi/wxjs.js assets/avatar/default_nor_avatar.png assets/avatar/default_meishiapp.png assets/avatar/default_hd_avatar.png assets/ipcall_country_code.txt assets/merged_features.xml assets/address .....
又看到了META-INF目錄和assets目錄。通常在Android的應用中assets都會放一些靜態的資源。
INPUT aapt add wandoujia.apk assets/channel.txt
INPUT aapt remove wandoujia.apk assets/channel.txt
在掌握了以上簡單的四個命令行後,修改美團的渠道就已經很是簡單了。先經過list命令找到渠道文件,而後直接刪除文件,最後再加入本身的惡意渠道。但豌豆莢的渠道也能夠經過相同的方式來進行篡改,但篡改以後須要從新簽名。在沒有官方的簽名文件時,是沒有辦法徹底僞造一個相同的簽名的。
添加comments多渠道打包方式中雖然渠道信息不是明文顯示,但也存在着被篡改的可能性。若是本身進行一些簡單的加密能夠杜絕大多數的惡意篡改,但因爲不須要從新簽名,這種方法仍是存在着被篡改的機率的。
具體選擇哪種多渠道打包方式,仍是由業務決定的。豌豆莢做爲一個應用商店,須要有本身的渠道,渠道的安全性會很很差。若是是一個普通的App,通常都是在一些應用商店上發佈新版本,應用商店自己會給開發者提供相對安全的環境。因此這也就是美團方案依然可以使用的關鍵。
本文重點介紹了3種高效的多渠道打包方式。這三種方式都不會產生二次編譯,二次編譯會對一個版本的程序產生多個版本的符號表,在後期的問題追查中會有不少障礙。利用結束語簡單說說符號表的重要性吧。
當程序正確打包發佈之後,能夠經過各類免費、收費或本身開發的工具來收集Crash等堆棧信息,查看錯誤並進行改正。你可能會獲得一段這樣的異常堆棧:
ava.lang.RuntimeException: There is no view set with ViewPackage found in the log tree. at o.λ.ˎ(:64) at o.λ.ˊ(:25) at o.к$ˋ.run(:233) at android.os.Handler.handleCallback(Handler.java:733) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:136) at android.os.HandlerThread.run(HandlerThread.java:61)
而後若是沒有一個符號表幫忙,你會陷入深深的沉思:這究竟是什麼問題?個人代碼在哪裏?若是有了正確的符號表,一條命令行就能夠就能夠把相關的異常堆棧信息翻譯正確。符號表和編譯過程有着緊密的耦合,因此在選擇打包工具時,都應該選擇不產生二次編譯的工具。在以上提到的三款工具中,添加多渠道都是基於已有APK進行添加的,並非基於源碼來添加渠道。因此都徹底知足條件。
考慮到文章的篇幅,有一些內容沒有展開說,開發者若是遇到這方面的問題,歡迎與我交流。