10分鐘了解Android項(xiàng)目構(gòu)建流程

前言

上兩篇博客中提到了構(gòu)建過程的問題,之前畢業(yè)在準(zhǔn)備面試的過程中,對(duì)這個(gè)部分有過較為認(rèn)真的學(xué)習(xí),也進(jìn)行了博客記錄,但是實(shí)際工作過程中,如果是在寫業(yè)務(wù)邏輯上,那么這方面的問題接觸的就會(huì)比較少了。逐漸的淡忘了,其次,之前所寫的文章條理性也不是很強(qiáng),同時(shí),最近準(zhǔn)備進(jìn)行Gradle插件的一系列博客的產(chǎn)出,其中將會(huì)涉及到很多與項(xiàng)目構(gòu)建相關(guān)的內(nèi)容。所以此文也將成為后續(xù)文章的一個(gè)鋪墊。

構(gòu)建過程

項(xiàng)目的構(gòu)建:當(dāng)我們打開一個(gè)項(xiàng)目,我們可以看到的是我們寫的Java Code文件or Other JVM Code,資源文件,Build配置文件,但是通過run the project,我們就可以得到一個(gè)在我們的Andoid設(shè)備上可以運(yùn)行的Apk,上線應(yīng)用市場(chǎng),還需要我們對(duì)其進(jìn)行簽名處理,來確保我們App的唯一性和安全性。整個(gè)過程就是所謂的項(xiàng)目構(gòu)建。

如何實(shí)現(xiàn)整個(gè)構(gòu)建的過程,對(duì)于每一個(gè)構(gòu)建的步驟,都需要相應(yīng)的功能模塊來進(jìn)行,比如Java Code編譯,如何打成dex包等等,而這Android則為我們提供了相應(yīng)的工具,在Android Studio命令行窗口中,我們可以通過相應(yīng)的命令行來進(jìn)行控制,但是,整個(gè)構(gòu)建過程涉及到很多的步驟,很多的工具的使用,如果都通過命令行來進(jìn)行控制,勢(shì)必會(huì)相當(dāng)麻煩,因此Androd Studio等IDE則對(duì)整個(gè)過程進(jìn)行了一個(gè)打包,當(dāng)我們?cè)赗un project的時(shí)候,底層的打包工具就會(huì)被調(diào)用,打包流程都會(huì)自動(dòng)執(zhí)行。然后我們只需要對(duì)構(gòu)建文件按照自己的需求進(jìn)行相應(yīng)的配置,就可以構(gòu)建出自己所需要的項(xiàng)目。

那么,整個(gè)Andoid項(xiàng)目的構(gòu)建過程中,都執(zhí)行了那些構(gòu)建的任務(wù)呢?

首先看一下,Google官方為我們提供的詳細(xì)的構(gòu)建過程圖

構(gòu)建過程概述

如果你接觸Android開發(fā)已經(jīng)有一段時(shí)間了,我想當(dāng)你看到這張圖的時(shí)候,就會(huì)覺得很清晰。但是更多的可能會(huì)一頭霧水,如果之前沒有閱讀相關(guān)的資料的話,那么,接下來,將針對(duì)上述的構(gòu)建過程,先給出一個(gè)概述,這樣你將會(huì)整個(gè)構(gòu)建流程在心中有一個(gè)框架,然后針對(duì)其中具體的細(xì)節(jié),進(jìn)行進(jìn)一步詳細(xì)的講解。

圖中綠色標(biāo)注為其中用到的相應(yīng)工具,藍(lán)色代表的是中間生成的各類文件類型。

  • 首先aapt工具會(huì)將資源文件進(jìn)行轉(zhuǎn)化,生成對(duì)應(yīng)資源ID的R文件和資源文件。
  • adil工具會(huì)將其中的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文件。
  • 此時(shí)我們得到了經(jīng)過處理后的資源文件和一個(gè)dex文件,當(dāng)然,還會(huì)存在一些其它的資源文件,這個(gè)時(shí)候,就是將其打包成一個(gè)類似apk的文件。但還并不是直接可以安裝在Android系統(tǒng)上的APK文件。
  • 通過簽名工具對(duì)其進(jìn)行簽名。
  • 通過Zipalign進(jìn)行優(yōu)化,提升運(yùn)行速度(原理后文會(huì)提及)。
  • 最終,一個(gè)可以安裝在我們手機(jī)上的APK了。

