- Android傳感器開發與智能設備案例實戰
- 朱元波
- 10320字
- 2019-01-05 01:08:03
第4章 Android技術核心框架分析
學習編程不能打無把握之仗,學習Android傳感器和外部設備開發也是如此。要想真正精通開發Android傳感器和外部設備應用程序開發技術的精髓,不僅需要學習底層和Android框架方面的知識,還需要掌握Android頂層應用程序開發的基本知識。本章將詳細講解Android系統的體系結構,為讀者步入本書后面高級知識的學習打下基礎。
4.1 分析Android的系統架構
為了更加深入地理解Android系統的精髓,初學者們很有必要了解Android系統的整體架構,了解它的具體組成。只有這樣才能知道Android究竟能干什么,人們所要學的是什么。
4.1.1 Android體系結構介紹
Android是一個移動設備的開發平臺,其軟件層次結構包括操作系統(OS)、中間件(MiddleWare)和應用程序(Application)。根據Android的軟件框圖,其軟件層次結構自下而上分為以下4層:
(1)操作系統層(OS);
(2)各種庫(Libraries)和Android運行環境(RunTime);
(3)應用程序框架(Application Framework);
(4)應用程序(Application)。
上述各個層的具體結構如圖4-1所示。

▲圖4-1 Android操作系統的組件結構圖
1.操作系統層(OS)——最底層
因為Android源于Linux,使用了Linux內核,所以Android使用Linux 2.6作為操作系統。Linux 2.6是一種標準的技術,Linux也是一個開放的操作系統。Android對操作系統的使用包括核心和驅動程序兩部分,Android的Linux核心為標準的Linux 2.6內核,Android更多的是需要一些與移動設備相關的驅動程序。主要的驅動如下所示。
· 顯示驅動(Display Driver):常用基于Linux的幀緩沖(Frame Buffer)驅動;
· Flash內存驅動(Flash Memory Driver):是基于MTD的Flash驅動程序;
· 照相機驅動(Camera Driver):常用基于Linux的v4l(Video for)驅動;
· 音頻驅動(Audio Driver):常用基于ALSA(Advanced Linux Sound Architecture,高級Linux聲音體系)驅動;
· Wi-Fi驅動(Camera Driver):基于IEEE 802.11標準的驅動程序;
· 鍵盤驅動(KeyBoard Driver):作為輸入設備的鍵盤驅動;
· 藍牙驅動(Bluetooth Driver):基于IEEE 802.15.1標準的無線傳輸技術;
· Binder IPC驅動:Android一個特殊的驅動程序,具有單獨的設備節點,提供進程間通信的功能;
· Power Management(能源管理):管理電池電量等信息。
2.各種庫(Libraries)和Android運行環境(RunTime)——中間層
本層次對應一般嵌入式系統,相當于中間件層次。Android的本層次分成兩個部分,一個是各種庫,另一個是Android運行環境。本層的內容大多是使用C實現的。其中包含的各種庫如下所示。
· C庫:C語言的標準庫,也是系統中一個最為底層的庫,C庫是通過Linux的系統調用來實現;
· 多媒體框架(MediaFrameword):這部分內容是Android多媒體的核心部分,基于PacketVideo(即PV)的OpenCORE,從功能上本庫一共分為兩大部分,一個部分是音頻、視頻的回放(PlayBack),另一部分是則是音視頻的記錄(Recorder);
· SGL:2D圖像引擎;
· SSL:即Secure Socket Layer位于TCP/IP協議與各種應用層協議之間,為數據通信提供安全支持;
· OpenGL ES 1.0:提供了對3D的支持;
· 界面管理工具(Surface Management):提供了對管理顯示子系統等功能;
· SQLite:一個通用的嵌入式數據庫;
· WebKit:網絡瀏覽器的核心;
· FreeType:位圖和矢量字體的功能;
Android的各種庫一般是以系統中間件的形式提供的,它們均有的一個顯著特點就是與移動設備的平臺的應用密切相關。
Android運行環境主要是指的虛擬機技術——Dalvik。Dalvik虛擬機和一般Java虛擬機(Java VM)不同,它執行的不是Java標準的字節碼(Bytecode),而是Dalvik可執行格式(.dex)中執行文件。在執行的過程中,每一個應用程序即一個進程(Linux的一個Process)。二者最大的區別在于Java VM是基于棧(Stack-based)的虛擬機,而Dalvik是基于寄存器(Register-based)的虛擬機。顯然,后者最大的好處在于可以根據硬件實現更大的優化,這更適合移動設備的特點。
3.應用程序(Application)
Android的應用程序主要是用戶界面(User Interface)方面的,通常用Java語言編寫,其中還可以包含各種資源文件(放置在res目錄中)。Java程序和相關資源在經過編譯后,會生成一個APK包。Android本身提供了主屏幕(Home)、聯系人(Contact)、電話(Phone)和瀏覽器(Browers)等眾多的核心應用。同時應用程序的開發者還可以使用應用程序框架層的API實現自己的程序。這也是Android開源的巨大潛力的體現。
4.應用程序框架(Application Framework)
Android的應用程序框架為應用程序層的開發者提供APIs,它實際上是一個應用程序的框架。由于上層的應用程序是以Java構建的,因此本層次提供的首先包含了UI程序中所需要的各種控件,例如:Views(視圖組件),其中又包括了List(列表)、Grid(柵格)、Text Box(文本框)和Button(按鈕)等。甚至一個嵌入式的Web瀏覽器。
作為一個基本的Android應用程序,可以利用應用程序框架中的以下5個部分來構建:
· Activity(活動);
· Broadcast Intent Receiver(廣播意圖接收者);
· Service(服務);
· Content Provider(內容提供者);
· Intent and Intent Filter(意圖和意圖過濾器)。
4.1.2 Android應用工程文件組成
講解完Android的整體結構之后,接下來開始講解Android工程文件的組成。在Eclipse中,一個基本的Android項目的目錄結構如圖4-2所示。

