1. 程式人生 > >Android FrameWork學習(一)Android 7.0系統原始碼下載\編譯

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
  1. 更新系統預設使用的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

這兩個指令的效果是一樣的,會初始化一些有用的命令工具

Paste_Image.png

我們後面執行的一些指令必須在初始化 envsetup之後才能執行

選擇編譯目標

接著我們通過 lunch 指令來選擇我們需要編譯的目標
執行lunch指令

$ lunch

會彈出可選目標項:

Paste_Image.png

所有的構建目標由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

Paste_Image.png

輸出的值如果是大於0的,則表明你的cpu支援

如果你使用的是vmware虛擬機器安裝的linux,注意要設定下虛擬機器的cpu來支援硬體虛擬化,先關閉虛擬機器,然後右鍵虛擬機器=》設定,選中cpu,勾選虛擬化Intel VT項,這樣就能支援KVM了。

Paste_Image.png

對於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檔案,如圖:

Paste_Image.png

而Jack編譯工具鏈則跳過了編譯成.class檔案這一過程,直接將Java程式碼編譯成.dex檔案

Paste_Image.png

它的優勢:

  • 完全開放原始碼
    原始碼均在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目錄

Paste_Image.png

可以通過修改該目錄中config.properties檔案裡的
.jack.server.max-service
值來設定併發執行緒數。

Paste_Image.png

同時,你也可以設定增加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

第一次啟動時間可能會有點長,耐心等待即可
Paste_Image.png

執行成功了,是不是有點小激動呢!

注意:如果你的命令列視窗關閉重開了,那emulator指令可能會提示找不到命令,我們可以在原始碼根目錄環境下,通過envsetup.sh重新初始化命令,執行lunch指令選擇編譯目標,這個時候你再執行emulator就不會提示找不到指令了(每次關閉命令列視窗都需要重新執行如下指令才能執行emulator)

也可以通過配置環境變數來設定emulator指令,不過我沒有成功,哈哈

$ source build/envsetup.sh
$ lunch
$ emulator

好啦,到此,我們的原始碼就編譯完畢啦,下一篇我們來聊聊如何使用Android Studio匯入Android系統原始碼,並通過AS進行Java原始碼除錯,以及使用GDB來除錯系統Native C\C++原始碼。