天天看點

Android源代碼編譯原理與前期準備

本文的主要内容是解決在Android源代碼的編譯過程中出現的各種問題。

大家都知道,Android是開源的,可以在Android Open Source Project(點選打開連結)下載下傳。下載下傳的流程與方法,可以通路上述網頁檢視詳細說明。

「編譯原理」

首先,我們應該對Android的編譯原理有所了解。普通的Android應用開發,多數是在eclipse中開發的。在eclipse中,Android Project是通過安裝在eclipse中ADT插件進行編譯的。這種編譯方式與在Liunx系統下的編譯方式是不同的。

在Liunx系統下,Android源代碼的編譯方式是通過make file(Android.mk)來實作的。也就是說,在編譯過程中,編譯指令會查找每個檔案夾中是否存在Android.mk檔案,如果存在,那麼系統就會按照Android.mk檔案中的編譯規則進行編譯。

「jdk版本不符合」

我們都知道,Android編譯的指令是make -j*(*代表CPU的核發數)。執行make -j*後,執行的檔案就是build/core/main.mk。在開始編譯之前,會檢測系統是否符合編譯需要的要求。例如檢測系統是否安裝jdk以及jdk的版本等。

不同的Android版本對jdk的版本要求也不盡相同。本文将以android-4.1.2_r1為例進行講解。

Android 4.1規定的jdk版本是1.6,也就是說,如果Linux系統安裝的jdk版本不是1.6的話,編譯将無法繼續進行。

在終端在會出現以下提示資訊:

You are attempting to build with the incorrect version

Your version is:  1.7.0_21.

The correct version is: Java SE 1.6.

解決這個問題的方法就是修改Android源代碼中的檔案,将其規定的版本号改為本機中已安裝的jdk的版本号即可。

修改檔案:

android-4.1.2_r1/build/core/main.mk