▲圖4-2 Android應用工程文件組成
1.src目錄
在里面保存了開發人員編寫的程序文件。和一般的Java項目一樣,“src”目錄下保存的是項目的所有包及源文件(.java),“res”目錄下包含了項目中的所有資源。例如,程序圖標(drawable)、布局文件(layout)和常量(values)等。不同的是,在Java項目中沒有“gen”目錄,也沒有每個Android項目都必須有的AndroidManfest.xml文件。
“.java”格式文件是在建立項目時自動生成的,這個文件是只讀模式,不能更改。R.java文件是定義該項目所有資源的索引文件。例如下面是某項目中R.java文件的代碼。
package com.yarin.Android.HelloAndroid; public final class R { public static final class attr { } public static final class drawable { public static final int icon=0x7f020000; } public static final class layout { public static final int main=0x7f030000; } public static final class string { public static final int app_name=0x7f040001; public static final int hello=0x7f040000; } }
在上述代碼中定義了很多常量,并且這些常量的名字都與res文件夾中的文件名相同,這再次證明.java文件中所存儲的是該項目所有資源的索引。有了這個文件,在程序中使用資源將變得更加方便,可以很快地找到要使用的資源,由于這個文件不能被手動編輯,所以在項目中加入了新的資源時,只需要刷新一下該項目,.java文件便自動生成了所有資源的索引。
2.設置文件AndroidManfest.xml
文件AndroidManfest.xml是一個控制文件,在里面包含了該項目中所使用的Activity、Service和Receiver。例如下面是某項目中文件AndroidManfest.xml的代碼。
<? xml version="1.0" encoding="utf-8"? > <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.yarin.Android.HelloAndroid" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".HelloAndroid" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> <uses-sdk android:minSdkVersion="9" /> </manifest>
在上述代碼中,intent-filters描述了Activity啟動的位置和時間。每當一個Activity(或者操作系統)要執行一個操作時,它將創建出一個Intent的對象,這個Intent對象可以描述想做什么、想處理什么數據、數據的類型以及一些其他信息。Android會和每個Application所出現的intent-filter的數據進行比較,找到最合適Activity來處理調用者所指定的數據和操作。下面來仔細分析AndroidManfest.xml文件,如表4-1所示。
表4-1 AndroidManfest.xml分析

