10分鐘瞭解Android項目構建流程

前言

上兩篇博客中提到了構建過程的問題,以前畢業在準備面試的過程當中,對這個部分有過較爲認真的學習,也進行了博客記錄,可是實際工做過程當中,若是是在寫業務邏輯上,那麼這方面的問題接觸的就會比較少了。逐漸的淡忘了,其次,以前所寫的文章條理性也不是很強,同時,最近準備進行Gradle插件的一系列博客的產出,其中將會涉及到不少與項目構建相關的內容。因此此文也將成爲後續文章的一個鋪墊。html

構建過程

項目的構建: 當咱們打開一個項目,咱們能夠看到的是咱們寫的Java Code文件or Other JVM Code,資源文件,Build配置文件,可是經過run the project,咱們就能夠獲得一個在咱們的Andoid設備上能夠運行的Apk,上線應用市場,還須要咱們對其進行簽名處理,來確保咱們App的惟一性和安全性。整個過程就是所謂的項目構建。java

如何實現整個構建的過程,對於每個構建的步驟,都須要相應的功能模塊來進行,好比Java Code編譯,如何打成dex包等等,而這Android則爲咱們提供了相應的工具,在Android Studio命令行窗口中,咱們能夠經過相應的命令行來進行控制,可是,整個構建過程涉及到不少的步驟,不少的工具的使用,若是都經過命令行來進行控制,勢必會至關麻煩,所以Androd Studio等IDE則對整個過程進行了一個打包,當咱們在Run project的時候,底層的打包工具就會被調用,打包流程都會自動執行。而後咱們只須要對構建文件按照本身的需求進行相應的配置,就能夠構建出本身所須要的項目。android

那麼,整個Andoid項目的構建過程當中,都執行了那些構建的任務呢?面試

首先看一下,Google官方爲咱們提供的詳細的構建過程圖segmentfault

構建過程概述

若是你接觸Android開發已經有一段時間了,我想當你看到這張圖的時候,就會以爲很清晰。可是更多的可能會一頭霧水,若是以前沒有閱讀相關的資料的話,那麼,接下來,將針對上述的構建過程,先給出一個概述,這樣你將會整個構建流程在心中有一個框架,而後針對其中具體的細節,進行進一步詳細的講解。安全

圖中綠色標註爲其中用到的相應工具,藍色表明的是中間生成的各種文件類型。bash

  • 首先aapt工具會將資源文件進行轉化,生成對應資源ID的R文件和資源文件。
  • adil工具會將其中的aidl接口轉化成Java的接口
  • 至此,Java Compiler開始進行Java文件向class文件的轉化,將R文件,Java源代碼,由aidl轉化來的Java接口,統一轉化成.class文件。
  • 經過dx工具將class文件轉化爲dex文件。
  • 此時咱們獲得了通過處理後的資源文件和一個dex文件,固然,還會存在一些其它的資源文件,這個時候,就是將其打包成一個相似apk的文件。但還並非直接能夠安裝在Android系統上的APK文件。
  • 經過簽名工具對其進行簽名。
  • 經過Zipalign進行優化,提高運行速度(原理後文會說起)。
  • 最終,一個能夠安裝在咱們手機上的APK了。

經過上述講解,我想對於Android項目的整個構建過程,應該有了一個很清晰的框架了,下面將針對其中的具體的細節,和前面挖的一些坑,來進行更細緻的分析,下圖是一個Android項目構建過程的詳細步驟圖。 框架

詳細構建過程
接下來的分析,咱們仍是按照上述構建過程概述的順序和流程,進行具體的分析。

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

講到資源文件的處理,咱們先來看一下Android中的資源文件有那些呢?Android應用程序資源能夠分爲兩大類,分別是assets和res:    1. assets類資源放在工程根目錄的assets子目錄下,它裏面保存的是一些原始的文件,能夠以任何方式來進行組織。這些文件最終會被原裝不動地打包在apk文件中。若是咱們要在程序中訪問這些文件,那麼就須要指定文件名來訪問。例如,假設在assets目錄下有一個名稱爲filename的文件,那麼就可使用如下代碼來訪問它:工具

AssetManager am= getAssets();    
InputStream is = assset.open("filename");  
複製代碼

