官术网_书友最值得收藏!

第3章 獲取并編譯源碼

要想掌握Android智能設備底層開發相關技術,需要先了解Android系統源碼的基本知識。在了解Android系統源碼之前,需要先獲取其具體源碼。因為目前市面上主流的操作系統有Windows、Linux和Mac OS的操作系統,由于Mac OS源自于Linux系統,所以本章講解分別在Windows系統和Linux系統中獲取Android源碼的知識,并講解編譯Android源碼的具體過程,為讀者步入本書后面知識的學習打下基礎。

3.1 在Linux系統獲取Android源碼

在Linux系統中,通常使用Ubuntu來下載和編譯Android源碼。由于Android的源碼內容很多,Google采用了git的版本控制工具,并對不同的模塊設置不同的git服務器,可以用repo自動化腳本來下載Android源碼,下面介紹如何一步一步地獲取Android源碼的過程。

1.下載repo

在用戶目錄下,創建bin文件夾,用于存儲repo,并把該路徑設置到環境變量中去,命令如下:

      $ mkdir ~/bin
      $ PATH=~/bin:$PATH

下載repo的腳本,用于執行repo,命令如下:

      $ curl https://dl-ssl.google.com/dl/googlesource/git-repo/repo > ~/bin/repo

設置可執行權限,命令如下:

      $ chmod a+x ~/bin/repo

2.初始化一個repo的客戶端

在用戶目錄下,創建一個空目錄,用于存儲Android源碼,命令如下:

      $ mkdir AndroidCode
      $ cd AndroidCode

進入AndroidCode目錄,并運行repo下載源碼,下載主線分支的代碼,主線分支包括最新修改的bug以及并未正式發出版本的最新源碼,命令如下:

      $ repo init -u https://android.googlesource.com/platform/manifest

下載其他分支,正式發布的版本,可以通過添加-b參數來下載,命令如下:

      $ repo init -u https://android.googlesource.com/platform/manifest -b
      android-4.4_r1

在下載過程中需要填寫Name和E-mail,填寫完畢之后,選擇Y進行確認,最后提示repo初始化完成,這時可以開始同步Android源碼,同步過程很漫長,需要耐心的等待,執行下面命令開始同步代碼:

      $ repo sync

經過上述步驟后,便開始下載并同步Android源碼,界面效果如圖3-1所示。在Windows平臺獲取Android源碼

▲圖3-1 下載同步

3.2 在Windows平臺獲取Android源碼

Windows平臺上獲取源碼和Linux原理相同,但是需要預先在Windows平臺上面搭建一個Linux環境,此處需要用到cygwin工具。cygwin的作用是構建一套在Windows上的Linux模擬環境,下載cygwin工具的地址如下:

      http://cygwin.com/install.html

下載成功后會得到一個名為“setup.exe”可執行文件,通過此文件可以更新和下載最新的工具版本,具體流程如下所示。

(1)啟動cygwin,如圖3-2所示。

▲圖3-2 啟動cygwin

(2)單擊“下一步”按鈕,選擇第一個選項:從網絡下載安裝,如圖3-3所示。

▲圖3-3 選擇從網絡下載安裝

(3)單擊“下一步”按鈕,選擇安裝根目錄,如圖3-4所示。

▲圖3-4 選擇安裝根目錄

(4)單擊“下一步”按鈕,選擇臨時文件目錄,如圖3-5所示。

▲圖3-5 選擇臨時文件目錄

(5)單擊“下一步”按鈕,設置網絡代理。如果所在網絡需要代理,則在這一步進行設置,如果不用代理,則選擇直接下載,如圖3-6所示。

▲圖3-6 設置網絡代理

(6)單擊“下一步”按鈕,選擇下載站點。一般選擇比較近的站點,速度會比較快,如圖3-7所示。

▲圖3-7 選擇下載站點

(7)單擊“下一步”按鈕,開始更新工具列表,如圖3-8所示。

▲圖3-8 更新工具列表

(8)單擊“下一步”按鈕,選擇需要下載的工具包。在此需要依次下載curl、git和python這些工具。如圖3-9所示。

▲圖3-9 依次下載工具

為了確保能夠安裝上述工具,一定要用鼠標雙擊變為Install形式,如圖3-10所示。

▲圖3-10 務必設置為Install形式

(9)單擊“下一步”按鈕,經過漫長的等待過程,如圖3-11所示。

▲圖3-11 下載進度條

如果下載安裝成功會出現提示信息,單擊“完成”按鈕即完成安裝。當安裝好cygwin后,打開cygwin,會模擬出一個Linux的工作環境,然后按照Linux平臺的源碼下載方法就可以下載Android源碼了。

建議讀者在下載Android源碼時,嚴格按照官方提供的步驟進行,地址是:http://source.android.com/source/downloading.html,這一點對初學者來說尤為重要。另外,整個下載過程比較漫長,需要大家耐心的等待。例如圖3-12所示是下載Android 4.4時的機器命令截圖。

▲圖3-12 在Windows中用cygwin工具下載Android源碼的截圖

3.3 分析Android源碼結構

獲得Android源碼后,可以將源碼的全部工程分為如下3個部分。

· Core Project:核心工程部分,這是建立Android系統的基礎,被保存在根目錄的各個文件夾中。

· External Project:擴展工程部分,可以使其他開源項目具有擴展功能,被保存在“external”文件夾中。

· Package:包部分,提供了Android的應用程序、內容提供者、輸入法和服務,被保存在“package”文件夾中。

無論是Android 1.5還是Android 4.4,各個版本的源碼目錄基本類似。在里面包含了原始Android的目標機代碼、主機編譯工具和仿真環境。解壓縮下載的Android 4.4源碼包后,第一級別目錄結構的具體說明如表3-1所示。

表3-1 Android 4.4源碼的根目錄

3.3.1 應用程序

應用程序主要是UI界面的實現,廣大開發者基于SDK上開發的一個個獨立的APK包,都是屬于應用程序這一層的,應用程序在Android系統中處于最上層的位置。源碼結構中的packages目錄用于實現系統的應用程序,packages的目錄結構如下所示。

      packages /
      ├—— apps //應用程序庫
      |   ├—— BasicSmsReceiver           //基礎短信接收
      |   ├—— Bluetooth                  //藍牙
      |   ├—— Browser                    //瀏覽器
      |   ├—— Calculator                 //計算器
      |   ├—— Calendar                   //日歷
      |   ├—— Camera                     //照相機
      |   ├—— CellBroadcastReceiver      //單元廣播接收
      |   ├—— CertInstaller              //被調用的包,在Android中安裝數字簽名
      |   ├—— Contacts                   //聯系人
      |   ├—— DeskClock                  //桌面時鐘
      |   ├—— Email                      //電子郵件
      |   ├—— Exchange                   //Exchange服務
      |   ├—— Gallery                    //圖庫
      |   ├—— Gallery2                   //圖庫2
      |   ├—— HTMLViewer                 //HTML查看器
      |   ├—— KeyChain                   //密碼管理
      |   ├—— Launcher2                  //啟動器2
      |   ├—— Mms                         //彩信
      |   ├—— Music                      //音樂
      |   ├—— MusicFX                    //音頻增強
      |   ├—— Nfc                         //近場通信
      |   ├—— PackageInstaller           //包安裝器
      |   ├—— Phone                      //電話
      |   ├—— Protips                    //主屏幕提示
      |   ├—— Provision                  //引導設置

      |   ├—— QuickSearchBox             //快速搜索框
      |   ├—— Settings                   //設置
      |   ├—— SoundRecorder              //錄音機
      |   ├—— SpareParts                 //系統設置
      |   ├—— SpeechRecorder             //錄音程序
      |   ├—— Stk                         //sim卡相關
      |   ├—— Tag                         //標簽
      |   ├—— VideoEditor                //視頻編輯
      |   └—— VoiceDialer                //語音編號
      ├—— experimental                    //非官方的應用程序
      |   ├—— BugReportSender            //bug的報告程序
      |   ├—— Bummer
      |   ├—— CameraPreviewTest          //照相機預覽測試車程序
      |   ├—— DreamTheater
      |   ├—— ExampleImsFramework
      |   ├—— LoaderApp
      |   ├—— NotificationLog
      |   ├—— NotificationShowcase
      |   ├—— procstatlog
      |   ├—— RpcPerformance
      |   └—— StrictModeTest
      ├—— inputmethods                    //輸入法
      |   ├—— LatinIME                   //拉丁文輸入法
      |   ├—— OpenWnn                    //OpenWnn輸入法
      |   └—— PinyinIME                  //拼音輸入法
      ├—— providers                       //提供器
      |   ├—— ApplicationsProvider       //應用程序提供器,提供應用程序所需要的界面
      |   ├—— CalendarProvider           //日歷提供器
      |   ├—— ContactsProvider           //聯系人提供器
      |   ├—— DownloadProvider           //下載管理提供器
      |   ├—— DrmProvider                //數據庫相關
      |   ├—— GoogleContactsProvider     //Google聯系人提供器
      |   ├—— MediaProvider              //媒體提供器
      |   ├—— TelephonyProvider          //彩信提供器
      |   └—— UserDictionaryProvider     //用戶字典提供器
      ├—— screensavers                    //屏幕保護
      |   ├—— Basic                      //基本屏幕保護
      |   ├—— PhotoTable                 //照片方格
      |   ├—— WebView                    //網頁
      └—— wallpapers                      //墻紙
      ├—— Basic                           //系統內置墻紙
      ├—— Galaxy4                         //S4內置墻紙
      ├—— HoloSpiral                      //手槍皮套墻紙
      ├—— LivePicker
      ├—— MagicSmoke
      ├—— MusicVisualization
      ├—— NoiseField
      └—— PhaseBeam