3.常量定義文件
下面介紹一在資源文件中對常量的定義,例如文件String.xml的代碼如下所示。
<? xml version="1.0" encoding="utf-8"? > <resources> <string name="hello">Hello World, HelloAndroid! </string> <string name="app_name">HelloAndroid</string> </resources>
上述常量定義文件的代碼非常簡單,只定義了兩個字符串資源,請不要小看上面的幾行代碼。它們的內容很“露臉”,里面的字符直接顯示在手機屏幕中,就像動態網站中的HTML一樣。
4.布局文件
布局(layout)文件一般位于“res\layout\main.xml”目錄,通過其代碼能夠生成一個顯示界面。例如下面的代碼。
<? xml version="1.0" encoding="utf-8"? > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" /> </LinearLayout>
在上述代碼中,有以下幾個布局和參數。
· <LinearLayout></LinearLayout>:在這個標簽中,所有元件都是按由上到下的隊列排成的。
· android:orientation:表示這個介質的版面配置方式是從上到下垂直地排列其內部的視圖。
· android:layout_width:定義當前視圖在屏幕上所占的寬度,fill_parent即填充整個屏幕。
· android:layout_height:定義當前視圖在屏幕上所占的高度,fill_parent即填充整個屏幕。
· wrap_content:隨著文字欄位的不同而改變這個視圖的寬度或高度。
在上述布局代碼中,使用了一個TextView來配置文本標簽Widget(構件),其中設置的屬性android:layout_width為整個屏幕的寬度,android:layout_height可以根據文字來改變高度,而android:text則設置了這個TextView要顯示的文字內容,這里引用了@string中的hello字符串,即String.xml文件中的hello所代表的字符串資源。hello字符串的內容Hello World, HelloAndroid!這就是人們在HelloAndroid項目運行時看到的字符串。
注意
上面介紹的文件只是主要文件,在項目中需要自行編寫。在項目中還有很多其他的文件,那些文件很少需要自己編寫,所以在此就不進行講解了。
4.2 Android的五大組件
一個典型的Android應用程序通常由5個組件組成,這5個組件構成了Android的核心功能。本節將一一講解這五大組件的基本知識,為讀者步入本書后面知識的學習打下基礎。
4.2.1 Activity界面組件
Activitiy是這5個組件中最常用的一個組件。程序中Activity通常的表現形式是一個單獨的界面(screen)。每個Activitiy都是一個單獨的類,它擴展實現了Activity基礎類。這個類顯示為一個由Views組成的用戶界面,并響應事件。大多數程序有多個Activity。例如,一個文本信息程序有以下幾個界面:顯示聯系人列表界面、寫信息界面、查看信息界面或者設置界面等。每個界面都是一個Activity。切換到另一個界面就是載入一個新的Activity。在某些情況下,一個Activity可能會給前一個Activity返回值,例如,一個讓用戶選擇相片的Activity會把選擇到的相片返回給其調用者。
打開一個新界面后,前一個界面就被暫停,并放入歷史棧中(界面切換歷史棧)。使用者可以回溯前面已經打開的存儲在歷史棧中的界面。也可以從歷史棧中刪除沒有界面價值的界面。Android在歷史棧中保留程序運行產生的所有界面:從第一個界面,到最后一個。
4.2.2 Intent切換組件
Android通過一個專門的Intent類來進行界面的切換。Intent描述了程序想做什么(Intent翻譯為意圖、目的、意向)。Intent類還有一個相關類IntentFilter。Intent是一個請求來做什么事情,IntentFilter則描述了一個Activity(或下文的IntentReceiver)能處理什么意圖。顯示某人聯系信息的Activity使用了一個IntentFilter,就是說它知道如何處理應用到此人數據的VIEW操作。Activities在文件AndroidManifest.xml中使用IntentFilters。
通過解析Intents可以實現Activity的切換,人們可以使用startActivity(myIntent)啟用新的Activity。系統會考察所有安裝程序的IntentFilters,然后找到與myIntent匹配最好的IntentFilters所對應的Activity。這個新Activity能夠接收Intent傳來的消息,并因此被啟用。解析Intents的過程發生在startActivity被實時調用的時候,這樣做有如下兩個好處。
(1)Activity僅發出一個Intent請求,便能重用其他組件的功能。
(2)Activity可以隨時被替換為有等價IntentFilter的新Activity。
4.2.3 Service服務組件
Service是一個沒有UI且長駐系統的代碼,最常見的例子是媒體播放器從播放列表中播放歌曲。在媒體播放器程序中,可能有一個或多個Activities讓用戶選擇播放的歌曲。然而在后臺播放歌曲時無須Activity干涉,因為用戶希望在音樂播放的同時能夠切換到其他界面。既然這樣,媒體播放器Activity需要通過Context.startService()啟動一個Service,這個Service在后臺運行以保持繼續播放音樂。在媒體播放器被關閉之前,系統會保持音樂后臺播放Service的正常運行。可以用Context.bindService()方法連接到一個Service上(如果Service未運行的話,連接后還會啟動它),連接后就可以通過一個Service提供的接口與Service進行通話。對音樂Service來說,提供了暫停和重放等功能。
1.如何使用服務
在Android系統中有如下兩種使用服務的方法。
(1)通過調用Context.startServece()啟動服務,調用Context.stopService()結束服務,startService()可以傳遞參數給Service。
(2)通過調用Context.bindService()啟動,調用Context.unbindService()結束,還可以通過ServiceConnection訪問Service。二者可以混合使用,例如可以先調用startService()再調用unbindService()。
2.Service的生命周期
在startService()后,即使調用startService()的進程結束了,Service還仍然存在,一直到有進程調用stoptService()或者Service自己結束(stopSelf())為止。
在bindService()后,Service就和調用bindService()的進程同生共死了,也就是說當調用bindService()的進程結束了,那么它綁定的Service也要跟著被結束,當然期間也可以調用unbind Service()讓Service結束。
當混合使用上述兩種方式時,例如你啟動startService(),我啟動bindService(),那么只有你調用stoptService()而且我也調用unbindService(),這個Service才會被結束。
3.進程生命周期
Android系統將會嘗試保留那些啟動的或者綁定的服務進程,具體說明如下所示。
(1)如果該服務正在進程的onCreate()、onStart()或者onDestroy()這些方法中執行時,那么主進程將會成為一個前臺進程,以確保此代碼不會被停止。
(2)如果服務已經開始,那么它的主進程的重要性會低于所有的可見進程,但是會高于不可見進程。由于只有少數幾個進程是用戶可見的,所以只要不是內存特別低,該服務就不會停止。
(3)如果有多個客戶端綁定了服務,只要客戶端中的一個對于用戶是可見的,就可以認為該服務是可見的。
4.2.4 用Broadcast/Receiver廣播機制組件
當要執行一些與外部事件相關的代碼時,例如來電響鈴時或者半夜時就可能用到IntentReceiver。盡管IntentReceiver使用NotificationManager來通知用戶一些好玩的事情發生,如果沒有UI.IntentReceivers可以在文件AndroidManifest.xml中聲明,也可以使用Context.register Receiver()來聲明。當一個IntentReceiver被觸發時,如果需要系統自然會自動啟動程序。程序也可以通過Context.broadcastIntent()來發送自己的Intent廣播給其他程序。
4.2.5 ContentProvider存儲組件
應用程序把數據存儲一個SQLite數據庫格式文件里,或者存儲在其他有效設備里。如果想讓其他程序能夠使用程序中的數據,此時Content Provider就很有用了。Content Provider是一個實現了一系列標準方法的類,這個類使得其他程序能存儲、讀取某種Content Provider可處理的數據。
4.3 進程和線程
進程和線程很容易理解,計算機中有一個進程管理器,當打開后,會顯示當前運行的所有程序。同樣在Android中也有進程,當某個組件第一次運行的時候,Android會啟動一個進程。在默認情況下,所有的組件和程序運行在這個進程和線程中,也可以安排組件在其他的進程或者線程中運行。
4.3.1 什么是進程
組件運行的進程是由manifest file控制的。組件的節點一般都包含一個process屬性,例如<activity>、<service>、<receiver>和<provider>節點。屬性process可以設置組件運行的進程,可以配置組件在一個獨立進程中運行,或者多個組件在同一個進程中運行,甚至可以多個程序在一個進程中運行,當然前提是這些程序共享一個User ID并給定同樣的權限。另外<application>節點也包含了process屬性,用來設置程序中所有組件的默認進程。
當更加常用的進程無法獲取足夠內存時,Android會智能地關閉不常用的進程。當下次啟動程序的時候會重新啟動這些進程。當決定哪個進程需要被關閉的時候,Android會考慮哪個對用戶更加有用。例如Android會傾向于關閉一個長期不顯示在界面的進程來支持一個經常顯示在界面的進程。是否關閉一個進程決定于組件在進程中的狀態。
4.3.2 什么是線程
當用戶界面需要很快對用戶進行響應,就需要將一些費時的操作,如網絡連接、下載或者非常占用服務器時間的操作等放到其他線程。也就是說,即使為組件分配了不同的進程,有時候也需要再分配線程。
線程是通過Java的標準對象Thread來創建的,在Android中提供了如下方便地管理線程的方法:
(1)Looper在線程中運行一個消息循環;
(2)Handler傳遞一個消息;
(3)HandlerThread創建一個帶有消息循環的線程;
(4)Android讓一個應用程序在單獨的線程中,指導它創建自己的線程;
(5)應用程序組件(Activity、Service、Broadcast receiver)所有都在理想的主線程中實例化;
(6)當被系統調用時,沒有一個組件應該執行長時間或是阻塞操作(例如網絡呼叫或是計算循環),這將中斷所有在該進程的其他組件;
(7)可以創建一個新的線程來執行長期操作。
4.3.3 應用程序的生命周期
自然界的事物都有自己的生命周期,例如人的生、老、病、死。作為一個Android應用程序也如同自然界的生物一樣,也有自己的生命周期。人們開發一個程序的目的是為了完成一個功能,例如銀行計算加息的軟件,每當一個用戶去柜臺辦理取款業務時,銀行工作人員便啟動了這個程序的生命,當用這個軟件完成利息計算時,這個軟件當前的任務就完成了,此時就需要結束自己的使命。肯定有人提出疑問:生生死死多么麻煩,就讓這個程序一直是“活著”的狀態,一個用戶辦理完取款業務后,繼續等著下一個用戶辦理取款業務,這樣這個程序就“長生不老”了。其實誰都想自己的程序“長生不老”,但是很不幸,不能這樣做。原因是計算機的處理性能是一定的,一個人、兩個人、三個人計算機可以處理這個任務。但是一個安裝這個軟件的機器一天會處理成千上萬個取款業務,如果它們都一直“活著”,一臺有限配置的計算機能承受得了嗎?
由此可見,應用程序的生命周期就是一個程序的存活時間,即在什么時間內有效。Android是一個構建在Linux之上的開源移動開發平臺,在Android中,多數情況下每個程序都是在各自獨立的Linux進程中運行的。當一個程序或其某些部分被請求時,它的進程就“出生”了;當這個程序沒有必要再運行下去且系統需要回收這個進程的內存用于其他程序時,這個進程就“死亡”了。可以看出,Android程序的生命周期是由系統控制的而非程序自身直接控制的。這和編寫桌面應用程序時的思維有一些不同,一個桌面應用程序的進程也是在其他進程或用戶請求時被創建的,但是往往是在程序自身收到關閉請求后執行一個特定的動作(例如從main函數中返回)而導致進程結束的。要想做好某種類型的程序或者某種平臺下的程序的開發,最關鍵的就是要弄清楚這種類型的程序或整個平臺下的程序的一般工作模式并熟記在心。在Android中,程序的生命周期控制就是屬于這個范疇。
開發者必須理解不同的應用程序組件,尤其是Activity、Service和Intent Receiver,需要了解這些組件是如何影響應用程序的生命周期的。如果不能正確地使用這些組件,可能會導致系統終止正在執行重要任務的應用程序進程。
一個常見的進程生命周期漏洞的例子是Intent Receiver(意圖接收器),當Intent Receiver在onReceive方法中接收到一個Intent(意圖)時,它會啟動一個線程,然后返回。一旦返回,系統將認為Intent Receiver不再處于活動狀態,因而Intent Receiver所在的進程也就不再有用了(除非該進程中還有其他的組件處于活動狀態)。因此,系統可能會在任意時刻終止該進程以回收占有的內存。這樣進程中創建出的那個線程也將被終止。解決這個問題的方法是從Intent Receiver中啟動一個服務,讓系統知道進程中還有處于活動狀態的工作。為了使系統能夠正確決定在內存不足時應該終止哪個進程,Android根據每個進程中運行的組件及組件的狀態把進程放入一個“Importance Hierarchy(重要性分級)”中。
進程的類型多種多樣,按照重要的程度主要包括如下幾類進程。
1.前臺進程(Foreground)
前臺進程是看得見的,與用戶當前正在做的事情密切相關,不同的應用程序組件能夠通過不同的方法將它的宿主進程移到前臺。在下面的任何一個條件下系統都會把進程移動到前臺。
· 進程正在屏幕的最前端運行一個與用戶交互的活動(Activity),它的onResume方法被調用;
· 進程有一正在運行的Intent Receiver(它的IntentReceiver.onReceive方法正在執行);
· 進程有一個服務(Service),并且在服務的某個回調函數(Service.onCreate、Service.onStart或Service.onDestroy)內有正在執行的代碼。
2.可見進程(Visible)
可見進程也是可見的,它有一個可以被用戶從屏幕上看到的活動,但不在前臺(它的onPause方法被調用)。假如前臺的活動是一個對話框,以前的活動隱藏在對話框之后就會現這種進程了。可見進程非常重要,一般不允許被終止,除非是了保證前臺進程的運行而不得不終止它。
3.服務進程(Service)
服務進程是無法看見的,擁有一個已經用startService()方法啟動的服務。雖然用戶無法直接看到這些進程,但它們做的事情卻是用戶所關心的(如后臺MP3回放或后臺網絡數據的上傳、下載)。所以系統將一直運行這些進程,除非內存不足以維持所有的前臺進程和可見進程。
4.后臺進程(Background)
后臺進程也是看不見的,只有打開之后才能看見。例如迅雷下載,可以將其最小化,雖然在桌面上看不見了,但是它一直在進行下載的工作。擁有一個當前用戶看不到的活動(它的onStop()方法被調用)。這些進程對用戶體驗沒有直接的影響。如果它們正確執行了活動生命周期,系統可以在任意時刻終止該進程以回收內存,并提供給前面3種類型的進程使用。系統中通常有很多這樣的進程在運行,因此要將這些進程保存在LRU列表中,以確保當內存不足時用戶最近看到的進程最后一個被終止。
5.空進程(Empty)
空進程是指不擁有任何活動的應用程序組件的進程。保留這種進程的唯一原因是在下次應用程序的某個組件需要運行時,不需要重新創建進程,這樣可以提高啟動速度。系統將以進程中當前處于活動狀態組件的重要程度為基礎對進程進行分類。進程的優先級可能也會根據該進程與其他進程的依賴關系而增長。假如進程A通過在進程B中設置Context.BIND_AUTO_CREATE標記或使用ContentProvider綁定到一個服務(Service),那么進程B在分類時至少要被看成與進程A同等重要。
例如Activity的狀態轉換圖如圖4-3所示。