2. res類資源放在工程根目錄的res子目錄下,它裏面保存的文件大多數都會被編譯,而且都會被賦予資源ID。這樣咱們就能夠在程序中經過ID來訪問res類的資源。res類資源按照不一樣的用途能夠進一步劃分爲如下10種子類型: layout(佈局文件),drawable,xml,value,menu,raw,color,anim,animator,mipmap。 爲了使得一個應用程序可以在運行時同時支持不一樣的大小和密度的屏幕,以及支持國際化,即支持不一樣的國家地區和語言,Android應用程序資源的組織方式有18個維度,每個維度都表明一個配置信息,從而可使得應用程序可以根據設備的當前配置信息來找到最匹配的資源來展示在UI上,從而提升用戶體驗。因爲Android應用程序資源的組織方式能夠達到18個維度,所以就要求Android資源管理框架可以快速定位最匹配設備當前配置信息的資源來展示在UI上,不然的話,就會影響用戶體驗。爲了支持Android資源管理框架快速定位最匹配資源,Android資源打包工具aapt在編譯和打包資源的過程當中,會執行如下兩個額外的操做:佈局

  • 賦予每個非assets資源一個ID值,這些ID值以常量的形式定義在一個R.java文件中。
  • 生成一個resources.arsc文件,用來描述那些具備ID值的資源的配置信息,它的內容就至關因而一個資源索引表。包含了全部的id值的數據集合。在該文件中,若是某個id對應的是string,那麼該文件會直接包含該值,若是id對應的資源是某個layout或者drawable資源,那麼該文件會存入對應資源的路徑。

爲何要轉化爲二進制文件?

  • 二進制格式的XML文件佔用空間更小。這是因爲全部XML元素的標籤、屬性名稱、屬性值和內容所涉及到的字符串都會被統一收集到一個字符串資源池中去,而且會去重。有了這個字符串資源池,原來使用字符串的地方就會被替換成一個索引到字符串資源池的整數值,從而能夠減小文件的大小。
  • 二進制格式的XML文件解析速度更快。這是因爲二進制格式的XML元素裏面再也不包含有字符串值,所以就避免了進行字符串解析,從而提升速度。 有了資源ID以及資源索引表以後,Android資源管理框架就能夠迅速將根據設備當前配置信息來定位最匹配的資源了。

對於具體的一些操做流程,能夠參考本人以前的一篇文章APK打包安裝過程或者更偏向於源碼層級的老羅的文章。(文後參考文獻連接)

第2步:aidl

aidl,全名Android Interface Definition Language,即Android接口定義語言。是咱們在編寫進程間通訊的代碼的時候,定義的接口。 輸入:aidl後綴的文件。輸出:可用於進程通訊的C/S端java代碼,位於build/generated/source/aidl。

第3步:Java源碼編譯

咱們有了R.java和aidl生成的Java文件,再加上工程的源代碼,如今可使用javac進行正常的java編譯生成class文件了。

輸入:java source的文件夾(另外還包括了build/generated下的:R.java, aidl生成的java文件,以及BuildConfig.java)。輸出:對於gradle編譯,能夠在build/intermediates/classes裏,看到輸出的class文件。

第4步:代碼混淆(proguard)

源碼編譯以後,咱們可能還會對其進行代碼的混淆,混淆的做用是增長反編譯的難度,同時也將一些代碼的命名進行了縮短,減小代碼佔用的空間。混淆完成以後,會生成一個混淆先後的映射表,這個是用來在反應咱們的應用執行的時候的一些堆棧信息,能夠將混淆後的信息轉化爲咱們混淆前實際代碼中的內容。 而這個過程使用的工具就是ProGuard,是一個開源的Java代碼混淆器(obfuscation)。ADT r8開始它被默認集成到了Android SDK中。 其具有三個主要功能。

  • 壓縮 - 移除無效的類、屬性、方法等
  • 優化 - 優化bytecode移除沒用的結構
  • 混淆 - 把類名、屬性名、方法名替換爲晦澀難懂的1到2個字母的名字 固然它也只能混淆Java代碼,Android工程中Native代碼,資源文件(圖片、xml),它是沒法混淆的。並且對於Java的常量值也是沒法混淆的,因此不要使用常量定義平文的密碼等重要信息。同時對於混淆,咱們能夠經過代碼制定去混淆那些,不去混淆那些。