通過上面的目錄結構可以看出,package目錄主要存儲的是Android系統應用層相關的內容,包括應用程序相關的包或者資源文件,其中既包括系統自帶的應用程序,又有第三方開發的應用程序,還有屏幕保護和墻紙等應用,所以源碼中package目錄對應著系統的應用層。

3.3.2 應用程序框架

應用程序框架是Android系統中的核心部分,也就是SDK部分,它會提供接口給應用程序使用,同時應用程序框架又會和系統服務、系統程序庫、硬件抽象層有關聯,所以其作用十分重要,應用程序框架的實現代碼大部分都在/frameworks/base和/frameworks/av目錄下,/frameworks/base的目錄結構如下所示。

      frameworks/base
      ├—— api             //全是XML文件,定義了API
      ├—— cmds            //Android中的重要命令(am、app_proce等)
      ├—— core            //核心庫
      ├—— data            //聲音字體等數據文件

      ├—— docs            //文檔
      ├—— drm             //數字版權管理
      ├—— graphics       //圖形圖像
      ├—— icu4j           //用于解決國際化問題
      ├—— include        //頭文件
      ├—— keystore       //數字簽名證書相關
      ├—— libs            //庫
      ├—— location       //地理位置
      ├—— media           //多媒體
      ├—— native         //本地庫
      ├—— nfc-extras     //NFC相關
      ├—— obex            //藍牙傳輸
      ├—— opengl         //opengl相關
      ├—— packages       //設置、TTS、VPN程序
      ├—— policy         //鎖屏界面相關
      ├—— sax             //XML解析器
      ├—— services       //Android的服務
      ├—— telephony      //電話相關
      ├—— test-runner    //測試相關
      ├—— tests           //測試相關
      ├—— tools           //工具
      ├—— voip            //可視通話
      └—— wifi            //無線網絡

以上這些文件夾包含了應用程序框架層的大部分代碼,正是這些目錄下的文件構成了Android的應用程序框架層,提供接口給應用程序調用,同時銜接系統程序庫和硬件抽象層,形成一個由上至下的調用過程。在/frameworks/base目錄下也涉及系統服務、程序庫中的一些代碼,將在后面的兩個小節中會詳細分析。

3.3.3 系統服務

在3.3.2中介紹了應用程序框架層的內容,了解到大部分的實現代碼保存在“/frameworks/base”目錄下。其實在這個目錄中還有一個名為“service”的目錄,里面的代碼是用于實現Android系統服務的。接下來將詳細介紹service目錄下的內容,其目錄結構如下所示。

      frameworks/base/services
      ├—— common_time          //時間日期相關的服務
      ├—— input                //輸入系統服務
      ├—— java                 //其他重要服務的Java層
      ├—— jni                  //其他重要服務的JNI層
      └—— tests                //測試相關

其中java和jni兩個目錄分別是一些其他的服務的Java層和JNI層實現,java目錄下更詳細的目錄結構以及其他Android系統服務的說明如下所示。

      frameworks/base/services/java/com/android/server
      ├—— accessibility
      ├—— am
      ├—— connectivity
      ├—— display
      ├—— dreams
      ├—— drm
      ├—— input
      ├—— location
      ├—— net
      ├—— pm
      ├—— power
      ├—— updates
      ├—— usb
      ├—— wm
      ├—— AlarmManagerService.java            //鬧鐘服務
      ├—— AppWidgetService.java                //應用程序小工具服務
      ├—— AppWidgetServiceImpl.java
      ├—— AttributeCache.java
      ├—— BackupManagerService.java           //備份服務

      ├—— BatteryService.java                  //電池相關服務
      ├—— BluetoothManagerService.java        //藍牙
      ├—— BootReceiver.java
      ├—— BrickReceiver.java
      ├—— CertBlacklister.java
      ├—— ClipboardService.java
      ├—— CommonTimeManagementService.java    //時間管理服務
      ├—— ConnectivityService.java
      ├—— CountryDetectorService.java
      ├—— DevicePolicyManagerService.java
      ├—— DeviceStorageMonitorService.java    //設備存儲器監聽服務
      ├—— DiskStatsService.java                //磁盤狀態服務
      ├—— DockObserver.java                    //底座監視服務
      ├—— DropBoxManagerService.java
      ├—— EntropyMixer.java
      ├—— EventLogTags.logtags
      ├—— INativeDaemonConnectorCallbacks.java
      ├—— InputMethodManagerService.java      //輸入法管理服務
      ├—— IntentResolver.java
      ├—— IntentResolverOld.java
      ├—— LightsService.java
      ├—— LocationManagerService.java         //地理位置服務
      ├—— MasterClearReceiver.java
      ├—— MountService.java                    //掛載服務
      ├—— NativeDaemonConnector.java
      ├—— NativeDaemonConnectorException.java
      ├—— NativeDaemonEvent.java
      ├—— NetworkManagementService.java       //網絡管理服務
      ├—— NetworkTimeUpdateService.java
      ├—— NotificationManagerService.java     //通知服務
      ├—— NsdService.java
      ├—— PackageManagerBackupAgent.java
      ├—— PreferredComponent.java
      ├—— ProcessMap.java
      ├—— RandomBlock.java
      ├—— RecognitionManagerService.java
      ├—— SamplingProfilerService.java
      ├—— SerialService.java                   //NFC相關
      ├—— ServiceWatcher.java
      ├—— ShutdownActivity.java
      ├—— StatusBarManagerService.java        //狀態欄管理服務
      ├—— SystemBackupAgent.java
      ├—— SystemServer.java
      ├—— TelephonyRegistry.java
      ├—— TextServicesManagerService.java
      ├—— ThrottleService.java
      ├—— TwilightCalculator.java
      ├—— TwilightService.java
      ├—— UiModeManagerService.java
      ├—— UpdateLockService.java               //鎖屏更新服務
      ├—— VibratorService.java                 //震動服務
      ├—— WallpaperManagerService.java        //壁紙服務
      ├—— Watchdog.java                        //看門狗
      ├—— WifiService.java                     //無線網絡服務
      └—— WiredAccessoryManager.java          //無線設備管理服務

從上面的文件夾和文件可以看出,Android中涉及的服務種類非常多,包括界面、網絡、電話等核心模塊基本上都有其專屬的服務,這些是屬于系統級別的服務,這些系統服務一般都會在Android系統啟動的時候加載,在系統關閉的時候結束,受到系統的管理,應用程序并沒有權力去打開或者關閉,它們會隨著系統的運行一直在后臺運行,供應用程序和其他的組件來使用。

另外在frameworks/av/下面也有一個services目錄,這個目錄下存儲的是音頻和照相機的服務的實現代碼,目錄結構如下所示。

      frameworks/av/services
     ├—— audioflinger         //音頻管理服務
     └—— camera               //照相機的管理服務

這個av/services目錄下的文件主要是用于支持Android系統中的音頻和照相機服務的,這是兩個非常重要的系統服務,在開發應用程序時會經常依賴這兩個服務的。

3.3.4 系統程序庫