通過上述講解,我想對(duì)于Android項(xiàng)目的整個(gè)構(gòu)建過程,應(yīng)該有了一個(gè)很清晰的框架了,下面將針對(duì)其中的具體的細(xì)節(jié),和前面挖的一些坑,來進(jìn)行更細(xì)致的分析,下圖是一個(gè)Android項(xiàng)目構(gòu)建過程的詳細(xì)步驟圖。


詳細(xì)構(gòu)建過程

接下來的分析,我們還是按照上述構(gòu)建過程概述的順序和流程,進(jìn)行具體的分析。

第1步:aapt打包資源文件,生成R.java和編譯后的資源(二進(jìn)制文件)

講到資源文件的處理,我們先來看一下Android中的資源文件有那些呢?Android應(yīng)用程序資源可以分為兩大類,分別是assets和res:
1. assets類資源放在工程根目錄的assets子目錄下,它里面保存的是一些原始的文件,可以以任何方式來進(jìn)行組織。這些文件最終會(huì)被原裝不動(dòng)地打包在apk文件中。如果我們要在程序中訪問這些文件,那么就需要指定文件名來訪問。例如,假設(shè)在assets目錄下有一個(gè)名稱為filename的文件,那么就可以使用以下代碼來訪問它:

AssetManager am= getAssets();    
InputStream is = assset.open("filename");  

2. res類資源放在工程根目錄的res子目錄下,它里面保存的文件大多數(shù)都會(huì)被編譯,并且都會(huì)被賦予資源ID。這樣我們就可以在程序中通過ID來訪問res類的資源。res類資源按照不同的用途可以進(jìn)一步劃分為以下10種子類型:
layout(布局文件),drawable,xml,value,menu,raw,color,anim,animator,mipmap。
為了使得一個(gè)應(yīng)用程序能夠在運(yùn)行時(shí)同時(shí)支持不同的大小和密度的屏幕,以及支持國(guó)際化,即支持不同的國(guó)家地區(qū)和語(yǔ)言,Android應(yīng)用程序資源的組織方式有18個(gè)維度,每一個(gè)維度都代表一個(gè)配置信息,從而可以使得應(yīng)用程序能夠根據(jù)設(shè)備的當(dāng)前配置信息來找到最匹配的資源來展現(xiàn)在UI上,從而提高用戶體驗(yàn)。由于Android應(yīng)用程序資源的組織方式可以達(dá)到18個(gè)維度,因此就要求Android資源管理框架能夠快速定位最匹配設(shè)備當(dāng)前配置信息的資源來展現(xiàn)在UI上,否則的話,就會(huì)影響用戶體驗(yàn)。為了支持Android資源管理框架快速定位最匹配資源,Android資源打包工具aapt在編譯和打包資源的過程中,會(huì)執(zhí)行以下兩個(gè)額外的操作:

  • 賦予每一個(gè)非assets資源一個(gè)ID值,這些ID值以常量的形式定義在一個(gè)R.java文件中。
  • 生成一個(gè)resources.arsc文件,用來描述那些具有ID值的資源的配置信息,它的內(nèi)容就相當(dāng)于是一個(gè)資源索引表。包含了所有的id值的數(shù)據(jù)集合。在該文件中,如果某個(gè)id對(duì)應(yīng)的是string,那么該文件會(huì)直接包含該值,如果id對(duì)應(yīng)的資源是某個(gè)layout或者drawable資源,那么該文件會(huì)存入對(duì)應(yīng)資源的路徑。