# Check for the correct version of java
java_version := $(shell java -version 2>&1 | head -n 1 | grep '^java .*[ "]1\.6[\. "$$]')
ifneq ($(shell java -version 2>&1 | grep -i openjdk),)
java_version :=
endif
ifeq ($(strip $(java_version)),)
$(info ************************************************************)
$(info You are attempting to build with the incorrect version)
$(info of java.)
$(info $(space))
$(info Your version is: $(shell java -version 2>&1 | head -n 1).)
$(info The correct version is: Java SE 1.6.)
$(info $(space))
$(info Please follow the machine setup instructions at)
$(info $(space)$(space)$(space)$(space)https://source.android.com/source/download.html)
$(info ************************************************************)
$(error stop)
endif
           

如果本機中安裝的jdk版本為1.7,那麼隻需将

java_version := $(shell java -version 2>&1 | head -n 1 | grep '^java .*[ "]1\.6[\. "$$]')

修改為

java_version := $(shell java -version 2>&1 | head -n 1 | grep '^java .*[ "]1\.7[\. "$$]')

即可。

同樣,javac的版本号也需要修改。

# Check for the correct version of javac
javac_version := $(shell javac -version 2>&1 | head -n 1 | grep '[ "]1\.6[\. "$$]')
ifeq ($(strip $(javac_version)),)
$(info ************************************************************)
$(info You are attempting to build with the incorrect version)
$(info of javac.)
$(info $(space))
$(info Your version is: $(shell javac -version 2>&1 | head -n 1).)
$(info The correct version is: 1.6.)
$(info $(space))
$(info Please follow the machine setup instructions at)
$(info $(space)$(space)$(space)$(space)https://source.android.com/source/download.html)
$(info ************************************************************)
$(error stop)
endif
           

javac_version := $(shell javac -version 2>&1 | head -n 1 | grep '[ "]1\.6[\. "$$]')

修改為

javac_version := $(shell javac -version 2>&1 | head -n 1 | grep '[ "]1\.7[\. "$$]')

「編譯中常見問題」

解決了jdk版本的問題後,随着編譯的進行可能會出現其他錯誤(主要為command not found),導緻編譯中止。

如果在沒有修改過的源代碼中編譯,出現錯誤的話,絕大部分是由于系統中缺少必要的軟體造成的。

經過本人親身實踐,編譯中會用到下列軟體:

 bison

 zlib1g-dev

 flex

 libncurses-dev

 gperf

 xsltproc

 build-essential

 g++

 g++-multilib

 ia32-libs

 libxml2-utils

解決辦法就是在編譯之前,提前安裝上述軟體,安裝指令如下:

sudo apt-get install bison
sudo apt-get install zlib1g-dev
sudo apt-get install flex
sudo apt-get install libncurses-dev
sudo apt-get install gperf
sudo apt-get install xsltproc
sudo apt-get install build-essential
sudo apt-get install g++
sudo apt-get install g++-multilib
sudo apt-get install ia32-libs
sudo apt-get install libxml2-utils
           

安裝完上述軟體後,再重新進行編譯,就可以正常編譯了。普通的4核電腦整個編譯過程大約會需要4個小時。

「out檔案夾」

編譯成功後,會在android-4.1.2_r1目錄下生成一個out檔案夾。out裡面的檔案就是編譯後的産物。

下面對out檔案夾的構成做簡單的講解。

out下有兩個檔案夾

 host

 target

其中,target檔案夾中有我們需要的東西。

target下又有兩個檔案夾

common

product

common 檔案夾中用的最多的就是取得編譯生成的java libraries。因為在sdk中所有标記為@hide的方法或變量都是無法通路的,是以當你需要通路他們的時候,就必須使用下面檔案夾内的library。

out/target/common/obj/JAVA_LIBRARIES

product檔案夾,顧名思義,就是編譯後最終生成的檔案的存放位置。

标準Android源代碼中,生成的檔案夾叫generic。廠商定制之後,這個檔案夾的名字會發生變化,會變成手機的型号。

在generic檔案夾内,有兩個地方是比較重要的。

一個是system.img

這是編譯生成的system檔案夾的鏡像檔案,制作ROM必不可少的檔案。

另一個就是system檔案夾

這裡面有我們需要的apk檔案,存放在system/app檔案夾下。

「odex與dex」

需要說明的是,預設情況下sytem/app檔案夾下的apk包括兩個檔案:

Launcher2.apk

Launcher2.odex

這是因為編譯過程中沒有将classes.jar與資源檔案打在同一個檔案中(classes.dex),而是單獨生成了odex檔案。

關閉odex化的方法是

修改android-4.1.2_r1/build/core/package.mk

ifneq (true,$(WITH_DEXPREOPT))
LOCAL_DEX_PREOPT :=
else
ifeq (,$(TARGET_BUILD_APPS))
ifneq (,$(LOCAL_SRC_FILES))
ifndef LOCAL_DEX_PREOPT
#feizl mod start
#LOCAL_DEX_PREOPT := true
LOCAL_DEX_PREOPT := false
#feizl mod end
endif
endif
endif
endif
ifeq (false,$(LOCAL_DEX_PREOPT))
LOCAL_DEX_PREOPT :=
endif
           

LOCAL_DEX_PREOPT := true

修改為

LOCAL_DEX_PREOPT := false

同樣,如果想讓framework也不生成odex檔案的話,就可以修改以下檔案

android-4.1.2_r1/build/core/java_library.mk

ifneq (true,$(WITH_DEXPREOPT))
LOCAL_DEX_PREOPT :=
else
ifeq (,$(TARGET_BUILD_APPS))
ifndef LOCAL_DEX_PREOPT
#feizl mod start
#LOCAL_DEX_PREOPT := true
LOCAL_DEX_PREOPT := false
#feizl mod end
endif
endif
endif
ifeq (false,$(LOCAL_DEX_PREOPT))
LOCAL_DEX_PREOPT :=
endif
           

LOCAL_DEX_PREOPT := true

修改為

LOCAL_DEX_PREOPT := false

修改之後,重新執行編譯指令,但要注意提前删除system/app和system/framework下的檔案。

在編譯完成後,上述檔案夾内的檔案就是classes與資源檔案合二為一了。

以上就是本文的全部内容,希望對大家有所幫助。

其他與編譯相關的内容将在以後推出,敬請期待。