Android應用程序都是用Java語言編寫的。Android SDK工具會把代碼以及相關的數據和資源文件編譯成Android包,也就是後綴為.apk的文檔。一個.apk文件就是一個應用程序,並且是android系統安裝改程序的安裝文件。
一旦安裝成功,應用程序就處於自己的安全沙盒中:
Android操作系統是一個多用戶的Linux系統,其中每一個應用程序就是一個不同的用戶。
缺省情況下,系統會給每一個應用程序分配一個單獨的Linux用戶ID(該ID只能被系統感知,應用程序是不感知該ID的)。系統會給該程序中的所有文件進行訪問權限設置,以便只有用戶ID和該程序的ID一樣的用戶才可以訪問這些文件。
每一個進程都有自己的虛擬機,因此每個程序的代碼運行都是和其它程序分開的。
缺省情況下,每一個程序都是在自己的Linux進程中運行。Android系統會在該程序的任何一個部件需要執行的時候啟動該進程,並在不需要的時候或者是系統需要為別的程序收復內存的時候關閉該進程。
因此,Android系統遵循最小權限原則。也就是說,缺省情況下,每一個應用程序只能訪問自身完成工作所需的部件,而不能訪問其它別的部件。這就創建了一種非常安全的環境,其中,應用程序是不能訪問未經授權的系統部件的。
然而,系統提供了多種應用程序間共享數據和程序訪問系統設備的方式:
可以為兩個應用程序指定相同的Linux 用戶ID,此時,這兩個應用程序間是可以互相訪問對方的文件的。為了節約系統資源,擁有相同的用戶ID的應用程序可以在同一個Linux進程中運行,並共享同一個VM(應用程序必須擁有相同的認證)。
應用程序可以請求用戶的許可來訪問諸如用戶的通訊錄,短信,SD卡,攝像頭,藍牙等的設備數據。這些許可必須在安裝應用程序的時候得到用戶的確認。
上面描述到的是應用程序如何存在於系統中的基礎知識。該文檔的剩下部分將介紹:
應用程序中的核心框架部件。
應用程序的清單文件(manifest文件)。它是聲明程序部件和所需的設備特性的地方。
資源。它是和程序代碼分開的,可使程序在各種配置的設備上都能良好運行。
應用程序部件
————————————————————————————————————————————————————————————————————————————
應用程序部件是Android 程序的重要組成部分。每一個部件都是系統可以進入應用程序的一個入口點。並非所有的部件都是為了讓用戶能夠進入程序的。有些部件之間是相互依賴的。但是,每一個部件都是作為一個獨立的實體存在的,並扮演著特定的角色——每一個部件都是一個獨特應用程序的組成塊,幫助我們來定義整個應用程序的行為。
Android應用程序中有四種不同類型的部件。每種類型的部件都有著獨特的設計目的和生命周期。生命周期中定義了部件是如何被創建和銷毀的。
以下就是這四種應用程序部件:
Activities 活動
一個活動代表的就是有用戶接口的一個“屏幕”。例如,電子郵件程序中可能會使用一個Activity來顯示信郵件列表,而使用另外一個Activity來編寫電子郵件,又使用別的Activity來閱讀電子郵件。盡管這些活動在應用程序中一起工作,相互配合來完成很好的用戶體驗,但其本身則是相互獨立的。這樣一來,應用程序是可以隨時啟動其中的任何一個活動的。例如,照相機程序是可以啟動電子郵件程序中的活動來編輯新的電子郵件,以便共享照片。
活動Activity的是作為Activity類派生來來實現的。在開發者指南中可以查閱到更多的關於Activities的信息。
Services 服務
一個Service部件是在後台運行,進行長時間操作或者是為遠端進程開展工作的部件。一個服務並不提供用戶接口。例如,一個服務可以用來播放背景音樂,而此時用戶可能在使用另外的應用程序,或者一個服務可用於從網絡上獲取數據而程序不用暫停用戶和活動的交互。Activity可以啟動服務並讓之運行,或者是與之進行綁定以便和其進行交互。
一個服務是作為類Service的派生類來實現的。在開發者指南中可以查閱更多關於Service的信息。
Content Provider 內容提供者
一個Content provider是用來管理應用程序共享的數據集。數據可以被存儲在文件系統中,或者是一個SQLite數據庫中,或者是網頁中,或者是任意其它應用程序可訪問到的的永久性存儲介質中。通過content provider,其它程序可以查詢甚至修改共享的數據(如果content provider 允許的話)。例如,Android系統中提供了一個content provider來管理用戶的通訊錄。這樣一來,任何經過正確授權的程序都可以查詢其中的數據,並對其中某條通訊錄進行讀寫操作。
Content provider還可以用於讀寫程序的私有數據而非共享數據。例如,Note Pad示例程序就是使用content provider 來存儲備忘錄的。
一個content provider是作為ContentProvider類的派生類來實現的,並且必須實現標准的API集,以便能夠和其它程序進行事務處理。在開發者指南中可以查閱更多的關於Content Provider的信息。
Broadcast receiver 廣播接收器
一個Broadcast receiver 是一個能對系統廣播消息的宣告進行響應的部件。許多的廣播消息都源自系統。比如,用於宣告屏幕被關閉的廣播消息,用於宣告電池低電量的廣播消息,或者是用於宣告照了一張相片的廣播消息。應用程序也可以發起廣播——比如,發起數據已經被下載完畢,並可用的廣播,以便其它程序可以感知並響應該事件。盡管broadcast receiver沒有用戶界面,但它可以在狀態欄進行通知,以便讓用戶知道廣播事件發生的時間。更常見的是,廣播接收器是作為通往其它程序部件的大門,其中通常只做少量的工作。比如,廣播接收可能只是啟動一個服務來完成基於該事件的某些工作。
一個broadcast receiver是作為Broadcast Receiver類的派生來來實現的,其中的廣播都是通過Intent對象來分發的。更多信息,請參閱BroadcastReceiver類。
Android系統中一個很獨特的一點就是任何程序都可以啟動別的程序的部件。例如,如果你想讓用戶使用攝像頭來拍攝一張照片,很有可能另外的應用程序已經有這樣的功能了,我們的程序可以直接使用,而不用自己開發一個活動來從攝像頭獲取一張照片。甚至我們都不用把完成該功能的程序的代碼和我們的代碼合在一起。我們只需要簡單地啟動另外程序的這個活動來獲取照片即可。當活動完成時,獲取的照片就會被返回到我們的程序中,供我們使用。對於用戶來說,攝像頭就好像是我們程序的一部分一樣。
當系統啟動了一個部件,同時也就啟動了部件所在的應用程序的進程,並對所需的部件類進行了實例化。例如,如果我們的程序中啟動了用來獲取圖片的照相機程序中的活動,那麼該活動是運行在照相機程序所對應的進程中的,而不是在我們自己程序的進程中的。因此,與其它大多數系統中的應用程序不同,Android應用程序並不是只有一個入口點(比如, 沒有main()函數)。
由於每一個程序是在獨立的進程中運行的,並且限制了其它程序對該程序的文件訪問,因此我們不能直接從我們的程序中來啟動別的程序的活動。然而,Android系統是可以激活別的程序中的活動的,我們必須向系統發送消息,闡明我們想要啟動特定活動的意圖(intent)。系統就會為我們啟動該活動。
激活部件
應用程序的四種部件中有三個都是通過叫做intent異步消息來被激活的。Intents把單獨的部件和運行時的其它部件結合在一起(我們可以把intents想像成是用於向別的部件請求動作的消息器),而不管這些部件是屬於哪個程序。
一個intent就是一個Intent對象。他定義了用來描述需要激活指定的部件或者是指定類型的部件的消息——一個intent可以是明確指定的,也可以是隱式指定的。
對於活動和服務來說,一個intent就定義了需要進行的操作(比如,想要呈現(view)或者是發送(send)某些事情))及其該操作所需的數據的URI(也就是啟動該活動是所需的其它東西)。例如,可以有intent來請求一個活動顯示一張圖片或者是打開一個網頁。在某種情況下,我們需要啟動一個活動並獲取其結果,此時,活動也是可以通過intent來返回其結果的(例如,我們可以請求一個intent來讓用戶選擇一條通訊錄並將之返回給我們——其返回的intent中就包含有一個指向被選擇的通訊錄的URI)。
對於broadcast receiver來說,intent僅僅是定義了被廣播的事件(例如,一個廣播來表示設備處於低電量狀態了的intent僅僅只包含了一個動作字符串來表示“電量低”)。
別的部件類型,content provider,並不是由intent來激活的,而是由來自ContentResolver的請求激活的。內容解析器(Content resolver)負責處理與內容提供者相關的所有直接事務,以便讓與內容提供者進行事務交易的部件不必調用ContentResolver對象的方法。這就在內容提供者和請求內容的部件之間提供了一個抽象層。
激活各種部件的方法也不盡相同:
l 通過調用startActivity(),並為其傳入一個Intent,我們就可以啟動一個活動,或者是在需要活動返回結果的時候調用startActivityForResult()。
l 通過調用startService(),並為其傳入一個Intent,我們就可以啟動一個服務,或者是為其正在運行的服務發送新的指令。或者是可用通過調用bindService(),並為其傳入一個Intent來和某個服務進行綁定。
l 通過調用ContentResolver的query()方法,我們可以完成對其的一次查詢。