-keep public class com.rensanning.example.Test
複製代碼

第5步:轉化爲dex

調用dx.bat將全部的class文件轉化爲classes.dex文件,dx會將class轉換爲Dalvik字節碼,生成常量池,消除冗餘數據等。因爲dalvik是一種針對嵌入式設備而特殊設計的java虛擬機,因此dex文件與標準的class文件在結構設計上有着本質的區別,當java程序編譯成class後,使用dx工具將全部的class文件整合到一個dex文件,目的是其中各個類可以共享數據,在必定程度上下降了冗餘,同時也是文件結構更加經湊,實驗代表,dex文件是傳統jar文件大小的50%左右。class文件結構和dex文件結構比對。

Dex和Class比對

第6步:apkbuilder

打包生成APK文件。舊的apkbuilder腳本已經廢棄,如今都已經經過sdklib.jar的ApkBuilder類進行打包了。輸入爲咱們以前生成的包含resources.arcs的.ap_文件,上一步生成的dex文件,以及其餘資源如jni、.so文件。 大體步驟爲 以包含resources.arcs的.ap_文件爲基礎,new一個ApkBuilder,設置debugMode

apkBuilder.addZipFile(f);
apkBuilder.addSourceFolder(f);
apkBuilder.addResourcesFromJar(f);
apkBuilder.addNativeLibraries(nativeFileList);
apkBuilder.sealApk(); // 關閉apk文件
generateDependencyFile(depFile, inputPaths, outputFile.getAbsolutePath());

複製代碼

第7步:對APK簽名

對APK文件進行簽名。Android系統在安裝APK的時候,首先會檢驗APK的簽名,若是發現簽名文件不存在或者校驗簽名失敗,則會拒絕安裝,因此應用程序在發佈以前必定要進行簽名。簽名信息中包含有開發者信息,在必定程度上能夠防止應用被僞造。對一個APK文件簽名以後,APK文件根目錄下會增長META-INF目錄,該目錄下增長三個文件:

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

Android系統就是根據這三個文件的內容對APK文件進行簽名檢驗的。簽名過程主要利用apksign.jar或者jarsinger.jar兩個工具。將根據咱們提供的Debug和Release兩個版本的Keystore進行相應的簽名。

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

[CERT].SF是對MANIFEST.MF文件總體簽名以及其中各個條目的簽名。通常地,若是是使用工具簽名,還多包括一項。就是對MANIFEST.MF頭部信息的簽名。

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

第8步:zipalign優化

Zipalign是一個Android平臺上整理APK文件的工具,它首次被引入是在Android 1.6版本的SDK軟件開發工具包中。它可以對打包的Android應用程序進行優化, 以使Android操做系統與應用程序之間的交互做用更有效率,這可以讓應用程序和整個系統運行得更快。用Zipalign處理過的應用程序執行時間達到最低限度,當設備運行APK應用程序時佔更少的RAM。

  • Zipalign如何進行優化的呢?

調用buildtoolszipalign,對簽名後的APK文件進行對齊處理,使APK中全部資源文件距離文件起始偏移爲4字節的整數倍,從而在經過內存映射訪問APK文件時會更快。同時也減小了在設備上運行時的內存消耗。若是對於爲什麼提速不理解,那麼能夠看下內存對齊的規則以及做用該篇文章,對於內存對齊的好處有比較生動詳細的解釋。最終這樣咱們的APK就生成完畢了。

典型的APK中內容

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

總結

至此,對於Andoid項目構建過程的分析已經完成,固然,並沒與深刻到源碼層級的分析,本文的旨在對於構建過程流程上的瞭解和其中一些優化的緣由所在,爲後續經過Gradle插件hook構建過程來作必定的操做,作一個鋪墊。

參考文章

Android APK 簽名原理及方法

改善android性能工具篇【zipalign】

Android應用程序資源的編譯和打包過程分析

Android資源管理框架(AssetManager)簡要介紹和學習計劃

Android代碼混淆之ProGuard

相關文章
相關標籤/搜索