項目的構(gòu)建:當(dāng)我們打開一個項目,我們可以看到的是我們寫的Java Code文件or Other JVM Code庶灿,資源文件梗摇,Build配置文件鹊漠,但是通過run the project毁兆,我們就可以得到一個在我們的Andoid設(shè)備上可以運(yùn)行的Apk浙滤,上線應(yīng)用市場,還需要我們對其進(jìn)行簽名處理气堕,來確保我們App的唯一性和安全性纺腊。整個過程就是所謂的項目構(gòu)建。如下圖所示茎芭。
上圖是Android官方提供的打包簡略流程圖揖膜。清晰地展示了一個Android Project經(jīng)過編譯和打包后生成apk文件,然后再經(jīng)過簽名梅桩,就可以安裝到設(shè)備上
我們將一個實際的apk文件后綴改為zip并解壓后次氨,得到的內(nèi)容如下
和上圖的描述一致。apk包內(nèi)容包括:
classes.dex…
resources.arsc
assets
res
AndroidManifest.xml
META-INF
其中:
res中圖片和raw文件下內(nèi)容保持原樣摘投,res中其他xml文件內(nèi)容均轉(zhuǎn)化為二進(jìn)制形式煮寡;assets文件內(nèi)容保持原樣
res中的文件會被映射到R.java文件中,訪問的時候直接使用資源ID即R.id.filename犀呼;assets文件夾下的文件不會被映射到R.java中幸撕,訪問的時候需要AssetManager類
在看下這張圖 ,android apk構(gòu)建詳細(xì)流程圖
如果你接觸Android開發(fā)已經(jīng)有一段時間了外臂,我想當(dāng)你看到這張圖的時候坐儿,就會覺得很清晰。但是更多的可能會一頭霧水宋光,如果之前沒有閱讀相關(guān)的資料的話貌矿,那么,接下來罪佳,將針對上述的構(gòu)建過程逛漫,先給出一個概述,這樣你將會整個構(gòu)建流程在心中有一個框架赘艳,然后針對其中具體的細(xì)節(jié)酌毡,進(jìn)行進(jìn)一步詳細(xì)的講解。
圖中綠色標(biāo)注為其中用到的相應(yīng)工具蕾管,藍(lán)色代表的是中間生成的各類文件類型枷踏。
首先aapt工具會將資源文件進(jìn)行轉(zhuǎn)化,生成對應(yīng)資源ID的R文件和資源文件掰曾。
adil工具會將其中的aidl接口轉(zhuǎn)化成Java的接口
至此旭蠕,Java Compiler開始進(jìn)行Java文件向class文件的轉(zhuǎn)化,將R文件旷坦,Java源代碼掏熬,由aidl轉(zhuǎn)化來的Java接口,統(tǒng)一轉(zhuǎn)化成.class文件塞蹭。
通過dx工具將class文件轉(zhuǎn)化為dex文件孽江。
此時我們得到了經(jīng)過處理后的資源文件和一個dex文件,當(dāng)然番电,還會存在一些其它的資源文件岗屏,這個時候,就是將其打包成一個類似apk的文件漱办。但還并不是直接可以安裝在Android系統(tǒng)上的APK文件这刷。
通過簽名工具對其進(jìn)行簽名。
通過Zipalign進(jìn)行優(yōu)化娩井,提升運(yùn)行速度(原理后文會提及)暇屋。
最終,一個可以安裝在我們手機(jī)上的APK了洞辣。
通過上述講解咐刨,我想對于Android項目的整個構(gòu)建過程昙衅,應(yīng)該有了一個很清晰的框架了,下面將針對其中的具體的細(xì)節(jié)定鸟,和前面挖的一些坑而涉,來進(jìn)行更細(xì)致的分析,下圖是一個Android項目構(gòu)建過程的詳細(xì)步驟圖联予。
接下來的分析啼县,我們還是按照上述構(gòu)建過程概述的順序和流程,進(jìn)行具體的分析沸久。
第1步:aapt打包資源文件季眷,生成R.java和編譯后的資源(二進(jìn)制文件)
講到資源文件的處理,我們先來看一下Android中的資源文件有那些呢?Android應(yīng)用程序資源可以分為兩大類卷胯,分別是assets和res:
1. assets類資源放在工程根目錄的assets子目錄下子刮,它里面保存的是一些原始的文件,可以以任何方式來進(jìn)行組織诵竭。這些文件最終會被原裝不動地打包在apk文件中话告。如果我們要在程序中訪問這些文件,那么就需要指定文件名來訪問卵慰。例如沙郭,假設(shè)在assets目錄下有一個名稱為filename的文件,那么就可以使用以下代碼來訪問它:
AssetManager am= getAssets();
InputStream is = assset.open("filename");
2. res類資源放在工程根目錄的res子目錄下裳朋,它里面保存的文件大多數(shù)都會被編譯病线,并且都會被賦予資源ID。這樣我們就可以在程序中通過ID來訪問res類的資源鲤嫡。res類資源按照不同的用途可以進(jìn)一步劃分為以下10種子類型:
layout(布局文件)送挑,drawable,xml暖眼,value惕耕,menu,raw诫肠,color司澎,anim,animator栋豫,mipmap挤安。
為了使得一個應(yīng)用程序能夠在運(yùn)行時同時支持不同的大小和密度的屏幕,以及支持國際化丧鸯,即支持不同的國家地區(qū)和語言蛤铜,Android應(yīng)用程序資源的組織方式有18個維度,每一個維度都代表一個配置信息,從而可以使得應(yīng)用程序能夠根據(jù)設(shè)備的當(dāng)前配置信息來找到最匹配的資源來展現(xiàn)在UI上围肥,從而提高用戶體驗剿干。由于Android應(yīng)用程序資源的組織方式可以達(dá)到18個維度,因此就要求Android資源管理框架能夠快速定位最匹配設(shè)備當(dāng)前配置信息的資源來展現(xiàn)在UI上虐先,否則的話怨愤,就會影響用戶體驗。為了支持Android資源管理框架快速定位最匹配資源蛹批,Android資源打包工具aapt在編譯和打包資源的過程中,會執(zhí)行以下兩個額外的操作:
賦予每一個非assets資源一個ID值篮愉,這些ID值以常量的形式定義在一個R.java文件中腐芍。
生成一個resources.arsc文件,用來描述那些具有ID值的資源的配置信息试躏,它的內(nèi)容就相當(dāng)于是一個資源索引表猪勇。包含了所有的id值的數(shù)據(jù)集合。在該文件中颠蕴,如果某個id對應(yīng)的是string泣刹,那么該文件會直接包含該值,如果id對應(yīng)的資源是某個layout或者drawable資源犀被,那么該文件會存入對應(yīng)資源的路徑椅您。
為什么要轉(zhuǎn)化為二進(jìn)制文件?
二進(jìn)制格式的XML文件占用空間更小寡键。這是由于所有XML元素的標(biāo)簽掀泳、屬性名稱、屬性值和內(nèi)容所涉及到的字符串都會被統(tǒng)一收集到一個字符串資源池中去西轩,并且會去重员舵。有了這個字符串資源池,原來使用字符串的地方就會被替換成一個索引到字符串資源池的整數(shù)值藕畔,從而可以減少文件的大小马僻。
二進(jìn)制格式的XML文件解析速度更快。這是由于二進(jìn)制格式的XML元素里面不再包含有字符串值注服,因此就避免了進(jìn)行字符串解析韭邓,從而提高速度。
有了資源ID以及資源索引表之后祠汇,Android資源管理框架就可以迅速將根據(jù)設(shè)備當(dāng)前配置信息來定位最匹配的資源了仍秤。
對于具體的一些操作流程,可以參考本人之前的一篇文章APK打包安裝過程或者更偏向于源碼層級的老羅的文章可很。(文后參考文獻(xiàn)鏈接)
第2步:aidl
aidl诗力,全名Android Interface Definition Language,即Android接口定義語言。是我們在編寫進(jìn)程間通信的代碼的時候苇本,定義的接口袜茧。
輸入:aidl后綴的文件。輸出:可用于進(jìn)程通信的C/S端java代碼瓣窄,位于build/generated/source/aidl笛厦。
第3步:Java源碼編譯
我們有了R.java和aidl生成的Java文件,再加上工程的源代碼俺夕,現(xiàn)在可以使用javac進(jìn)行正常的java編譯生成class文件了裳凸。
輸入:java source的文件夾(另外還包括了build/generated下的:R.java, aidl生成的java文件,以及BuildConfig.java)劝贸。輸出:對于gradle編譯姨谷,可以在build/intermediates/classes里,看到輸出的class文件映九。
第4步:代碼混淆(proguard)
源碼編譯之后梦湘,我們可能還會對其進(jìn)行代碼的混淆,混淆的作用是增加反編譯的難度件甥,同時也將一些代碼的命名進(jìn)行了縮短捌议,減少代碼占用的空間∫校混淆完成之后瓣颅,會生成一個混淆前后的映射表,這個是用來在反應(yīng)我們的應(yīng)用執(zhí)行的時候的一些堆棧信息轿曙,可以將混淆后的信息轉(zhuǎn)化為我們混淆前實際代碼中的內(nèi)容弄捕。
而這個過程使用的工具就是ProGuard,是一個開源的Java代碼混淆器(obfuscation)导帝。ADT r8開始它被默認(rèn)集成到了Android SDK中守谓。 其具備三個主要功能。
壓縮 - 移除無效的類您单、屬性斋荞、方法等
優(yōu)化 - 優(yōu)化bytecode移除沒用的結(jié)構(gòu)
混淆 - 把類名、屬性名虐秦、方法名替換為晦澀難懂的1到2個字母的名字
當(dāng)然它也只能混淆Java代碼平酿,Android工程中Native代碼,資源文件(圖片悦陋、xml)蜈彼,它是無法混淆的。而且對于Java的常量值也是無法混淆的俺驶,所以不要使用常量定義平文的密碼等重要信息幸逆。同時對于混淆,我們可以通過代碼制定去混淆那些,不去混淆那些还绘。
-keep public class com.rensanning.example.Test
第5步:轉(zhuǎn)化為dex
調(diào)用dx.bat將所有的class文件轉(zhuǎn)化為classes.dex文件楚昭,dx會將class轉(zhuǎn)換為Dalvik字節(jié)碼,生成常量池拍顷,消除冗余數(shù)據(jù)等抚太。由于dalvik是一種針對嵌入式設(shè)備而特殊設(shè)計的java虛擬機(jī),所以dex文件與標(biāo)準(zhǔn)的class文件在結(jié)構(gòu)設(shè)計上有著本質(zhì)的區(qū)別,當(dāng)java程序編譯成class后昔案,使用dx工具將所有的class文件整合到一個dex文件尿贫,目的是其中各個類能夠共享數(shù)據(jù),在一定程度上降低了冗余爱沟,同時也是文件結(jié)構(gòu)更加經(jīng)湊帅霜,實驗表明,dex文件是傳統(tǒng)jar文件大小的50%左右呼伸。class文件結(jié)構(gòu)和dex文件結(jié)構(gòu)比對。
第6步:apkbuilder
打包生成APK文件钝尸。舊的apkbuilder腳本已經(jīng)廢棄括享,現(xiàn)在都已經(jīng)通過sdklib.jar的ApkBuilder類進(jìn)行打包了。輸入為我們之前生成的包含resources.arcs的.ap_文件珍促,上一步生成的dex文件铃辖,以及其他資源如jni、.so文件猪叙。
大致步驟為
以包含resources.arcs的.ap_文件為基礎(chǔ)娇斩,new一個ApkBuilder,設(shè)置debugMode
apkBuilder.addZipFile(f);
apkBuilder.addSourceFolder(f);
apkBuilder.addResourcesFromJar(f);
apkBuilder.addNativeLibraries(nativeFileList);
apkBuilder.sealApk(); // 關(guān)閉apk文件
generateDependencyFile(depFile, inputPaths, outputFile.getAbsolutePath());
第7步:對APK簽名
對APK文件進(jìn)行簽名穴翩。Android系統(tǒng)在安裝APK的時候犬第,首先會檢驗APK的簽名,如果發(fā)現(xiàn)簽名文件不存在或者校驗簽名失敗芒帕,則會拒絕安裝歉嗓,所以應(yīng)用程序在發(fā)布之前一定要進(jìn)行簽名。簽名信息中包含有開發(fā)者信息背蟆,在一定程度上可以防止應(yīng)用被偽造鉴分。對一個APK文件簽名之后,APK文件根目錄下會增加META-INF目錄带膀,該目錄下增加三個文件:
MANIFEST.MF
NETEASE.RSA
NETEASE.SF
Android系統(tǒng)就是根據(jù)這三個文件的內(nèi)容對APK文件進(jìn)行簽名檢驗的志珍。簽名過程主要利用apksign.jar或者jarsinger.jar兩個工具。將根據(jù)我們提供的Debug和Release兩個版本的Keystore進(jìn)行相應(yīng)的簽名垛叨。
第8步:zipalign優(yōu)化
Zipalign是一個Android平臺上整理APK文件的工具伦糯,它首次被引入是在Android 1.6版本的SDK軟件開發(fā)工具包中。它能夠?qū)Υ虬腁ndroid應(yīng)用程序進(jìn)行優(yōu)化, 以使Android操作系統(tǒng)與應(yīng)用程序之間的交互作用更有效率舔株,這能夠讓應(yīng)用程序和整個系統(tǒng)運(yùn)行得更快莺琳。用Zipalign處理過的應(yīng)用程序執(zhí)行時間達(dá)到最低限度,當(dāng)設(shè)備運(yùn)行APK應(yīng)用程序時占更少的RAM载慈。
- Zipalign如何進(jìn)行優(yōu)化的呢惭等?
調(diào)用buildtoolszipalign,對簽名后的APK文件進(jìn)行對齊處理办铡,使APK中所有資源文件距離文件起始偏移為4字節(jié)的整數(shù)倍辞做,從而在通過內(nèi)存映射訪問APK文件時會更快。同時也減少了在設(shè)備上運(yùn)行時的內(nèi)存消耗寡具。如果對于為何提速不理解秤茅,那么可以看下內(nèi)存對齊的規(guī)則以及作用該篇文章,對于內(nèi)存對齊的好處有比較生動詳細(xì)的解釋童叠。最終這樣我們的APK就生成完畢了框喳。
典型的APK中內(nèi)容
AndroidManifest.xml
程序全局配置文件classes.dex
Dalvik字節(jié)碼resources.arsc
資源索引表META-INF
該目錄下存放的是簽名信息res
該目錄存放資源文件assets
該目錄可以存放一些配置或資源文件
總結(jié)
至此,對于Andoid項目構(gòu)建過程的分析已經(jīng)完成厦坛,當(dāng)然五垮,并沒與深入到源碼層級的分析,本文的旨在對于構(gòu)建過程流程上的了解和其中一些優(yōu)化的原因所在杜秸,為后續(xù)通過Gradle插件hook構(gòu)建過程來做一定的操作放仗,做一個鋪墊。接下來會推出對于構(gòu)建AndroidStudio上運(yùn)行的Gradle插件的一系列文章撬碟。
參考文章:https://segmentfault.com/a/1190000008445988
http://www.reibang.com/p/4962634901fb