為什么要轉(zhuǎn)化為二進(jìn)制文件?

  • 二進(jìn)制格式的XML文件占用空間更小。這是由于所有XML元素的標(biāo)簽、屬性名稱、屬性值和內(nèi)容所涉及到的字符串都會(huì)被統(tǒng)一收集到一個(gè)字符串資源池中去,并且會(huì)去重。有了這個(gè)字符串資源池,原來使用字符串的地方就會(huì)被替換成一個(gè)索引到字符串資源池的整數(shù)值,從而可以減少文件的大小。
  • 二進(jìn)制格式的XML文件解析速度更快。這是由于二進(jìn)制格式的XML元素里面不再包含有字符串值,因此就避免了進(jìn)行字符串解析,從而提高速度。
    有了資源ID以及資源索引表之后,Android資源管理框架就可以迅速將根據(jù)設(shè)備當(dāng)前配置信息來定位最匹配的資源了。

對(duì)于具體的一些操作流程,可以參考本人之前的一篇文章APK打包安裝過程或者更偏向于源碼層級(jí)的老羅的文章。(文后參考文獻(xiàn)鏈接)

第2步:aidl

aidl,全名Android Interface Definition Language,即Android接口定義語(yǔ)言。是我們?cè)诰帉戇M(jìn)程間通信的代碼的時(shí)候,定義的接口。
輸入: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)。輸出:對(duì)于gradle編譯,可以在build/intermediates/classes里,看到輸出的class文件。

第4步:代碼混淆(proguard)

源碼編譯之后,我們可能還會(huì)對(duì)其進(jìn)行代碼的混淆,混淆的作用是增加反編譯的難度,同時(shí)也將一些代碼的命名進(jìn)行了縮短,減少代碼占用的空間?;煜瓿芍螅瑫?huì)生成一個(gè)混淆前后的映射表,這個(gè)是用來在反應(yīng)我們的應(yīng)用執(zhí)行的時(shí)候的一些堆棧信息,可以將混淆后的信息轉(zhuǎn)化為我們混淆前實(shí)際代碼中的內(nèi)容。
而這個(gè)過程使用的工具就是ProGuard,是一個(gè)開源的Java代碼混淆器(obfuscation)。ADT r8開始它被默認(rèn)集成到了Android SDK中。 其具備三個(gè)主要功能。

  • 壓縮 - 移除無效的類、屬性、方法等
  • 優(yōu)化 - 優(yōu)化bytecode移除沒用的結(jié)構(gòu)
  • 混淆 - 把類名、屬性名、方法名替換為晦澀難懂的1到2個(gè)字母的名字
    當(dāng)然它也只能混淆Java代碼,Android工程中Native代碼,資源文件(圖片、xml),它是無法混淆的。而且對(duì)于Java的常量值也是無法混淆的,所以不要使用常量定義平文的密碼等重要信息。同時(shí)對(duì)于混淆,我們可以通過代碼制定去混淆那些,不去混淆那些。
-keep public class com.rensanning.example.Test

第5步:轉(zhuǎn)化為dex

調(diào)用dx.bat將所有的class文件轉(zhuǎn)化為classes.dex文件,dx會(huì)將class轉(zhuǎn)換為Dalvik字節(jié)碼,生成常量池,消除冗余數(shù)據(jù)等。由于dalvik是一種針對(duì)嵌入式設(shè)備而特殊設(shè)計(jì)的java虛擬機(jī),所以dex文件與標(biāo)準(zhǔn)的class文件在結(jié)構(gòu)設(shè)計(jì)上有著本質(zhì)的區(qū)別,當(dāng)java程序編譯成class后,使用dx工具將所有的class文件整合到一個(gè)dex文件,目的是其中各個(gè)類能夠共享數(shù)據(jù),在一定程度上降低了冗余,同時(shí)也是文件結(jié)構(gòu)更加經(jīng)湊,實(shí)驗(yàn)表明,dex文件是傳統(tǒng)jar文件大小的50%左右。class文件結(jié)構(gòu)和dex文件結(jié)構(gòu)比對(duì)。


Dex和Class比對(duì)

第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一個(gè)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步:對(duì)APK簽名