Android的系統程序庫類型非常多,功能也非常強大,正是有了這些程序庫,Android系統才能運行多種多樣的應用程序。在接下來的內容中,本書選擇了一些很常用的也是很重要的系統程序庫來分析它們在源碼中所處的位置。

1.系統C庫

Android系統采用的是一個從BSD繼承而來的標準的系統函數庫bionic,在源碼根目錄下有這個文件夾,其目錄結構如下所示。

      bionic/
      ├—— libc            //C庫
      ├—— libdl           //動態鏈接庫相關
      ├—— libm            //數學庫
      ├—— libstdc++      //C++實現庫
      ├—— libthread_db   //線程庫
      ├—— linker         //連接器相關
      └—— test            //測試相關

2.媒體庫

Android中的媒體庫在2.3之前是由OpenCore實現的,2.3之后Stragefright被替換了,OpenCore成為了新的多媒體的實現庫。同時Android也自帶了一些音視頻的管理庫,用于管理多媒體的錄制、播放、編碼和解碼等功能。Android的多媒體程序庫的實現代碼主要在/frameworks/av/media目錄下,其目錄結構如下所示。

      frameworks/av/media/
      ├—— common_time               //時間相關
      ├—— libeffects                //多媒體效果
      ├—— libmedia                  //多媒體錄制、播放
      ├—— libmedia_native           //里面只有一個Android.mk,用來編譯native文件
      ├—— libmediaplayerservice     //多媒體播放服務的實現庫
      ├—— libstagefright            //stagefright的實現庫
      ├—— mediaserver               //跨進程多媒體服務
      └—— mtp                       //mtp協議的實現(媒體傳輸協議)

3.圖層顯示庫

Android中的圖層顯示庫主要負責對顯示子系統的管理,負責圖層的渲染、疊加、繪制等功能,提供了2D和3D圖層的無縫融合,是整個Android系統顯示的“大腦中樞”,其代碼在/frameworks/native/services/surfaceflinger/目錄下,其目錄結構如下所示。

      frameworks/native/services/surfaceflinger/
      ├—— DisplayHardware           //顯示底層相關
      ├—— tests                     //測試
      ├—— Android.mk                //MakeFile文件
      ├—— Barrier.h
      ├—— Client.cpp                //顯示的客戶端實現文件
      ├—— Client.h
      ├—— clz.cpp
      ├—— clz.h
      ├—— DdmConnection.cpp
      ├—— DdmConnection.h
      ├—— DisplayDevice.cpp         //顯示設備相關
      ├—— DisplayDevice.h
      ├—— EventThread.cpp           //消息線程

      ├—— EventThread.h
      ├—— GLExtensions.cpp          //opengl擴展
      ├—— GLExtensions.h
      ├—— Layer.cpp                 //圖層相關
      ├—— Layer.h
      ├—— LayerBase.cpp             //圖層基類
      ├—— LayerBase.h
      ├—— LayerDim.cpp              //圖層相關
      ├—— LayerDim.h
      ├—— LayerScreenshot.cpp       //圖層相關
      ├—— LayerScreenshot.h
      ├—— MessageQueue.cpp          //消息隊列
      ├—— GLExtensions.h
      ├—— MessageQueue.h
      ├—— MODULE_LICENSE_APACHE2    //證書
      ├—— SurfaceFlinger.cpp        //圖層管理者,圖層管理的核心類
      ├—— SurfaceFlinger.h
      ├—— SurfaceTextureLayer.cpp   //文字圖層
      ├—— SurfaceTextureLayer.h
      ├—— Transform.cpp
      └—— Transform.h

4.網絡引擎庫

網絡引擎庫主要是用來實現Web瀏覽器的引擎,支持Android的Web瀏覽器和一個可嵌入的Web視圖,這個是采用第三方開發的瀏覽器引擎Webkit實現的,Webkit的代碼在/external/webkit/目錄下,其目錄結構如下所示。

      external/webkit/
      ├—— Examples                  //Webkit例子
      ├—— LayoutTests               //布局測試
      ├—— PerformanceTests          //表現測試
      ├—— Source                    //Webkit源代碼
      ├—— Tools                     //工具
      ├—— WebKitLibraries           //Webkit用到的庫
      ├—— Android.mk                //Makefile
      ├—— bison_check.mk
      ├—— CleanSpec.mk
      ├—— MODULE_LICENSE_LGPL       //證書
      ├—— NOTICE
      └—— WEBKIT_MERGE_REVISION     //版本信息

5.3D圖形庫

Android中的3D圖形渲染是采用Opengl來實現的,Opengl是開源的第三方圖形渲染庫,使用該庫可以實現Android中的3D圖形硬件加速或者3D圖形軟件加速功能,是一個非常重要的功能庫。從Android 4.3開始,支持最新、最強大的OpenGL ES 3.0。其實現代碼在/frameworks/native/opengl中,其目錄結構如下所示。

      frameworks/native/opengl/
      ├—— include                   //Opengl中的頭文件
      ├—— libagl                    //在mac os上的庫
      ├—— libs                      //Opengl的接口和實現庫
      ├—— specs                     //Opengl的文檔
      ├—— tests                     //測試相關
      └—— tools                     //工具庫

6.SQLite

SQLite是Android系統自帶的一個輕量級關系數據庫,其實現源代碼已經在網上開源。SQLite的具有操作簡單方便、運行速度較快和占用資源較少等特點,比較適合在嵌入式設備上面使用。SQLite是Android系統自帶的實現數據庫功能的核心庫,其代碼實現分為Java和C兩個部分,Java部分的代碼在/frameworks/base/core/java/android/database,目錄結構如下所示。

      frameworks/base/core/java/android/database/
      ├—— sqlite                                     //SQLite的框架文件
      ├—— AbstractCursor.java                       //游標的抽象類
      ├—— AbstractWindowedCursor.java
      ├—— BulkCursorDescriptor.java
      ├—— BulkCursorNative.java
      ├—— BulkCursorToCursorAdaptor.java            //游標適配器
      ├—— CharArrayBuffer.java
      ├—— ContentObservable.java
      ├—— ContentObserver.java                      //內容觀察者
      ├—— CrossProcessCursor.java
      ├—— CrossProcessCursorWrapper.java            //CrossProcessCursor的封裝類
      ├—— Cursor.java                               //游標實現類
      ├—— CursorIndexOutOfBoundsException.java     //游標出界異常
      ├—— CursorJoiner.java
      ├—— CursorToBulkCursorAdaptor.java            //適配器
      ├—— CursorWindow.java                         //游標窗口
      ├—— CursorWindowAllocationException.java     //游標窗口異常
      ├—— CursorWrapper.java                        //游標封裝類
      ├—— DatabaseErrorHandler.java                 //數據庫錯誤句柄
      ├—— DatabaseUtils.java                        //數據庫工具類
      ├—— DataSetObservable.java
      ├—— DataSetObserver.java
      ├—— DefaultDatabaseErrorHandler.java         //默認數據庫錯誤句柄
      ├—— IBulkCursor.java
      ├—— IContentObserver.aidl                     //aidl用于跨進程通信
      ├—— MatrixCursor.java
      ├—— MergeCursor.java
      ├—— Observable.java
      ├—— package.html
      ├—— SQLException.java                         //數據庫異常
      └—— StaleDataException.java

Java層的代碼主要是實現SQLite的框架和接口,方便用戶開發應用程序的時候能很簡單地操作數據庫,并且捕獲數據庫異常。

C++層的代碼在/external/sqlite路徑下,其目錄結構如下所示。

      external/sqlite/
      ├—— android        //Android數據庫的一些工具包
      └—— dist            //Android數據庫底層實現

從上面Java和C部分的代碼目錄結構可以看出,SQLite在Android中還是有很重要的地位的,并且在SDK中會有開放的接口讓應用程序可以很簡單方便地操作數據庫,例如對數據進行存儲和刪除。

3.3.5 系統運行庫

眾所周知,Android系統的應用層是采用Java開發的,由于Java語言的跨平臺特性,Java代碼必須運行在虛擬機中。正是因為這個特性,Android系統自己也實現了一個類似JVM但是更適用于嵌入式平臺的Java虛擬機,這被稱為dalvik。