▲圖4-3 Activity狀態轉換圖
圖4-3所示的狀態的變化是由Android內存管理器決定的,Android會首先關閉那些包含Inactive Activity的應用程序,然后關閉Stopped狀態的程序。只有在極端情況下才會移除Paused狀態的程序。
4.4 Android和Linux的關系
在了解Linux和Android的關系之前,首先需要明確如下3點。
(1)Android采用Linux作為內核。
(2)Android對Linux內核做了修改,以適應其在移動設備上的應用。
(3)Andorid開始是作為Linux的一個分支,后來由于無法并入Linux的主開發樹,曾經被Linux內核組從開發樹中刪除。2012年5月18日,Linux kernel 3.3發布后來又被加入。
4.4.1 Android繼承于Linux
Android是在Linux的內核基礎之上運行的,提供的核心系統服務包括安全、內存管理、進程管理、網絡組和驅動模型等內容。內核部分還相當于一個介于硬件層和系統中其他軟件組之間的一個抽象層次。但是嚴格來說它不算是Linux操作系統。
因為Android內核是由標準的Linux內核修改而來的,所以繼承了Linux內核的諸多優點,保留了Linux內核的主題架構。同時Android按照移動設備的需求,在文件系統、內存管理、進程間通信機制和電源管理方面進行了修改,添加了相關的驅動程序和必要的新功能。但是和其他精簡的Linux系統相比(例如uClinux), Android很大程度地保留了Linux的基本架構,因此Android的應用性和擴展性更強。當前Android版本對應的Linux內核版本如下所示。
· Android 1.5:Linux-2.6.27。
· Android 1.6:Linux-2.6.29。
· Android 2.0,2.1:Linux-2.6.29。
· Android 2.2:Linux-2.6.32.9。
· Android 4.3:Linux-3.4。
4.4.2 Android和Linux內核的區別
Android系統的系統層面的底層是Linux,中間加上了一個叫作Dalvik的Java虛擬機,表面層上面是Android運行庫。每個Android應用都運行在自己的進程上,享有Dalvik虛擬機為它分配的專有實例。為了支持多個虛擬機在同一個設備上高效運行,Dalvik被改寫過。
Dalvik虛擬機執行的是Dalvik格式的可執行文件(.dex),該格式經過優化,以降低內存耗用到最低。Java編譯器將Java源文件轉為class文件,class文件又被內置的dx工具轉化為dex格式文件,這種文件在Dalvik虛擬機上注冊并運行。
Android系統的應用軟件都是運行在Dalvik之上的Java軟件,而Dalvik是運行在Linux中的,在一些底層功能,例如線程和低內存管理方面,Dalvik虛擬機是依賴Linux內核的。由此可見,可以說Android是運行在Linux之上的操作系統,但是它本身不能算是Linux的某個版本。
Android內核和Linux內核的差別主要體現在11個方面,接下來將一一簡要介紹。
1.Android Binder
其源代碼位于:
drivers/staging/android/binder.c
Android Binder是基于OpenBinder框架的一個驅動,用于提供Android平臺的進程間通信(Inter-Process Communication, IPC)。原來的Linux系統上層應用的進程間通信主要是D-bus(Desktop bus),采用消息總線的方式來進行IPC。
2.Android電源管理(PM)
Android電源管理是一個基于標準Linux電源管理系統的輕量級的Android電源管理驅動,針對嵌入式設備做了很多優化。利用鎖和定時器來切換系統狀態,控制設備在不同狀態下的功耗,以達到節能的目的。
Android電源管理的源代碼分別位于:
kernel/power/earlysuspend.c kernel/power/consoleearlysuspend.c kernel/power/fbearlysuspend.c kernel/power/wakelock.c kernel/power/userwakelock.c
3.低內存管理器(Low Memory Killer)
Android中的低內存管理器和Linux標準的OOM(Out Of Memory)相比,其機制更加靈活,它可以根據需要殺死進程來釋放需要的內存。Low Memory Killer的代碼很簡單,關鍵的一個函數是Lowmem_shrinker。作為一個模塊在初始化時調用register_shrinke注冊了個lowmem_shrinker,它會被VM在內存緊張的情況下調用。Lowmem_shrinker完成具體操作。簡單說就是尋找一個最合適的進程將其殺死,從而釋放它占用的內存。
低內存管理器的源代碼位于drivers/staging/android/lowmemorykiller.c。
4.匿名共享內存(Ashmem)
匿名共享內存為進程間提供大塊共享內存,同時為內核提供回收和管理這個內存的機制。如果一個程序嘗試訪問Kernel釋放的一個共享內存塊,它將會收到一個錯誤提示,然后重新分配內存并重載數據。
匿名共享內存的源代碼位于mm/ashmem.c。
5.Android PMEM(Physical)
PMEM用于向用戶空間提供連續的物理內存區域,DSP和某些設備只能工作在連續的物理內存上。驅動中提供了mmap、open、release和ioctl等接口。
Android PMEM的源代碼位于drivers/misc/pmem.c。
6.Android Logger
Android Logger是一個輕量級的日志設備,用于抓取Android系統的各種日志,是Linux所沒有的。
Android Logger的源代碼位于drivers/staging/android/logger.c。
7.Android Alarm
Android Alarm提供了一個定時器用于把設備從睡眠狀態喚醒,同時它也提供了一個即使在設備睡眠時也會運行的時鐘基準。
Android Alarm的源代碼位于:
· drivers/rtc/alarm.c;
· drivers/rtc/alarm-dev.c。
8.USB Gadget驅動
USB Gadget驅動是一個基于標準Linux USB gadget驅動框架的設備驅動,Android的USB驅動是基于gadget框架的。
USB Gadget驅動的源代碼位于:
· drivers/usb/gadget/android.c;
· drivers/usb/gadget/f_adb.c;
· drivers/usb/gadget/f_mass_storage.c。
9.Android Ram Console
為了提供調試功能,Android允許將調試日志信息寫入一個被稱為RAM Console的設備里,它是一個基于RAM的Buffer。
Android Ram Console的源代碼位于drivers/staging/android/ram_console.c。
10.Android timed device
Android timed device提供了對設備進行定時控制功能,目前僅僅支持vibrator和LED設備。
Android timed device的源代碼位于drivers/staging/android/timed_output.c(timed_gpio.c)。
11.YAFFS2文件系統
在Android系統中,采用YAFFS2作為MTD nand flash文件系統。YAFFS2是一個快速穩定的應用于NAND和NOR Flash的跨平臺的嵌入式設備文件系統,同其他Flash文件系統相比,YAFFS2使用更小的內存來保存它的運行狀態,因此它占用內存小;YAFFS2的垃圾回收非常簡單而且快速,因此能達到更好的性能;YAFFS2在大容量的NAND Flash上性能表現尤為明顯,非常適合大容量的Flash存儲。
YAFFS2文件系統源代碼位于fs/yaffs2/目錄下。
Android是在Linux的內核基礎之上運行的,提供的核心系統服務包括安全、內存管理、進程管理、網絡組和驅動模型等內容。內核部分還相當于一個介于硬件層和系統中其他軟件組之間的一個抽象層次。但是嚴格來說它不算是Linux操作系統。
4.5 第一段Android程序
本實例的功能是在手機屏幕中顯示問候語“你好我的朋友!”,在具體開始之前先做一個簡單的流程規劃,如圖4-4所示。

