本文的主要内容是解決在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與資源檔案合二為一了。
以上就是本文的全部内容,希望對大家有所幫助。
其他與編譯相關的内容将在以後推出,敬請期待。