dalvik功能等同于JVM,為Android平臺上的Java代碼提供了運行環境,dalvik本身是由C++語言實現的,在源碼中根目錄下有dalvik文件夾,里面存儲的是dalvik虛擬機的實現代碼,其目錄結構如下所示。

      ./
      ├—— dalvikvm                        //入口目錄
      ├—— dexdump                         //dex反匯編
      ├—— dexgen                          //dex生成相關
      ├—— dexlist                         //dex列表
      ├—— dexopt                          //與驗證和優化
      ├—— docs                            //文檔
      ├—— dvz                             //zygot相關
      ├—— dx                              //dx工具,將多個Java轉換為dex

      ├—— hit
      ├—— libdex                          //dex庫的實現代碼
      ├—— opcode-gen
      ├—— tests                           //測試相關
      ├—— tools                           //工具
      ├—— unit-tests                      //測試相關
      ├—— vm                              //虛擬機的實現
      ├—— Android.mk                      //Makefile
      ├—— CleanSpec.mk
      ├—— MODULE_LICENSE_APACHE2
      ├—— NOTICE
      └—— README.txt

正是有上面這些代碼實現的Android虛擬機,所以應用程序生成的二進制執行文件能夠快速、穩定地運行在Android系統上。

3.3.6 硬件抽象層

Android的硬件抽象是各種功能的底層實現,理論上不同的硬件平臺會有不同的硬件抽象層實現,這一個層次也是與驅動層和硬件層有緊密的聯系的,起著承上啟下的作用,對上要實現應用程序框架層的接口,對下要實現一些硬件基本功能以及調用驅動層的接口。需要注意的是,這一層也是廣大OEM廠商改動最大的一層,因為這一層的代碼跟終端采用什么樣硬件的硬件平臺有很大關系。源碼中存儲的是硬件抽象層框架的實現代碼和一些平臺無關性的接口的實現。硬件抽象層代碼存儲在源碼根目錄下的hardware文件夾中,其目錄結構如下所示。

      hardware/
      ├—— libhardware               //新機制硬件庫
      ├—— libhardware_legacy        //舊機制硬件庫
      └—— ril                       //ril模塊相關的底層實現

從上面的目錄結構可以看出,硬件抽象層中主要是實現了一些底層的硬件庫,用來實現應用層框架層中的功能,具體硬件庫中有哪些內容,可以繼續細分其目錄結構,例如libhardware目錄下的結構如下。

      hardware/libhardware/
      ├—— include                   //入口目錄
      ├—— modules                   //dex反匯編
      |   ├—— audio                 //音頻相關底層庫
      |   ├—— audio_remote_submix  //音頻混合相關
      |   ├—— gralloc               //幀緩沖
      |   ├—— hwcomposer            //音頻相關
      |   ├—— local_time            //本地時間
      |   ├—— nfc                   //nfc功能
      |   ├—— nfc-nci               //nfc的接口
      |   ├—— power                 //電源
      |   ├—— usbaudio              //USB音頻設備
      |   ├—— Android.mk            //Makefile
      |   ├—— README.android
      ├—— tests                     //dex生成相關
      ├—— dexlist                   //dex列表
      ├—— dexopt                    //與驗證和優化
      └—— docs                      //文檔

從上面的目錄結構可以分析出,libhardware目錄主要是Android系統的某些功能的底層實現,包括audio、nfc和power。

libhardware_legacy的目錄與libhardware大同小異,只是針對舊的實現方式做的一套硬件庫,其目錄下還有uevent、wifi以及虛擬機的底層實現。這兩個目錄下的代碼一般會由設備廠家根據自身的硬件平臺來實現符合Android機制的硬件庫。

ril目錄下存儲的是無線硬件設備與電話的實現,其目錄結構如下所示。

      hardware/ril/

      ├—— include                   //頭文件
      ├—— libril                    //libril庫
      ├—— mock-ril
      ├—— reference-ril             //reference ril庫
      ├—— rild                      //ril守護進程
      └—— CleanSpec.mk

3.4 編譯源碼

編譯Android源碼的方法非常簡單,只需要使用Android源碼根目錄下的Makefile,執行make命令即可輕松實現。當然在編譯Android源碼之前,首先要確定已經完成同步工作。進入Android源碼目錄使用make命令進行編譯,使用此命令的格式如下所示。

      $: cd ~/Android4.3(這里的“Android4.3”就是下載源碼的保存目錄)
      $: make

編譯Android源碼可以得到“~/project/android/cupcake/out”目錄。

整個編譯過程是非常漫長的,需要讀者耐心等待。

3.4.1 搭建編譯環境

在編譯Android源碼之前,需要先進行環境搭建工作。在接下來的內容中,以Ubuntu系統為例講解搭建編譯環境以及編譯Android源碼的方法。具體流程如下所示。

(1)安裝JDK,編譯Android 4.3的源碼需要JDK1.6,下載jdk-6u23-linux-i586.bin后進行安裝,對應命令如下:

      $ cd /usr
      $ mkdir java
      $ cd java
      $ sudo cp jdk-6u23-linux-i586.bin所在目錄 ./
      $ sudo chmod 755 jdk-6u23-linux-i586.bin
      $ sudo sh jdk-6u23-linux-i586.bin

(2)設置JDK環境變量,將如下環境變量添加到主文件夾目錄下的.bashrc文件中,然后用source命令使其生效,加入的環境變量代碼如下:

      export JAVA_HOME=/usr/java/jdk1.6.0_23
      export JRE_HOME=$JAVA_HOME/jre
      export CLASSPATH=.:$JAVA_HOME/lib:$JRE_HOME/lib:$CLASSPATH
      export PATH=$PATH:$JAVA_HOME/bin:$JAVA_HOME/bin/tools.jar:$JRE_HOME/bin
      export ANDROID_JAVA_HOME=$JAVA_HOME

(3)安裝需要的包,讀者可以根據編譯過程中的提示進行選擇,可能需要的包的安裝命令如下:

      $ sudo apt-get install git-core bison zlib1g-dev flex libx13-dev gperf sudo aptitude
      install  git-core  gnupg  flex  bison  gperf  libsdl-dev  libesd0-dev  libwxgtk2.6-dev
      build-essential zip curl libncurses5-dev zlib1g-dev

3.4.2 開始編譯

當所依賴的包安裝完成之后,就可以開始編譯Android源碼了,具體步驟如下所示。

(1)首先進行編譯初始化工作,在終端中執行下面的命令:

      source build/envsetup.sh

或:

      . build/envsetup.sh

執行后將會輸出:

      source build/envsetup.sh

      including device/asus/grouper/vendorsetup.sh
      including device/asus/tilapia/vendorsetup.sh
      including device/generic/armv7-a-neon/vendorsetup.sh
      including device/generic/armv7-a/vendorsetup.sh
      including device/generic/mips/vendorsetup.sh
      including device/generic/x86/vendorsetup.sh
      including device/samsung/maguro/vendorsetup.sh
      including device/samsung/manta/vendorsetup.sh
      including device/samsung/toroplus/vendorsetup.sh
      including device/samsung/toro/vendorsetup.sh
      including device/ti/panda/vendorsetup.sh
      including sdk/bash_completion/adb.bash

(2)然后選擇編譯目標,命令是:

      lunch full-eng

執行后會輸出如下所示的提示信息:

      ============================================
      PLATFORM_VERSION_CODENAME=REL
      PLATFORM_VERSION=4.3
      TARGET_PRODUCT=full
      TARGET_BUILD_VARIANT=eng
      TARGET_BUILD_TYPE=release
      TARGET_BUILD_APPS=
      TARGET_ARCH=arm
      TARGET_ARCH_VARIANT=armv7-a
      HOST_ARCH=x86
      HOST_OS=linux
      HOST_OS_EXTRA=Linux-2.6.33-45-generic-x86_64-with-Ubuntu-10.04-lucid
      HOST_BUILD_TYPE=release
      BUILD_ID=JOP40C
      OUT_DIR=out
      ============================================

(3)接下來開始編譯代碼,在終端中執行下面的命令:

      make -j4