▲圖4-4 規劃流程圖

在本節的內容中,將詳細講解本實例的具體實現流程。
4.5.1 新建Android工程
(1)在Eclipse中依次單擊“File”|“New”|“Project”新建一個工程文件。如圖4-5所示。

▲圖4-5 新建工程文件
(2)選擇“Android Project”選項,單擊“Next”按鈕。
(3)在彈出的“New Android Project”對話框中,設置工程信息。如圖4-6所示。

▲圖4-6 設置工程
在圖4-6所示的界面中依次設置工程名稱、包名稱、Activity名稱和應用名稱。
現在已經創建了一個名為“first”的工程文件,現在打開文件first.java,會顯示自動生成的如下代碼。
package first.a; import android.app.Activity; import android.os.Bundle; public class fistMM extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } }
如果此時運行程序,將不會顯示任何內容。此時可以對上述代碼進行適當的修改,讓程序輸出“HelloWorld”。具體代碼如下所示。
package first.a; import android.app.Activity; import android.os.Bundle; import android.widget.TextView; public class fistMM extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); TextView tv = new TextView(this); tv.setText("你好我的朋友!"); setContentView(tv); } }
經過上述代碼改寫后,應該可以在屏幕中輸出“你好我的朋友!”,完全符合預期的要求。
4.5.2 調試程序
Android調試一般分為3個步驟,分別是設置斷點、Debug調試和斷點調試。
1.設置斷點
此處的設置斷點和Java中的方法是一樣的,可以通過雙擊代碼左側的區域進行斷點設置。如圖4-7所示。