對(duì)APK文件進(jìn)行簽名。Android系統(tǒng)在安裝APK的時(shí)候,首先會(huì)檢驗(yàn)APK的簽名,如果發(fā)現(xiàn)簽名文件不存在或者校驗(yàn)簽名失敗,則會(huì)拒絕安裝,所以應(yīng)用程序在發(fā)布之前一定要進(jìn)行簽名。簽名信息中包含有開發(fā)者信息,在一定程度上可以防止應(yīng)用被偽造。對(duì)一個(gè)APK文件簽名之后,APK文件根目錄下會(huì)增加META-INF目錄,該目錄下增加三個(gè)文件:

  • MANIFEST.MF
  • [CERT].RSA
  • [CERT]

Android系統(tǒng)就是根據(jù)這三個(gè)文件的內(nèi)容對(duì)APK文件進(jìn)行簽名檢驗(yàn)的。簽名過程主要利用apksign.jar或者jarsinger.jar兩個(gè)工具。將根據(jù)我們提供的Debug和Release兩個(gè)版本的Keystore進(jìn)行相應(yīng)的簽名。

MANIFEST.MF中包含對(duì)apk中除了/META-INF文件夾外所有文件的簽名值,簽名方法是先SHA1()(或其他hash方法)在base64()。存儲(chǔ)形式是:Name加[SHA1]-Digest。

[CERT].SF是對(duì)MANIFEST.MF文件整體簽名以及其中各個(gè)條目的簽名。一般地,如果是使用工具簽名,還多包括一項(xiàng)。就是對(duì)MANIFEST.MF頭部信息的簽名。

[CERT].RSA包含用私鑰對(duì)[CERT].SF的簽名以及包含公鑰信息的數(shù)字證書。

第8步:zipalign優(yōu)化

Zipalign是一個(gè)Android平臺(tái)上整理APK文件的工具,它首次被引入是在Android 1.6版本的SDK軟件開發(fā)工具包中。它能夠?qū)Υ虬腁ndroid應(yīng)用程序進(jìn)行優(yōu)化, 以使Android操作系統(tǒng)與應(yīng)用程序之間的交互作用更有效率,這能夠讓應(yīng)用程序和整個(gè)系統(tǒng)運(yùn)行得更快。用Zipalign處理過的應(yīng)用程序執(zhí)行時(shí)間達(dá)到最低限度,當(dāng)設(shè)備運(yùn)行APK應(yīng)用程序時(shí)占更少的RAM。

  • Zipalign如何進(jìn)行優(yōu)化的呢?

調(diào)用buildtoolszipalign,對(duì)簽名后的APK文件進(jìn)行對(duì)齊處理,使APK中所有資源文件距離文件起始偏移為4字節(jié)的整數(shù)倍,從而在通過內(nèi)存映射訪問APK文件時(shí)會(huì)更快。同時(shí)也減少了在設(shè)備上運(yùn)行時(shí)的內(nèi)存消耗。如果對(duì)于為何提速不理解,那么可以看下內(nèi)存對(duì)齊的規(guī)則以及作用該篇文章,對(duì)于內(nèi)存對(duì)齊的好處有比較生動(dòng)詳細(xì)的解釋。最終這樣我們的APK就生成完畢了。

典型的APK中內(nèi)容

  • AndroidManifest.xml 程序全局配置文件
  • classes.dex Dalvik字節(jié)碼
  • resources.arsc 資源索引表
  • META-INF該目錄下存放的是簽名信息
  • res 該目錄存放資源文件
  • assets該目錄可以存放一些配置或資源文件

總結(jié)

至此,對(duì)于Andoid項(xiàng)目構(gòu)建過程的分析已經(jīng)完成,當(dāng)然,并沒與深入到源碼層級(jí)的分析,本文的旨在對(duì)于構(gòu)建過程流程上的了解和其中一些優(yōu)化的原因所在,為后續(xù)通過Gradle插件hook構(gòu)建過程來做一定的操作,做一個(gè)鋪墊。

參考文章

Android APK 簽名原理及方法
改善android性能工具篇【zipalign】
Android應(yīng)用程序資源的編譯和打包過程分析
Android資源管理框架(Asset Manager)簡(jiǎn)要介紹和學(xué)習(xí)計(jì)劃
Android代碼混淆之ProGuard

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容