其中“-j4”表示用4個線程進行編譯。整個編譯進度根據不同機器的配置而需要不同的時間。例如這里計算機為intel i5-2300四核2.8 GHz、4 GB內存,經過近4小時才編譯完成。當出現下面的信息時表示編譯完成:

      target  Java:  ContactsTests  (out/target/common/obj/APPS/ContactsTests_intermediates/
      classes)
      target Dex: Contacts
      Done!
      Install: out/target/product/generic/system/app/Browser.odex
      Install: out/target/product/generic/system/app/Browser.apk
      Note: Some input files use or override a deprecated API.
      Note: Recompile with -Xlint:deprecation for details.
      Copying: out/target/common/obj/APPS/Contacts_intermediates/noproguard.classes.dex
      target Package: Contacts (out/target/product/generic/obj/APPS/Contacts_intermediates/
      package.apk)
       'out/target/common/obj/APPS/Contacts_intermediates/classes.dex' as 'classes.dex'...
      Processing target/product/generic/obj/APPS/Contacts_intermediates/package.apk
      Done!
      Install: out/target/product/generic/system/app/Contacts.odex
      Install: out/target/product/generic/system/app/Contacts.apk
      build/tools/generate-notice-files.py  out/target/product/generic/obj/NOTICE.txt  out/
      target/product/generic/obj/NOTICE.html "Notices for files contained in the filesystem
      images in this directory:" out/target/product/generic/obj/NOTICE_FILES/src
      Combining NOTICE files into HTML
      Combining NOTICE files into text
      Installed file list: out/target/product/generic/installed-files.txt
      Target   system   fs   image:   out/target/product/generic/obj/PACKAGING/systemimage_
      intermediates/system.img
      Running: mkyaffs2image -f out/target/product/generic/system out/target/product/generic/
      obj/PACKAGING/systemimage_intermediates/system.img

        Install system fs image: out/target/product/generic/system.img
        DroidDoc took 5331 sec. to write docs to out/target/common/docs/doc-comment-check

3.4.3 在模擬器中運行

之后在模擬器中運行的步驟就比較簡單了,只需要在終端中執行下面的命令即可:

        emulator

運行成功后的效果如圖3-13所示。

▲圖3-13 在模擬器中的編譯執行效果

3.4.4 常見的錯誤分析

雖然編譯方法非常簡單,但是作為初學者來說很容易出錯,在下面列出了其中常見的編譯錯誤類型。

1.缺少必要的軟件

進入到Android目錄下,使用make命令編譯,可能會出現如下錯誤提示。

        host C: libneo_cgi <= external/clearsilver/cgi/cgi.c
        external/clearsilver/cgi/cgi.c:22:18: error: zlib.h: No such file or directory

上述錯誤是因為缺少zlib1g-dev,需要使用apt-get命令從軟件倉庫中安裝zlib1g-dev,具體命令如下所示。

        sudo apt-get install zlib1g-dev

同理需要安裝下面的軟件,否則也會出現上述類似的錯誤。

        sudo apt-get install flex
        sudo apt-get install bison
        sudo apt-get install gperf
        sudo apt-get install libsdl-dev
        sudo apt-get install libesd0-dev
        sudo apt-get install libncurses5-dev
        sudo apt-get install libx13-dev

2.沒有安裝Java環境JDK

當安裝所有上述軟件后,運行make命令再次編譯Android源碼。如果在之前忘記安裝Java環境JDK,則此時會出現很多Java文件無法編譯的錯誤,如果打開Android的源碼,可以看到在下面目錄中發現有很多Java源文件。

        android/dalvik/libcore/dom/src/test/java/org/w3c/domts

這充分說明在編譯Android之前必須先安裝Java環境JDK,安裝流程如下所示。

· 從Oracle官方網站下載jdk-6u16-linux-i586.bin文件,然后安裝。

在Ubuntu 8.04中,“/etc/profile”文件是全局的環境變量配置文件,它適用于所有的shell。在登錄Linux系統時應該先啟動“/etc/profile”文件,然后再啟動用戶目錄下的“~/.bash_profile”“~/.bash_login”或“~/.profile”文件中的其中一個,執行的順序和上面的排序一樣。如果“~/.bash_profile”文件存在話,則還會執行“~/.bashrc”文件。在此只需要把JDK的目錄存儲到“/etc/profile”目錄下即可。

        JAVA_HOME=/usr/local/src/jdk1.6.0_16
        PATH=$PATH:$JAVA_HOME/bin:/usr/local/src/android-sdk-linux_x86-1.1_r1/tools:~/bin

· 重新啟動機器,輸入java -version命令,輸出下面的信息則表示配置成功。

        ava version "1.6.0_16"
        Java(TM) SE Runtime Environment (build 1.6.0_16-b01)
        Java HotSpot(TM) Client VM (build 13.3-b01, mixed mode, sharing)

當成功編譯Android源碼后,在終端會輸出如下提示。

        Target system fs image: out/target/product/generic/obj/PACKAGING/systemimage_unopt_
        intermediates/system.img
        Install system fs image: out/target/product/generic/system.img
        Target ram disk: out/target/product/generic/ramdisk.img
        Target userdata fs image: out/target/product/generic/userdata.img
        Installed file list: out/target/product/generic/installed-files.txt
        root@dfsun2009-desktop:/bin/android#

3.4.5 實踐演練——演示兩種編譯Android程序的方法

Android編譯環境本身比較復雜,并且不像普通的編譯環境那樣只有頂層目錄下才有Makefile文件,而其他的每個component都使用統一標準的Android.mk文件。不過這并不是人們熟悉的Makefile,而是經過Android自身編譯系統的很多處理。所以說要真正理清楚其中的聯系還比較復雜,不過這種方式的好處在于,編寫一個新的Android.mk給Android增加一個新的component會變得比較簡單。為了使讀者更加深入地理解在Linux環境下編譯Android程序的方法,在接下來的內容中,將分別演示兩種編譯Android程序的方法。

1.編譯Native C(本地C程序)的helloworld模塊

編譯Java程序可以直接采用Eclipse的集成環境來完成,實現方法非常簡單,在這里就不再重復了。接下來將主要針對C/C++進行說明,通過一個例子來講解在Android中增加一個C程序的Hello World的方法。

(1)在“$(YOUR_ANDROID)/development”目錄下創建一個名為“hello”的目錄,并用“$(YOUR_ANDROID)”指向Android源代碼所在的目錄。

        - # mkdir $(YOUR_ANDROID)/development/hello

(2)在目錄“$(YOUR_ANDROID)/development/hello/”下編寫一個名為“hello.c”的C語言文件,文件hello.c的實現代碼如下所示。

        #include <stdio.h>
        int main()
        {
            printf("Hello World! \n"); //輸出Hello World
        return 0;
        }

(3)在目錄“$(YOUR_ANDROID)/development/hello/”下編寫Android.mk文件。這是Android Makefile的標準命名,不能更改。文件Android.mk的格式和內容可以參考其他已有的Android.mk文件的寫法,針對helloworld程序的Android.mk文件內容如下所示。

        LOCAL_PATH:= $(call my-dir)
        include $(CLEAR_VARS)
        LOCAL_SRC_FILES:= \
            hello.c
        LOCAL_MODULE := helloworld
        include $(BUILD_EXECUTABLE)

上述各個內容的具體說明如下所示。

· LOCAL_SRC_FILES:用來指定源文件用;

· LOCAL_MODULE:指定要編譯的模塊的名字,在下一步驟編譯時將會用到;

· include $(BUILD_EXECUTABLE):表示要編譯成一個可執行文件,如果想編譯成動態庫則可用BUILD_SHARED_LIBRARY,這些具體用法可以在“$(YOUR_ANDROID)/build/core/config.mk”查到。(4)回到Android源代碼頂層目錄進行編譯。

      # cd $(YOUR_ANDROID) && make helloworld

在此需要注意,make helloworld中的目標名helloworld就是上面Android.mk文件中由LOCAL_MODULE指定的模塊名。最終的編譯結果如下所示。

      target thumb C: helloworld <= development/hello/hello.c
      target Executable: helloworld (out/target/product/generic/obj/EXECUTABLES/helloworld_
      intermediates/LINKED/helloworld)
      target   Non-prelinked:   helloworld   (out/target/product/generic/symbols/system/bin/
      helloworld)
      target   Strip:   helloworld   (out/target/product/generic/obj/EXECUTABLES/helloworld_
      intermediates/helloworld)
      Install: out/target/product/generic/system/bin/helloworld

(5)如果和上述編譯結果相同,則編譯后的可執行文件存儲在如下目錄:

      out/target/product/generic/system/bin/helloworld

這樣通過”adb push”將它傳送到模擬器上,再通過”adb shell”登錄到模擬器終端后就可以執行了。

2.手工編譯C模塊