▲圖4-7 設置斷點
為了調試方便,可以設置顯示代碼的行數。只需要在代碼左側的空白部分單擊右鍵,在彈出命令中選擇“Show Line Numbers”。如圖4-8所示。

▲圖4-8 顯示行數
2.Debug調試
Debug Android調試項目的方法和普通Debug Java調試項目的方法類似,唯一的不同是在選擇調試項目時選擇“Android Application”命令。具體方法是右鍵單擊項目名,在彈出命令中依次選擇“Debug As”|“Android Application”命令。如圖4-9所示。

▲圖4-9 Debug項目
3.斷點調試
可以進行單步調試,具體調試方法和調試普通Java程序的方法類似,調試界面如圖4-10所示。

▲圖4-10 調試界面
4.5.3 運行程序
將上述代碼保存后就可運行這段程序了,具體過程如下所示。
(1)右鍵單擊項目名,在彈出命令中依次選擇“Run As”|“Android Application”。如圖4-11所示。

▲圖4-11 開始調試
(2)此時工程開始運行,運行完成后在屏幕中輸出“你好我的朋友!”這段文字。如圖4-12所示。

▲圖4-12 運行結果
- Bootstrap Site Blueprints Volume II
- JavaScript 從入門到項目實踐(超值版)
- Spring技術內幕:深入解析Spring架構與設計
- BeagleBone Media Center
- Java技術手冊(原書第7版)
- Java開發入行真功夫
- AutoCAD VBA參數化繪圖程序開發與實戰編碼
- iOS編程基礎:Swift、Xcode和Cocoa入門指南
- ASP.NET程序設計教程
- RISC-V體系結構編程與實踐(第2版)
- 全棧自動化測試實戰:基于TestNG、HttpClient、Selenium和Appium
- Building RESTful Python Web Services
- 編程菜鳥學Python數據分析
- Advanced Express Web Application Development
- TMS320LF240x芯片原理、設計及應用