Android FrameWork學習(一)Android 7.0系統原始碼下載\編譯
最近計劃著研究下Android 7.0的系統原始碼,之前也沒做過什麼記錄,這次正好將學習的內容記錄下來,方便以後複習鞏固。
既然要學習我們的系統原始碼,那我們第一步要做的就是下載原始碼並進行編譯了。
硬體環境要求
1. 編譯環境
按照官方的說法,編譯Android 2.3.x及以上版本的系統原始碼需要64位的系統執行環境來支援,而編譯2.3.x以下的版本則需要32位的系統執行環境。
2. 硬碟空間
官方建議最好預留100G的磁碟空間來下載原始碼,150G的磁碟空間用來編譯原始碼,如果使用了ccache(一個高速編譯快取工具,可以大幅加快gcc的編譯速度),那麼則需要更大的空間來支援。
所以儘可能地保證自己的磁碟空間夠大吧,之前就因為磁碟空間預留不夠導致原始碼編譯過程中空間不足,狠狠地把自己坑了一把。
3. 記憶體空間
如果你是在虛擬機器上跑linux,官方建議至少需要16G的記憶體空間,我的機器只有8G的記憶體空間跑虛擬機器,目前跑起來也沒太大問題,就是編譯原始碼的過程非常漫長,不知道是否跟記憶體大小有關。
軟體環境要求
1. 作業系統
Android系統的原始碼的編譯支援Linux跟Mac OS兩種作業系統,一般情況下,Android系統原始碼都是在Linux Ubuntu系統上進行開發與測試的,所以如果你準備使用Linux系統來進行原始碼編譯,那一般推薦安裝Ubuntu版本的Linux。
下面列出了各Android版本與編譯系統版本的對應關係
Linux:
Android版本 | GNU/Linux |
---|---|
Android 6.0 (Marshmallow) - Android最新版本 | Ubuntu 14.04 (Trusty) |
Android 2.3.x (Gingerbread) - Android 5.x (Lollipop) | Ubuntu 12.04 (Precise) |
Android 1.5 (Cupcake) - Android 2.2.x (Froyo) | Ubuntu 10.04 (Lucid) |
Mac OS
Android版本 | Mac OS (Intel/x86) |
---|---|
Android 6.0 (Marshmallow) - Android最新版本 | Mac OS v10.10 (Yosemite) or later with Xcode 4.5.2 and Command Line Tools |
Android 5.x (Lollipop) | Mac OS v10.8 (Mountain Lion) with Xcode 4.5.2 and Command Line Tools |
Android 4.1.x-4.3.x (Jelly Bean) - Android 4.4.x (KitKat) | Mac OS v10.6 (Snow Leopard) or Mac OS X v10.7 (Lion) and Xcode 4.2 (Apple’s Developer Tools) |
Android 1.5 (Cupcake) - Android 4.0.x (Ice Cream Sandwich) | Mac OS v10.5 (Leopard) or Mac OS X v10.6 (Snow Leopard) and the Mac OS X v10.5 SDK |
2.JDK 版本要求
不同的Android版本編譯也需要對應的JDK環境,這裡列出了各版本之間的對應關係
Android版本 | JDK版本(Ubuntu) | JDK版本(Mac OS) |
---|---|---|
Android 2.3.x (Gingerbread) - Android 4.4.x (KitKat) | ||
Android 1.5 (Cupcake) - Android 2.2.x (Froyo) |
搭建編譯環境
根據上面列出的軟硬體要求,我們可以根據自己要編譯的Android版本以及自己的裝置來選擇合適的系統及JDK,接下來我們就來說說如何搭建編譯環境。
這裡我們主要針對Android 7.0的需要的編譯環境分別對Linux和Mac OS進行配置:
設定Linux系統編譯環境
1.安裝JDK
Android 7.0目前需要openJDK 8的JDK環境
Ubuntu 15.04+
如果你的系統是Ubuntu 15.04及以上版本的話,直接執行如下指令即可直接安裝:
$ sudo apt-get update
$ sudo apt-get install openjdk-8-jdk
Ubuntu 14.04
如果你使用的是Ubuntu 14.04版本,現在並沒有專門針對14.0.4可用的open jdk8的包,
但是Ubuntu 15.04 OpenJDK 8的包可以在14.0.4上成功地執行,所以我們下載Ubuntu 15.04 OpenJDK 8的安裝包來手動安裝:
sudo apt-get update
接著依次對上面下載的三個deb檔案執行如下指令進行安裝:
sudo dpkg -i 下載的檔案地址
最後執行 apt-get -f 指令修復安裝依賴的包
sudo apt-get -f install
- 更新系統預設使用的JDK版本
如果你的系統安裝了多個版本的JDK,可以通過下面的指令執行切換,會彈出可選的JDK版本,根據你的需要選擇對應的版本就可以了:
sudo update-alternatives --config java
sudo update-alternatives --config javac
2.安裝所需要的工具包
Ubuntu 14.04
我們編譯過程中會用到下面的依賴包,執行如下指令統一安裝:
sudo apt-get install git-core gnupg flex bison gperf build-essential \
zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 \
lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z-dev ccache \
libgl1-mesa-dev libxml2-utils xsltproc unzip
3.設定原始碼編譯輸出路徑
預設情況下,編譯好的系統原始碼會在原始碼所在目錄的out資料夾下,
如果你希望調整輸出目錄的路徑,可以執行下面的指令指定輸出目錄:
export OUT_DIR_COMMON_BASE=<path-to-your-out-directory>
4.設定USB介面訪問裝置
在linux下,預設情況是不允許普通使用者直接通過USB介面來訪問裝置的.
推薦方法是以根使用者身份在 /etc/udev/rules.d/51-android.rules
路徑建立檔案。
我們可以通過如下指令來實現(注意用你的系統username替換指令中的):
wget -S -O - http://source.android.com/source/51-android.rules | sed "s/<username>/$USER/" | sudo tee >/dev/null /etc/udev/rules.d/51-android.rules; sudo udevadm control --reload-rules
設定Mac OS系統編譯環境
Mac OS的檔案系統預設情況下保留了大小寫實際上卻又不區分大小寫。
目前的git指令無法支援這樣的檔案系統,會導致一些莫名其妙的錯誤,所以在Mac OS上編譯Android系統原始碼,我們必須先建立一塊區分大小寫的磁碟映象。
建立一塊區分大小寫的磁碟映象
這裡我們直接通過命令列來建立:
hdiutil create -type SPARSE -fs 'Case-sensitive Journaled HFS+' -size 40g ~/android.dmg
該指令會在系統根目錄下生成一個android.dmg或是android.dmg.sparseimage檔案,一旦掛載,將被作為支援Android開發所需格式的驅動映象分割槽。
如果之後你需要更大的空間,你可以通過下面的指令進行空間調整:
hdiutil resize -size <new-size-you-want>g ~/android.dmg.sparseimage
你還可以在 ~/.bash_profile
檔案中,新增幫助函式來掛載跟取消掛載:
# mount the android file image
function mountAndroid { hdiutil attach ~/android.dmg -mountpoint /Volumes/android; }
#如果建立dmg檔案時生成的是android.dmg.sparseimage檔案,則使用
function mountAndroid { hdiutil attach ~/android.dmg.sparseimage -mountpoint /Volumes/android; }
# unmount the android file image
function umountAndroid() { hdiutil detach /Volumes/android; }
之後我們就可以通過執行mountAndroid指令來執行掛載映象,通過umountAndroid指令來取消掛載。
安裝JDK
安裝工具依賴包
1. 安裝xcode命令列工具
$ xcode-select --install
對於老版本的Mac OS系統(10.8或者10.8之前的),我們需要到蘋果開發者站點進行下載安裝.
如果你還沒有註冊成為蘋果開發者,你需要先註冊一個蘋果賬號來進行下載.
2. 到 macports.org 上下載對應Mac OS版本的macports(類似於Linux下的apt-get,用來幫助你安裝其他應用程式)
注意:確保
/opt/local/bin
在路徑/usr/bin
前,如果沒有,在~/.bash_profile
檔案中進行新增
export PATH=/opt/local/bin:$PATH
注意:如果根目錄下沒有
.bash_profile
檔案,那就手動建立一個
3. 通過macports安裝make, git以及GPG
$ POSIXLY_CORRECT=1 sudo port install gmake libsdl git gnupg
如果使用的是Mac OS X v10.4版本的系統,還需要安裝bison:
$ POSIXLY_CORRECT=1 sudo port install bison
注意:如果是編譯Android 4.0.x及以下版本的系統,gmake 3.8.2版本存在一個bug,需要還原到gmake 3.8.1
優化編譯環境(可選)
設定ccache
我們可以自由選擇是否開啟ccache編譯工具。
ccache是一個高速編譯快取工具,它通過將標頭檔案快取記憶體到原始檔之中而改進了構建效能,因而通過減少每一步編譯時新增標頭檔案所需要的時間而提高了C\C++的構建速度。
從編譯的全過程來看,不使用ccache的情況下,編譯過程中會多次解析相同的標頭檔案,浪費了處理器週期,更重要的是浪費了開發者的時間,因為他們要等待這一過程的完成。在一個團隊中,這一影響可能會更為明顯,因為團隊成員可能會反覆編譯解析相同的標頭檔案。
所以,一般對於專門用來編譯系統的伺服器或是大容量的生產環境,這個功能比較有用,它可以加速重新編譯的速度。
注意:如果你只是個人開發者,不是專門的編譯伺服器,不需要進行增量構建的話,那麼使用ccache可能會因為快取記憶體缺失而降低你的構建速度。
開啟ccache
要開啟ccache,在原始碼樹的根路徑下執行下面的指令:
$ export USE_CCACHE=1
$ export CCACHE_DIR=/<path_of_your_choice>/.ccache
$ prebuilts/misc/linux-x86/ccache/ccache -M 50G
快取的大小一般設定為50G-100G
接著在 .bashrc (或者etc/profile)中新增下面的指令
export USE_CCACHE=1
預設情況下,快取會存在home根目錄的~/.ccache中,但是如果你使用的是NFS或者其他的非本地檔案系統,那麼你同樣需要在.bashrc指定快取目錄地址
在Mac OS的系統中,你需要將linux-x86替換成darwin-x86:
prebuilts/misc/darwin-x86/ccache/ccache -M 50G
當編譯的Android系統是4.0.x或者更老的版本,ccache的快取路徑會有所不同
prebuilt/linux-x86/ccache/ccache -M 50G
這個設定會一直儲存在CCACHE_DIR中。
在Linux上,你可以通過以下指令開啟對ccache的監聽:
$ watch -n1 -d prebuilts/misc/linux-x86/ccache/ccache -s
下載原始碼
編譯環境配置好之後,我們就可以開始下載我們的原始碼了
安裝Repo
Repo是google用python寫的一個指令碼工具,Android使用git作為程式碼管理工具,一個Android系統由N多個git庫構成,如果手動進行一個個下載,那簡直是一件非常痛苦的事,而repo就是用來對這些git庫進行維護管理跟下載的。
通過Repo工具,我們可以輕鬆地完成Android系統原始碼的下載。
1.在系統home根路徑下建立bin目錄並且新增到path路徑中:
#建立bin目錄
$ mkdir ~/bin
#把bin目錄的路徑新增到PATH中
$ PATH=~/bin:$PATH
2.下載repo工具並設定其可執行
#curl是個開原始檔傳輸工具,在這裡是把遠端的repo檔案下載到指定的~/bin/repo路徑
$ curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
#修改repo對所有人可執行
$ chmod a+x ~/bin/repo
初始化Repo客戶端
1.建立一個空目錄用來存放我們的Android系統原始碼,名字自己隨便定
#建立名為WORKING_DIRECTORY的目錄
$ mkdir WORKING_DIRECTORY
#進入到建立的目錄中
$ cd WORKING_DIRECTORY
2.初始化repo倉庫
從主幹master下載原始碼,目前最新版本
$ repo init -u https://android.googlesource.com/platform/manifest
如果需要下載某個特定版本系統的分支,可以在上述命令後加-b 版本分支號,這裡我指定Android 7.0的版本分支
$ repo init -u https://android.googlesource.com/platform/manifest -b android-7.1.0_r7
具體的版本分支號可以到這個地址檢視(需要翻牆):
Android系統個版本分支號
3.同步原始碼到本地
這時執行sync指令便可以自動下載原始碼到本地了
$ repo sync
使用國內映象下載原始碼
由於國內網路的問題,上述操作的原始碼下載需要翻牆才能進行,速度會受到很大影響,幾十G的系統原始碼可能需要花上上週的時間才能下完,
因此我們可以選擇國內的映象進行原始碼下載:
清華大學的映象站
參照頁面上的描述對上面的指令稍作調整便可以了,站點上寫得比較詳細,這裡我們就不作贅述了。
根據網速的不同,一般一天之內能夠下載完畢。
對於下載下來的原始碼,我們並不能直接刷到我們的目標裝置上或者是使用模擬器執行,我們必須對原始碼進行編譯生成對應的image二進位制映象檔案,
當然你也可以直接從官網下載對應系統版本的映象檔案(需翻牆):
Google’s Nexus driver page
Binaries Preview for Nexus Devices
這裡我們還是自己來編譯下原始碼,熟悉下整個編譯過程。
原始碼編譯
首先我們通過命令列進入到原始碼目錄中,我這裡目錄的名稱是aosp
cd aosp
清空輸出目錄
為了確保我們編譯生成的檔案不受之前build構建的檔案影響,我們在原始碼目錄中執行下面的指令,該指令會清空out輸出目錄中的所有檔案
$ make clobber
設定編譯環境
首先我們通過原始碼build目錄中的 envsetup.sh
指令碼檔案初始化我們的編譯環境,執行
$ source build/envsetup.sh
或
$ . build/envsetup.sh
這兩個指令的效果是一樣的,會初始化一些有用的命令工具
我們後面執行的一些指令必須在初始化 envsetup
之後才能執行
選擇編譯目標
接著我們通過 lunch
指令來選擇我們需要編譯的目標
執行lunch指令
$ lunch
會彈出可選目標項:
所有的構建目標由BUILD-BUILDTYPE的形式組成:
BUILD對應codename
這是官方提供的一份對照表:
Device | Code name | Build configuration |
---|---|---|
Pixel XL | marlin | aosp_marlin-userdebug |
Pixel | sailfish | aosp_sailfish-userdebug |
HiKey | hikey | hikey-userdebug |
Nexus 6P | angler | aosp_angler-userdebug |
Nexus 5X | bullhead | aosp_bullhead-userdebug |
Nexus 6 | shamu | aosp_shamu-userdebug |
Nexus Player | fugu | aosp_fugu-userdebug |
Nexus 9 | volantis (flounder) | aosp_flounder-userdebug |
Nexus 5 (GSM/LTE) | hammerhead | aosp_hammerhead-userdebug |
Nexus 7 (Wi-Fi) | razor (flo) | aosp_flo-userdebug |
Nexus 7 (Mobile) | razorg (deb) | aosp_deb-userdebug |
Nexus 10 | mantaray (manta) | full_manta-userdebug |
Nexus 4 | occam (mako) | full_mako-userdebug |
Nexus 7 (Wi-Fi) | nakasi (grouper) | full_grouper-userdebug |
Nexus 7 (Mobile) | nakasig (tilapia) | full_tilapia-userdebug |
Galaxy Nexus (GSM/HSPA+) | yakju (maguro) | full_maguro-userdebug |
Galaxy Nexus (Verizon) | mysid (toro) | aosp_toro-userdebug |
Galaxy Nexus (Experimental) | mysidspr (toroplus) | aosp_toroplus-userdebug |
Motorola Xoom (U.S. Wi-Fi) | wingray | full_wingray-userdebug |
Nexus S | soju (crespo) | full_crespo-userdebug |
Nexus S 4G | sojus (crespo4g) | full_crespo4g-userdebug |
構建型別 | 用途 |
---|---|
user | 有限的訪問許可權,主要用於釋出正式產品,沒有root跟除錯許可權 |
userdebug | 跟user型別差不多,但是多了root跟debug除錯許可權 |
eng | 擁有各種除錯工具的開發版設定,擁有root跟debug許可權 |
如果作為開發使用的話,那我們一般都是選 -eng,
這裡我自己是準備在模擬器上執行編譯的image映象,並且我電腦的cpu是intel x86的,所以我選擇了 6. aosp_x86-eng
我們可以根據自己的需要選擇對應的cpu型別。
注意:我們知道,Android官方的模擬器速度很慢,
Intel特意提供了一個叫HAXM的虛擬硬體加速技術,全稱為:Intel Hardware Accelerated Execution Manager.只要你的CPU是intel的產品並且支援VT(virtualization Technology)就可以使用HAXM技術將你的模擬器的速度提升至真機的水平。
目前Intel只提供了windows版和MAC版,Linux系統只有通過安裝KVM來達到這個效果。
安裝KVM
首先我們檢測下自己的cpu是否支援 hardware virtualization(硬體虛擬化)
egrep -c '(vmx|svm)' /proc/cpuinfo
輸出的值如果是大於0的,則表明你的cpu支援
如果你使用的是vmware虛擬機器安裝的linux,注意要設定下虛擬機器的cpu來支援硬體虛擬化,先關閉虛擬機器,然後右鍵虛擬機器=》設定,選中cpu,勾選虛擬化Intel VT項,這樣就能支援KVM了。
對於Ubuntu 10.0.4以上的版本,我們通過下面的指令安裝KVM即可
sudo apt-get install qemu-kvm libvirt-bin ubuntu-vm-builder bridge-utils
這樣,在編譯完目標intel cpu的映象檔案後,我們執行模擬器就會自動進行加速了。
編譯原始碼
好了,一切就緒,我們可以開始編譯我們的原始碼了,
我們在原始碼路徑下通過 make -jN
指令來進行原始碼編譯,這裡的N一般建議設定為cpu核心執行緒數的1-2倍。
$ make -j4
一般情況下,我們等待原始碼編譯完成就可以了,不過從Android 7.0 N開始,make指令預設會開啟Jack編譯工具鏈來進行Java程式碼的編譯,這個過程中可能會出現一些問題。
什麼是Jack編譯工具鏈(The Jack toolchain)?
我們知道,我們平時編譯Android程式碼的時候會先將Java程式碼編譯成.class檔案,最終再轉換成.dex檔案,如圖:
而Jack編譯工具鏈則跳過了編譯成.class檔案這一過程,直接將Java程式碼編譯成.dex檔案
它的優勢:
- 完全開放原始碼
原始碼均在AOSP中,合作伙伴可貢獻原始碼 - 加快編譯原始碼
Jack 提供特殊的配置,減少編譯時間:pre-dexing, 增量編譯和Jack編譯伺服器. - 支援程式碼壓縮,混淆,重打包和multidex
- 不在使用額外單獨的包,例如ProGuard。
如果想進一步瞭解Jack,可以訪問Compiling with Jack(需翻牆),這裡就不作太多解釋了。
按照官方的說法Jack可以加快編譯速度
但實際編譯過程中,在我的裝置上Jack會佔用大量記憶體,並且拖慢編譯速度,還會報錯,而且官方文件上寫的配置方式對Jack並不起作用。
Jack編譯過程中遇到的問題
編譯過程中報Out of memory error並中斷編譯
在執行make指令後,當第一次編譯Java程式碼的時候,Jack會被啟用,這個時候經常會卡住,並且一段時間後報錯Out of memory error。
按照官方的說法,Jack並行編譯的時候佔用的資源太大導致記憶體溢位了
可以通過在 $HOME/.jack
檔案中減小 SERVER_NB_COMPILE
的值來減小並行編譯數量, SERVER_NB_COMPILE
的值預設為4
但實際情況是Jack沒有在根路徑下生成 .jack
檔案,並且手動建立設定 SERVER_NB_COMPILE
後重啟Jack服務也沒有效果。
經測試發現make編譯過程中當Jack第一次被啟用時會在home根路徑下生成.jack-server目錄
可以通過修改該目錄中config.properties檔案裡的
值來設定併發執行緒數。
.jack.server.max-service
同時,你也可以設定增加Jack的記憶體容量來解決這個問題,指令如下
export JACK_SERVER_VM_ARGUMENTS="-Dfile.encoding=UTF-8 -XX:+TieredCompilation -Xmx4096m"
然後進入到輸出路徑的bin目錄下:
cd /home/cjpx00008/aosp/out/host/linux-x86/bin
執行下面的指令重啟Jack服務
./jack-admin stop-server
./jack-admin start-server
我改了service大小,同時增加了記憶體,後來編譯的過程中沒有發生其他問題。
有辦法關閉Jack編譯嗎?
既然Jack有問題,那我們可以關閉Jack編譯嗎?
目前來說我還沒有找到如何在Android 7.0編譯的時候關閉Jack,如果有知道的小夥伴歡迎留言告訴我哈,感激不盡!!
執行編譯出的image映象
經過漫長的等待,我們的原始碼終於編譯結束了,是時候來執行編譯出的image映象了。
這時我們只要在原始碼目錄下執行 emulator
指令即可執行模擬器
$ emulator
第一次啟動時間可能會有點長,耐心等待即可
執行成功了,是不是有點小激動呢!
注意:如果你的命令列視窗關閉重開了,那emulator指令可能會提示找不到命令,我們可以在原始碼根目錄環境下,通過envsetup.sh重新初始化命令,執行lunch指令選擇編譯目標,這個時候你再執行emulator就不會提示找不到指令了(每次關閉命令列視窗都需要重新執行如下指令才能執行emulator)
也可以通過配置環境變數來設定emulator指令,不過我沒有成功,哈哈
$ source build/envsetup.sh
$ lunch
$ emulator
好啦,到此,我們的原始碼就編譯完畢啦,下一篇我們來聊聊如何使用Android Studio匯入Android系統原始碼,並通過AS進行Java原始碼除錯,以及使用GDB來除錯系統Native C\C++原始碼。