在前面講解了通過標準的Android.mk文件來編譯C模塊的具體流程,其實可以直接運用gcc命令行來編譯C程序,這樣可以更好地了解Android編譯環境的細節。具體流程如下所示。

(1)在Android編譯環境中,提供了”showcommands”選項來顯示編譯命令行,可以通過打開這個選項來查看一些編譯時的細節。

(2)在具體操作之前需要使用如下命令把前面中的helloworld模塊清除。

      # make clean-helloworld

上面的“make clean-$(LOCAL_MODULE)”命令是Android編譯環境提供的make clean的方式。

(3)使用showcommands選項重新編譯helloworld,具體命令如下所示。

      # make helloworld showcommands
      build/core/product_config.mk:229: WARNING: adding test OTA key
      target thumb C: helloworld <= development/hello/hello.c
      prebuilt/linux-x86/toolchain/arm-eabi-4.3.1/bin/arm-eabi-gcc  -I system/core/include
      -I hardware/libhardware/include   -I hardware/ril/include   -I dalvik/libnativehelper/
      include  -I frameworks/base/include   -I external/skia/include   -I out/target/product/
      generic/obj/include   -I bionic/libc/arch-arm/include   -I bionic/libc/include   -I
      bionic/libstdc++/include   -I bionic/libc/kernel/common   -I bionic/libc/kernel/arch-
      arm   -I bionic/libm/include   -I bionic/libm/include/arch/arm   -I bionic/libthread_
      db/include    -I  development/hello    -I  out/target/product/generic/obj/EXECUTABLES/
      helloworld_intermediates  -c  -fno-exceptions -Wno-multichar -march=armv5te -mtune=
      xscale  -msoft-float  -fpic  -mthumb-interwork  -ffunction-sections  -funwind-tables
      -fstack-protector -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_
      5TE__ -include system/core/include/arch/linux-arm/AndroidConfig.h -DANDROID -fmessage-
      length=0-W -Wall -Wno-unused -DSK_RELEASE -DNDEBUG -O2-g -Wstrict-aliasing=2-finline-
      functions -fno-inline-functions-called-once -fgcse-after-reload -frerun-cse-after-loop
      -frename-registers  -DNDEBUG  -UDEBUG  -mthumb  -Os  -fomit-frame-pointer  -fno-strict-
      aliasing  -finline-limit=64     -MD  -o  out/target/product/generic/obj/EXECUTABLES/
      helloworld_intermediates/hello.o development/hello/hello.c

      target Executable: helloworld (out/target/product/generic/obj/EXECUTABLES/helloworld_
      intermediates/LINKED/helloworld)

      prebuilt/linux-x86/toolchain/arm-eabi-4.3.1/bin/arm-eabi-g++ -nostdlib -Bdynamic -Wl,
      -T, build/core/armelf.x -Wl, -dynamic-linker, /system/bin/linker -Wl, --gc-sections -Wl, -z,
      nocopyreloc -o out/target/product/generic/obj/EXECUTABLES/helloworld_ intermediates/
      LINKED/helloworld  -Lout/target/product/generic/obj/lib  -Wl, -rpath-link=  out/target/
      product/generic/obj/lib-lc-lstdc++-lm  out/target/product/generic/obj/lib/crtbegin_
      dynamic.o out/target/product/generic/obj/EXECUTABLES/helloworld_ intermediates/hello.o

      -Wl, --no-undefined  prebuilt/linux-x86/toolchain/arm-eabi-4.3.1/  bin/../lib/gcc/arm-
      eabi/4.3.1/interwork/libgcc.a out/target/product/generic/obj/lib/crtend_android.o

      target Non-prelinked: helloworld (out/target/product/generic/symbols/system/bin/
      helloworld)

      out/host/linux-x86/bin/acp -fpt out/target/product/generic/obj/EXECUTABLES/
      helloworld_intermediates/LINKED/helloworld out/target/product/generic/symbols/system/
      bin/helloworld

      target Strip: helloworld (out/target/product/generic/obj/EXECUTABLES/helloworld_
      intermediates/helloworld)

      out/host/linux-x86/bin/soslim --strip --shady --quiet out/target/product/generic/
      symbols/system/bin/helloworld --outfile out/target/product/generic/obj/EXECUTABLES/
      helloworld_intermediates/helloworld

      Install: out/target/product/generic/system/bin/helloworld

      out/host/linux-x86/bin/acp -fpt out/target/product/generic/obj/EXECUTABLES/
      helloworld_intermediates/helloworld out/target/product/generic/system/bin/helloworld

從上述命令行可以看到,Android編譯環境所用的交叉編譯工具鏈如下所示。

      prebuilt/linux-x86/toolchain/arm-eabi-4.3.1/bin/arm-eabi-gcc

其中參數“-I”和“-L”分別指定了所用的C庫頭文件和動態庫文件路徑,分別是“bionic/libc/include”和“out/target/product/generic/obj/lib”,其他還包括很多編譯選項以及-D所定義的預編譯宏。

(4)此時可以利用上面的編譯命令手工編譯helloworld程序,首先手工刪除上次編譯得到的helloworld程序。

      # rm out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/hello.o
      # rm out/target/product/generic/system/bin/helloworld

然后再用gcc編譯以生成目標文件。

      #  prebuilt/linux-x86/toolchain/arm-eabi-4.3.1/bin/arm-eabi-gcc  -I  bionic/libc/arch-
      arm/include  -I  bionic/libc/include  -I  bionic/libc/kernel/common    -I  bionic/libc/
      kernel/arch-arm  -c    -fno-exceptions  -Wno-multichar  -march=armv5te  -mtune=xscale
      -msoft-float  -fpic  -mthumb-interwork  -ffunction-sections  -funwind-tables  -fstack-
      protector  -D__ARM_ARCH_5__  -D__ARM_ARCH_5T__  -D__ARM_ARCH_5E__  -D__ARM_ARCH_5TE__
      -include   system/core/include/arch/linux-arm/AndroidConfig.h   -DANDROID   -fmessage-
      length=0  -W  -Wall  -Wno-unused  -DSK_RELEASE  -DNDEBUG  -O2  -g  -Wstrict-aliasing=2
      -finline-functions -fno-inline-functions-called-once -fgcse-after-reload -frerun-cse-
      after-loop  -frename-registers  -DNDEBUG  -UDEBUG  -mthumb  -Os  -fomit-frame-pointer
      -fno-strict-aliasing -finline-limit=64-MD -o out/target/product/generic/obj/ EXECUTABLES/
      helloworld_intermediates/hello.o development/hello/hello.c

如果此時與Android.mk編譯參數進行比較,會發現上面主要減少了不必要的參數“-I”。

(5)接下來開始生成可執行文件。

      #  prebuilt/linux-x86/toolchain/arm-eabi-4.3.1/bin/arm-eabi-gcc  -nostdlib  -Bdynamic
      -Wl, -T, build/core/armelf.x  -Wl, -dynamic-linker, /system/bin/linker  -Wl, --gc-sections
      -Wl, -z, nocopyreloc -o out/target/product/generic/obj/EXECUTABLES/helloworld_ intermediates/
      LINKED/helloworld  -Lout/target/product/generic/obj/lib  -Wl, -rpath-link=out/target/
      product/generic/obj/lib   -lc   -lm   out/target/product/generic/obj/EXECUTABLES/
      helloworld_intermediates/hello.o out/target/product/generic/obj/lib/ crtbegin_dynamic.o
      -Wl, --no-undefined ./prebuilt/linux-x86/toolchain/arm-eabi-4.3.1/ bin/../lib/gcc/arm-
      eabi/4.3.1/interwork/libgcc.a out/target/product/generic/obj/lib/crtend_android.o

在此需要特別注意的是參數“-Wl, -dynamic-linker, /system/bin/linker”,它指定了Android專用的動態鏈接器是“/system/bin/linker”,而不是平常使用的“ld.so”。

(6)最后可以使用命令file和readelf來查看生成的可執行程序。

      #  file  out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/LINKED/
      helloworld
      out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/LINKED/helloworl

      d: ELF 33-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs),
      not stripped
      #   readelf  -d  out/target/product/generic/obj/EXECUTABLES/helloworld_intermediates/
      LINKED/helloworld |grep NEEDED
       0x00000001 (NEEDED)                  Shared library: [libc.so]
       0x00000001 (NEEDED)                  Shared library: [libm.so]

這就是ARM格式的動態鏈接可執行文件,在運行時需要libc.so和libm.so。當提示“not stripped”時表示它還沒被STRIP(剝離)。嵌入式系統中為節省空間通常將編譯完成的可執行文件或動態庫進行剝離,即去掉其中多余的符號表信息。在前面“make helloworld showcommands”命令的最后也可以看到,Android編譯環境中使用了“out/host/linux-x86/bin/soslim”工具進行STRIP。

3.5 編譯Android Kernel

編譯Android Kernel代碼就是編譯Android內核代碼,在進行具體編譯工作,之前需要先了解在Android開源系統中包含的如下3部分代碼。

· 仿真器公共代碼:對應的工程名是kernel/common.get;

· MSM平臺的內核代碼:對應的工程名是kernel/msm.get;

· OMAP平臺的內核代碼:對應的工程名是kernel/omap.get。

在本節的內容中,將詳細講解編譯上述Android Kernel的基本知識。

3.5.1 獲取Goldfish內核代碼

Goldfish是一種虛擬的ARM處理器,通常在Android的仿真環境中使用。在Linux的內核中,Goldfish作為ARM體系結構的一種“機器”。在Android的發展過程中,Goldfish內核的版本也從Linux 2.6.25升級到了Linux 3.4,此處理器的Linux內核和標準的Linux內核有以下3個方面的差別。

· Goldfish機器的移植;

· Goldfish一些虛擬設備的驅動程序;

· Android中特有的驅動程序和組件。

Goldfish處理器有兩個版本,分別是ARMv5和ARMv7,在一般情況下,只需要使用ARMv5版本即可。在Android開源工程的代碼倉庫中,使用git工具得到Goldfish內核代碼的命令如下所示。

      $ git clone git://android.git.kernel.org/kernel/common.git

在其Linux源代碼的根目錄中,配置和編譯Goldfish內核的過程如下所示。

      $make ARCH=arm goldfish_defconfig .config
      $make ARCH=arm CROSS_COMPILE={path}/arm-none-linux-gnueabi-

其中,CROSS_COMPILE的path值用于指定交叉編譯工具的路徑。

編譯結果如下所示。

      LD vmlinux
      SYSMAP system.map
      SYSMAP .tmp_system.map
      OBJCOPY arch/arm/boot/Image
      Kernel: arch/arm/boot/Image is ready
      AS arch/arm/boot/compressed/head.o
      GZIP arch/arm/boot/compressed/piggy.gz
      AS arch/arm/boot/compressed/piggy.o
      CC arch/arm/boot/compressed/misc.o
      LD arch/arm/boot/compressed/vmlinux
        OBJCONPY arch/arm/boot/zImage
        Kernel: arch/arm/boot/zImage is ready

· vmlinux:是Linux進行編譯和連接之后生成的Elf格式的文件;

· Image:是未經過壓縮的二進制文件;

· piggy:是一個解壓縮程序;

· zImage:是解壓縮程序和壓縮內核的組合。

在Android源代碼的根目錄中,vmlinux和zImage對應Android代碼prebuilt中的預編譯的arm內核。使用zImage可以替換prebuilt中的“prebuilt/android-arm/”目錄下的goldfish_defconfig,此文件的主要片斷如下所示。

      CONFIG_ARM=y
      #
      # System Type
      #
      CONFIG_ARCH_GOLDFISH=y
      #
      # Goldfish options
      #
      CONFIG_MACH_GOLDFISH=y
      # CONFIG_MACH_GOLDFISH_ARMV7 is not set

因為GoldFish是ARM處理器,所以CONFIG_ARM宏需要被使用,CONFIG_ARCH_GOLDFISH和CONFIG_MACH_GOLDFISH宏是GoldFish處理器這類機器使用的配置宏。

在gildfish_defconfig中,與Android系統相關的宏如下所示。

      #
      # android
      #
      CONFIG_ANDROID=y
      CONFIG_ANDROID_BUNDER_IPC=y #binder ipc驅動程序
      CONFIG_ANDROID_LOGGER=y #log記錄器驅動程序
      # CONFIG_ANDROID_RAM_CONSOLE is not set
      CONFIG_ANDROID_TIMED_OUTPUT=y #定時輸出驅動程序框架
      CONFIG_ANDROID_LOW_MEMORY_KILLER=y
      CONFIG_ANDROID_PMEM=y #物理內存驅動程序
      CONFIG_ASHMEM=y #匿名共享內存驅動程序
      CONFIG_RTC_INTF_ALARM=y
      CONFIG_HAS_WAKELOCK=y電源管理相關的部分wakelock和earlysuspend
      CONFIG_HAS_EARLYSUSPEND=y
      CONFIG_WAKELOCK=y
      CONFIG_WAKELOCK_STAT=y
      CONFIG_USER_WAKELOCK=y
      CONFIG_EARLYSUSPEND=y
      goldfish_defconfig配置文件中,另外有一個宏是處理器虛擬設備的“驅動程序”,其內容如下所示。
      CONFIG_MTD_GOLDFISH_NAND=y
      CONFIG_KEYBOARD_GOLDFISH_EVENTS=y
      CONFIG_GOLDFISH_TTY=y
      CONFIG_BATTERY_GOLDFISH=y
      CONFIG_FB_GOLDFISH=y
      CONFIG_MMC_GOLDFISH=y
      CONFIG_RTC_DRV_GOLDFISH=y

在Goldfish處理器的各個配置選項中,體系結構和Goldfish的虛擬驅動程序基于標準Linux的內容的驅動程序框架,但是這些設備在不同的硬件平臺的移植方式不同;Android專用的驅動程序是Android中特有的內容,非Linux標準,但是和硬件平臺無關。

和原Linux內核相比,Android內核增加了Android的相關Driver,對應的目錄如下所示。

      kernel/drivers/android

主要分為以下幾類Driver。

· Android IPC系統:Binder (binder.c);

· Android日志系統:Logger (logger.c);

· Android電源管理:Power (power.c);

· Android鬧鐘管理:Alarm (alarm.c);

· Android內存控制臺:Ram_console (ram_console.c);

· Android時鐘控制的gpio:Timed_gpio (timed_gpio.c)。

對于本書講解的驅動程序開發來說,人們比較關心的是GoldFish平臺下相關的驅動文件,具體說明如下所示。

1.字符輸出設備

      kernel/drivers/char/goldfish_tty.c

2.圖像顯示設備(Frame Buffer)

      kernel/drivers/video/goldfishfb.c

3.鍵盤輸入設備文件

      kernel/drivers/input/keyboard/goldfish_events.c

4.RTC設備(Real Time Clock)文件

      kernel/drivers/rtc/rtc-goldfish.c

5.USB Device設備文件

      kernel/drivers/usb/gadget/android_adb.c

6.SD卡設備文件

      kernel/drivers/mmc/host/goldfish.c

7.FLASH設備文件

      kernel/drivers/mtd/devices/goldfish_nand.c
      kernel/drivers/mtd/devices/goldfish_nand_reg.h

8.LED設備文件

      kernel/drivers/leds/ledtrig-sleep.c

9.電源設備

      kernel/drivers/power/goldfish_battery.c

10.音頻設備

      kernel/arch/arm/mach-goldfish/audio.c

11.電源管理

      kernel/arch/arm/mach-goldfish/pm.c

12.時鐘管理

      kernel/arch/arm/mach-goldfish/timer.c

3.5.2 獲取MSM內核代碼

在當前市面中,谷歌的手機產品G1是基于MSM內核的,MSM是高通公司的應用處理器,在Android代碼庫中公開了對應的MSM的源代碼。在Android開源工程的代碼倉庫中,使用git工具得到MSM內核代碼的命令如下所示。

      $ git clone git://android.git.kernel.org/kernel/msm.git

3.5.3 獲取OMAP內核代碼

OMAP是德州儀器公司的應用處理器,為Android使用的是OMAP3系列的處理器。在Android代碼庫中公開了對應的MSM的源代碼,使用git工具得到MSM內核代碼的命令如下所示。

      $ git clone git://android.git.kernel.org/kernel/omap.git

3.5.4 編譯Android的Linux內核

了解了上述3類Android內核后,下面開始講解編譯Android內核的方法。在此假設以Ubuntu 8.10為例,完整編譯Android內核的流程如下所示。

1.構建交叉編譯環境

Android的默認硬件處理器是ARM,因此需要在自己的機器上構建交叉編譯環境。交叉編譯器GNU Toolchain for ARM Processors下載地址如下所示。

      http://www.codesourcery.com/gnu_toolchains/arm/download.html

單擊GNU/Linux對應的鏈接,之后單擊“Download Sourcery CodeBench Lite 5.12012.03-117”鏈接后直接下載,如圖3-14所示。

▲圖3-14 下載交叉編譯器

把arm-2008q3-73-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar.bz2解壓到一目錄下,例如“~/programes/”,并加入PATH環境變量:

      vim ~/.bashrc

然后添加:

      ARM_TOOLCHIAN=~/programes/arm-2008q3/bin/
      export PATH=${PATH}:${ARM_TOOLCHIAN};

保存后并入source ~/.bashrc。

2.獲取內核源碼

源碼地址如下所示。

      http://code.google.com/p/android/downloads/list

選擇的內核版本要與選用的模擬器版本盡量一致。下載后并解壓后得到kernel.git文件夾:

      tar -xvf ~/download/linux-3.2.5-android-4.3_r1.tar.gz

3.獲取內核編譯配置信息文件

編譯內核時需要使用configure,通常configure有很多選項,人們往往不知道需要哪些選項。在運行Android模擬器時,有一個文件“/proc/config.gz”,這是當前內核的配置信息文件,把config.gz獲取并解壓到“kernel.git/”下,然后改名為.config。命令如下所示。

      cd kernel.git/
      emulator &
      adb pull /proc/config.gz
      gunzip config.gz
      mv config .config

4.修改Makefile

修改195行的代碼:

      CROSS_COMPILE    = arm-none-linux-gnueabi-

將CROSS_COMPILE值改為arm-none-linux-gnueabi-,這是安裝的交叉編譯工具鏈的前綴,修改此處意在告訴make在編譯的時候要使用該工具鏈。然后注釋掉562和563行的如下代碼:

      #LDFLAGS_BUILD_ID = $(patsubst -Wl$(comma)%, %, /
      #$(call ld-option, -Wl$(comma)--build-id, ))

必須將上述代碼中的build id值注釋掉,因為目前版本的Android內核不支持該選項。

5.編譯

使用make進行編譯,并同時生成zImage。

      LD     arch/arm/boot/compressed/vmlinux
      OBJCOPY arch/arm/boot/zImage
      Kernel: arch/arm/boot/zImage is ready

這樣生成zImage大小為1.23 MB, android- sdk-linux_x86-4.3_r1/tools/lib/images/kernel-qemu是1.24 MB。

6.使用模擬器加載內核測試

命令如下所示。

      cd android/out/cupcake/out/target/product/generic
      emulator   -image   system.img   -data   userdata.img   -ramdisk   ramdisk.img   -kernel
      ~/project/android/kernel.git/arch/arm/boot/zImage &

到此為止,模擬器就加載成功了。

3.6 編譯源碼生成SDK

平時大部分Android應用程序開發都是基于SDK實現的,其過程是使用SDK中的接口實現各種各樣的功能。可以在Android的官方網站上面直接下載最新的SDK版本,不過也可以從源碼中生成SDK,因為源碼里面也包含有SDK的代碼。

在下載的Android 4.3的源碼的根目錄下有個SDK目錄,所有的SDK相關的代碼都存儲在這個目錄,包括鏡像文件、模擬器和ADB等常用工具以及SDK中的開發包的文檔,可以通過編譯的方式來生成開發需要的SDK,編譯命令如下所示。

      $ Make SDK

當編譯完成后,會在/out/host/linux-x86/sdk/目錄下生成SDK,這個SDK是完全跟源碼同步的,與官網上下載的SDK功能完全相同,會有開發用的JAR包、模擬器管理工具和ADB調試工具,可以使用這個編譯生成的SDK來開發應用程序。

對于Android系統的開發,基本可以分為如下兩種開發方式。

· 基于SDK的開發;

· 基于源碼的開發。

在一般的情況下,開發的應用程序都是基于SDK的開發,比較方便而且兼容性比較好。基于源碼的開發相對于基于SDK的開發要求對源碼的架構認識更深刻,一般用于需要修改系統層面的場合。兩種方式應用場景不同,各有優缺點,在本節將主要介紹基于SDK的開發。

如果想基于SDK開發Android的應用程序,需要JDK、SDK和一個開發環境,JDK和SDK在不同的平臺下有不同的版本,本章主要討論Windows 7平臺下的開發環境搭建。

1.安裝JDK

由于Android的應用程序是使用Java語言開發的,所以首先需要安裝Java的JDK,下載鏈接為http://java.sun.com/javase/downloads/index.jsp,進入后選擇合適的平臺以及下載最新版本的JDK,安裝成功后在命令行下可以查看JDK版本,如圖3-15所示。

▲圖3-15 成功安裝的JDK

2.安裝Eclipse

Eclipse是開發Android應用程序的IDE環境,有非常豐富的插件可以使用,單擊http://www.eclipse.org/downloads/可以下載合適平臺的最新版本Eclipse。

3.安裝Android SDK

Android SDK是Google對外發布的專門用于Android開發的工具包,里面有各種版本的開發框架和工具以及豐富的文檔,打開http://developer.android.com/sdk/index.html可以下載最新版本的針對Window 7平臺的SDK。

當下載完成上述3個工具之后,需要對開發環境進行如下所示的配置。

1.配置Eclipse

第1步:打開Eclipse,在菜單欄上選擇“help”|“Install New SoftWare”,出現圖3-16所示的界面。

▲圖3-16 “Install”界面

第2步:單擊“Add”按鈕,會出現圖3-17所示的界面。

▲圖3-17 “Add Repository”界面

第3步:在Name欄里面輸入“Android”或者自定義任何名字,在Location里面輸入“https://dl-ssl.google.com/android/eclipse/”,輸入后的效果如圖3-18所示。

▲圖3-18 “Add Site”界面

第4步:如果發現https://無法使用,可以改成http://嘗試下,當輸入完名字和地址之后,單擊“OK”按鈕,會出現圖3-19所示的界面。

▲圖3-19 “Install”界面

圖3-19中的兩個插件都是開發Android必不可少的工具包,Android DDMS是可以用來調試、管理Android進程和存儲器、查看日志的工具,Android Development Tool簡稱ADT,是開發Android的插件,只有裝了ADT才能創建Android工程。

第5步:單擊“Next”按鈕,出現圖3-20所示的界面。

▲圖3-20 選擇安裝

在圖3-20中列出了將會安裝的工具包,選中“I accept the terms of the license agreements”選項,單擊“Next”按鈕會開始安裝插件,界面如圖3-21所示。

▲圖3-21 開始安裝

第6步:當所有插件安裝成功后,會彈出提示界面,如圖3-22所示。

▲圖3-22 成功

這時需要單擊“Yes”按鈕重啟Eclipse讓所有插件生效。

2.配置Android SDK

打開Eclipse,單擊“Window”|“Preferences”,進入圖3-23所示的界面。

▲圖3-23 配置界面

這樣就可以從Eclipse中新建Android工程,要想新建工程是基于何種版本的Android系統,可以打開SDK根目錄下的SDK管理工具SDK Manager.exe,雙擊后會進入SDK工具包管理界面,如圖3-24所示。

▲圖3-24 Android SDK管理

在圖3-24中可以看到,很清晰地列出了當前版本SDK中包含的工具包以及已經安裝的和沒有安裝的版本。可以繼續單擊“Install 11 packages”或者“Delete 8 packages”按鈕安裝和刪除SDK中的工具包。如果是安裝,則過程會比較慢,與網速的關系比較密切。當SDK中的工具包安裝完畢,同時也就完成了Eclipse和SDK的配置工作,至此在Windows 7平臺下基于SDK的Android的開發環境搭建全部完成。

主站蜘蛛池模板: 娱乐| 赤峰市| 开鲁县| 沙洋县| 衡阳县| 松溪县| 宝坻区| 大荔县| 蒙山县| 长兴县| 迁安市| 温泉县| 荔波县| 定陶县| 邢台市| 汝阳县| 青州市| 太仆寺旗| 资讯 | 土默特左旗| 淮滨县| 太康县| 东乌| 微山县| 金平| 胶州市| 定结县| 沁源县| 青浦区| 瑞昌市| 东港市| 济阳县| 谢通门县| 泉州市| 开远市| 克拉玛依市| 宜宾县| 石嘴山市| 泽普县| 玛曲县| 荔波县|