小提示: 如果你忘記引入 TextView 的包,可以嘗試 Ctrl-Shift-O (如果是Mac 系統Cmd-Shift-O)。 這是Eclipse 管理應用的快捷方式-它會顯示沒有找到的包然後自動為你加上。在Android 裡,使用者接口由一些稱之為視圖的不同層次的類組成。一個視圖就是一個簡單的對象。如單選框,動畫控件,一個文本框(我們的例子裡的),我們稱處理文本的這樣一個子視圖就叫TextView。這裡教你如何建立 TextView。 這裡教你如何建立TextView: TextView tv = new TextView(this); TextView 構造器就是Android 上下文的執行個體,這個上下文僅僅是指向系統的一個句柄,它提供像資源處理之類的服務。包含一些進入資料庫以及參數選擇的入口。這個活動也是繼承上下文。 HelloAndroid 類是活動的一個子類,它也是一個上下文,我們能通過this 操作TextView。 建立TextView 後,加入需要顯示的内容: tv.setText("Hello, Android"); 這裡很正常。我們建立一個TextView,然後告訴它顯示的内容。最後一步就是讓TextView 在螢幕上顯示出來,像這樣: setContentView(tv); 活動裡setContentView()的方法表明哪個視圖需要在目前UI 上被操作。如果一個活動不能調用這個方法,那麼目前就沒有界面系統顯示為一個空白螢幕。我們僅僅想顯示一些文本,是以我們将剛才建立的TextView 連接配接上 這就是Android 平台裡的“Hello,World”,當然,我們可以看下運作情況。 不使用Eclipse 建立工程 如果你不使用Eclipse(比如你喜歡其他IDE 工具,或者僅僅使用文本編輯器或者指令行工具),那麼Eclipse 的插件對你沒有作用。别擔心-它不會因為你不使用Eclipse 而使用失去任何功能。 Android 對Eclipse 的插件僅僅是Android SDK 外圍的一個工具。(這些工具如:模拟器,aapt, adb, ddms 等等都在 其他文檔) 是以,将這些工具和其他工具如“ant”編譯檔案結合也是有可能的。 Android SDK裡包含一個Python 的腳本“activitycreator.py”,它可以為你的項目建立所有的代碼以及目錄。就像ant 中的“build.xml”檔案。這允許你使用指令行編譯你的工程或者使用你自己的IDE 工具內建。 例如,就像我們剛使用Eclipse 建立的HelloAndroid 項目,你可以使用指令: activitycreator.py --out HelloAndroid com.android.hello.HelloAndroid 編譯工程的時候,你運作“ant”指令,當指令成功執行後,将在“bin”目錄裡産生一個“HelloAndroid.apk”的檔案,“apk”檔案是Android 的一個包,你可以使用“adb”工具安裝或者執行。 如果想獲得更多資訊,請閱讀網站提供的替他文檔。 Android 應用程式構成 一般情況Android 應用程式是由以下四種元件構造而成的: · 活動 · 廣播接收器 · 服務 · 内容提供器 需要注意的是,并不是每個Andorid 應用程式都必須建構這4 個元件,有些可能由這些元件的組合而成。 一旦你确定了你的應用程式中需要的元件,那麼你就應該在AndroidManifest.xml 中列出他們。 這是一個XML 配置檔案,它用于定義應用程式中需要的元件、元件的功能及必要條件等。這個檔案是必須的。詳情參見Android manifest file documentation 四種元件說明如下: 活動 活動是最基本的Andorid 應用程式元件,應用程式中,一個活動通常就是一個單獨的螢幕。每一個活動都被實作為一個獨立的類,并且從活動基類中繼承而來, 活動類将會顯示由視圖控件組成的使用者接口,并對事件做出響應。 大多數的應用是由多螢幕顯示組成。例如,一個文本資訊的應用也許有一個顯示發送消息的聯系人清單螢幕,第二個螢幕用來寫文本消息和選擇收件人, 再來一個螢幕檢視消息曆史或者消息設定操作等。 這裡每一個這樣的螢幕就是一個活動,很容易實作從一個螢幕到一個新的螢幕并且完成新的活動。 在某些情況下目前的螢幕也許需要向上一個螢幕動提供傳回值--比如讓使用者從手機中挑選一張照片傳回通訊錄做為電話撥入者的頭像。 當打開一個新的螢幕時,之前一個螢幕會被置為暫停狀态并且壓入曆史堆棧中。使用者可以通過回退操回到以前打開過的螢幕。我們可以選擇性的移除一些沒有必要保留的螢幕,因為Android 會把每個從桌面打開的程式保留在堆棧中。 Intent 和 Intent Filters 調用Android 專有類 Intent 進行構螢幕之間的切換。 Intent 是描述應用想要做什麼。Intent 資料結構兩最重要的部分是動作和動作對應的資料。典型的動作類型有:MAIN(活動的門戶)、VIEW、PICK、EDIT 等。而動作對應的資料則以URI 的形式進行表示。例如:要檢視某一個人的聯系方式,你需要建立一個動作類型為VIEW 的intent,以及一個表示這個人的URI。 與之有關系的一個類叫IntentFilter。當intent 被要求做某事的時候,intent filter 用于描述一個活動(或者BroadcastReceiver,看下面)能夠操作哪些intent。一個活動如果要顯示一個人的聯系方式時,需要聲明一個IntentFilter,這個IntentFilter 要知道怎麼去處理VIEW 動作和表示一個人的URI。 IntentFilter 需要在AndroidManifest.xml 中定義。 通過解析各種intent,從一個螢幕切換到另一個螢幕是很簡單的。當向前導航時,活動将會調用startActivity(myIntent)方法。然後,系統會在所有安裝的應用程式定義的IntentFilter 中查找,找到最比對myIntent 的Intent 對應的活動。新的活動接收到myIntent 的通知後,開始運作。當start 活動方法被調用将觸發解析myIntent 的動作,這個機制提供了兩個關鍵好處: · 活動能夠重複利用從其它元件中以Intent 的形式産生的一個請求 · 活動可以在任何時候被一個具有相同IntentFilter 的新的活動取代 廣播接收器 你可以使用BroadcastReceiver 來讓你的應用對一個外部的事件做出響應。比如:當電話呼入時,資料網絡可用時,或者到了晚上時。BroadcastReceivers 不能顯示UI,它隻能通過 NotificationManager 來通知使用者這些有趣的事情發生了。 BroadcastReceivers 既可以在AndroidManifest.xml 中注冊,也可以在代碼中使用Context.registerReceiver()進行注冊。但這些有趣的事情發生時,你的應用不必對請求調用BroadcastReceivers,系統會在需要的時候啟動你的應用,并在必要情況下觸發BroadcastReceivers。各種應用還可以通過使用Context.sendBroadcast()将它們自己的intent broadcasts 廣播給其它應用程式。 服務 一個服務是具有一段較長生命周期且沒有使用者界面的程式。比較好的一個例子就是一個正在從播放清單中播放歌曲的媒體播放器。在一個媒體播放器的應用中,應該會有多個活動,讓使用者可以選擇歌曲并播放歌曲。然而,音樂重放這個功能并沒有對應的活動,因為使用者當然會認為在導航到其它螢幕時音樂應該還在播放的。在這個例子中,媒體播放器這個活動會使用Context.startService() 來啟動一個服務,進而可以在背景保持音樂的播放。同時,系統也将保持這個服務一直執行,直到這個service 運作結束。(你可以通過閱讀Life Cycle of an Android Application 擷取更多關于服務的介紹). 另外,我們還可以通過使用Context.bindService() 方法,連接配接到一個服務上(如果這個服務還沒有運作将啟動它)。當連接配接到一個服務之後,我們還可以通過服務提供的接口與它進行通訊。拿媒體播放器這個例子來說,我們還可以進行暫停、重播等操作。 教程:一個記事本應用程式範例 本教程通過手把手教你的方式,講解如何利用Android 架構和諸多工具建立自己的手機應用。從一個預先配置好的工程檔案開始,該教程通過一個簡單記事本應用程式完整的開發過程,并輔以貫穿始終的詳盡例子,指導你如何搭建工程、組織應用邏輯以及UI,乃至接下來的編譯及運作可執行程式等等。 該教程将這個記事本應用的開發過程視作一組練習(見如下),每一個練習都由若幹步驟組成。你可以亦步亦趨地完成每個練習步驟,逐漸建立并完善自己的應用程式。這些練習提供了你實作此應用所需的——細到每一步驟的——具體範例代碼。 當你完成此教程後,一個具有實際功能的Android 應用就從你手上誕生了,并且你對Android 應用開發中的一些極為重要的概念也會有更加深刻的了解。若你想為你這個簡單的記事本應用添加更多複雜功能的話,你可以用另一方式實作的記事本程式比照你的練習代碼,具體可參看 Sample Code 文檔部分。 本教程目标讀者 該教程主要是面向有一定經驗,尤其是那些具備一定Java 程式設計語言知識的開發者。如果你之前從未寫過一個Java 應用程式的話,仍可以使用此教程,隻是學習進度稍稍慢一點罷了。 本教程假定你已熟悉了一些基本的Android 應用概念和術語。如果你對這些還不夠熟稔的話,你得将 Overview of an Android Application 好好溫故一下,才能繼續下面的學習。 同時需注意的時,該教程的內建開發環境是預裝Android 插件的Eclipse。如果你不用Eclipse,仍可做下面的這些練習和建立應用,但你屆時将不得不面對一些涉及Eclipse的步驟在非Eclipse IDE 中如何實作的問題。 Android 應用程式子產品: 應用, 任務, 程序, 和線程 在大多數作業系統裡,存在獨立的一個1 對1 的可執行檔案(如Windows 裡的exe 檔案),它可以産生程序,并能和界面圖示、應用進行使用者互動。但在Android 裡,這是不固定的,了解将這些分散的部分如何進行組合是非常重要的。 由于Android 這種可靈活變通的,在實作一個應用不同部分時你需要了解一些基礎技術: · 一個android 包 (簡稱 .apk ) ,裡面包含應用程式的代碼以及資源。這是一個應用釋出,使用者能下載下傳并安裝他們裝置上的檔案。 · 一個 任務 ,通常使用者能當它為一個“應用程式”來啟動:通常在桌面上會有一個圖示可以來啟動任務,這是一個上層的應用,可以将你的任務切換到前台來。 · 一個 程序 是一個底層的代碼運作級别的核心程序。通常.apk 包裡所有代碼運作在一個程序裡,一個程序對于一個.apk 包;然而, 程序 标簽常用來改變代碼運作的位置,可以是 全部的.apk 包 或者是獨立的 活動, 接收器, 服務, 或者 提供器元件。 任務 記住關鍵的一點:當使用者看到的“應用”,無論實際是如何處理的,它都是一個任務。如果你僅僅通過一些活動來建立一個.apk 包,其中有一個肯定是上層入口(通過動作的intent-filter 以及分android.intent.category.LAUNCHER),然後你的.apk 包就建立了一個單獨任務,無論你啟動哪個活動都會是這個任務的一部分。 一個任務,從使用者的觀點,他是一個應用程式;對開發者來講,它是貫穿活動着任務的一個或者多個視圖,或者一個活動棧。當設定Intent.FLAG_ACTIVITY_NEW_TASK标志啟動一個活動意圖時,任務就被建立了;這個意圖被用作任務的根用途,定義區分哪個任務。如果活動啟動時沒有這個标記将被運作在同一個任務裡(除非你的活動以特殊模式被啟動,這個後面會讨論)。如果你使用 FLAG_ACTIVITY_NEW_TASK 标記并且這個意圖的任務已經啟動,任務将被切換到前台而不是重新加載。 FLAG_ACTIVITY_NEW_TASK 必須小心使用:在使用者看來,一個新的應用程式由此啟動。如果這不是你期望的,你想要建立一個新的任務。另外,如果使用者需要從桌面退出到他原來的地方然後使用同樣的意圖打開一個新的任務,你需要使用新的任務标記。否則,如果使用者在你剛啟動的任務裡按桌面(HOME)鍵,而不是退出(BACK)鍵,你的任務以及任務的活動将被放在桌面程式的後面,沒有辦法再切換過去。 任務親和力(Affinities) 一些情況下Android 需要知道哪個任務的活動附屬于一個特殊的任務,即使該任務還沒有被啟動。這通過任務親和力來完成,它為任務中一個或多個可能要運作的活動提供一個獨一無二的靜态名字。預設為活動命名的任務親和力的名字,就是實作該活動.apk 包的名字。這提供一種通用的特性,對使用者來說,所有在.apk 包裡的活動都是單一應用的一部分。 當不帶 Intent.FLAG_ACTIVITY_NEW_TASK 标記啟動一個新的活動,任務親和力對新啟動的活動将沒有影響作用:它将一直運作在它啟動的那個任務裡。然而,如果使用NEW_TASK 标記,親和力會檢測已經存在的任務是否具有相同的親和力。如果是,該任務會被切換到前台,新的活動會在任務的最上面被啟動。 你可以在你的表現檔案裡的應用程式标簽裡為.apk 包裡所有的活動設定你自己的任務親和力,當然也可以為單獨的活動設定标簽。這裡有些例子示範如何使用: · 如果你的.apk 包裡包含多個使用者可啟動的上層應用程式,那麼你可能想要為每個活動配置設定不同的親和力。這裡有一個不錯的協定,你可以将不同的名字字串加上冒号附加在.apk 包名字的後面。 例如,"com.android.contacts"的親和力命名可以是"com.android.contacts:Dialer"and"com.android.contacts:ContactsList"。 · 如果你想替換一個通知,快捷鍵,或者其它能從外部啟動的應用程式的内部活動,你需要在你想替換的活動裡明确的設定任務親和力(taskAffinity)。例如,如果你想替換聯系人詳細資訊浏覽界面(使用者可以直接操作或者通過快捷方式調用),你需要設定任務親和力(taskAffinity)為“com.android.contacts”。 啟動模式以及啟動标記 你控制活動和任務通信的最主要的方法是通過設定啟動模式的屬性以及意圖相應的标記。這兩個參數能以不同的組合來共同控制活動的啟動結果,這在相應的文檔裡有描述。 這裡我們隻描述一些通用的用法以及幾種不同的組合方式。 你最通常使用的模式是singleTop(除了預設為standard 模式)。這不會對任務産生什麼影響;僅僅是防止在棧頂多次啟動同一個活動。 singleTask 模式對任務有一些影響:它能使得活動總是在新的任務裡被打開(或者将已經打開的任務切換到前台來)。使用這個模式需要加倍小心該程序是如何和系統其他部分互動的,它可能影響所有的活動。這個模式最好被用于應用程式入口活動的标記中。 (支援MAIN 活動和LAUNCHER 分類)。 singleInstance 啟動模式更加特殊,該模式隻能當整個應用隻有一個活動時使用。有一種情況你會經常遇到,其它實體(如搜尋管理器SearchManager 或者 通知管理器NotificationManager)會啟動你的活動。這種情況下,你需要使用Intent.FLAG_ACTIVITY_NEW_TASK 标記,因為活動在任務(這個應用/任務還沒有被啟動)之外被啟動。就像之前描述的一樣,這種情況下标準特性就是目前和任務和新的活動的親和性比對的任務将會切換到前台,然後在最頂端啟動一個新的活動。當然,你也可以實作其它類型的特性。 一個常用的做法就是将Intent.FLAG_ACTIVITY_CLEAR_TOP 和NEW_TASK 一起使用。這樣做,如果你的任務已經處于運作中,任務将會被切換到前台來, 在棧裡的所有的活動除了根活動,都将被清空,根活動的onNewIntent(Intent) 方法傳入意圖參數後被調用。當使用這種方法的時候 singleTop 或者 singleTask 啟動模式經常被使用,這樣目前執行個體會被置入一個新的意圖,而不是銷毀原先的任務然後啟動一個新的執行個體。 另外你可以使用的一個方法是設定活動的任務親和力為空字串(表示沒有親和力),然後設定finishOnBackground 屬性。 如果你想讓使用者給你提供一個單獨的活動描述的通知,倒不如傳回到應用的任務裡,這個比較管用。要指定這個屬性,不管使用者使用BACK還是HOME,活動都會結束;如果這個屬性沒有指定,按HOME 鍵将會導緻活動以及任務還留在系統裡,并且沒有辦法傳回到該任務裡。 請確定閱讀過文檔啟動模式屬性(launchMode attribute) 以及 意圖示記(Intent flags) ,關注這些選項的詳細資訊。 程序 在Android 中,程序是應用程式的完整實作,而不是使用者通常了解的那樣。他們主要用途很簡單: · 提高穩定性和安全性,将不信任或者不穩定的代碼移動到其他程序。 · 可将多個.apk 包運作在同一個程序裡減少系統開銷。 · 幫助系統管理資源,将重要的代碼放在一個單獨的程序裡,這樣就可以單獨銷毀應用程式的其他部分。 像前面描述的一樣,程序的屬性被用來控制那些有特殊應用元件運作的程序。注意這個屬性不能違反系統安全: 如果兩個.apk 包不能共享同一個使用者ID,卻試圖運作在通一個程序裡,這種情況是不被允許的,事實上系統将會建立兩個不同的程序。 請檢視安全相關文檔以擷取更多關于安全限制方面的資訊。 線程 每個程序包含一個或多個線程。多數情況下,Android 避免在程序裡建立多餘的線程,除非它建立它自己的線程,我們應保持應用程式的單線程性。一個重要的結論就是所有呼叫執行個體, 廣播接收器, 以及 服務的執行個體都是由這個程序裡運作的主線程建立的。 注意新的線程不是為活動,廣播接收器,服務或者内容提供器執行個體建立:這些應用程式的元件在程序裡被執行個體化(除非另有說明,都在同一個程序處理),實際上是程序的主線程。這說明當系統調用時這些元件(包括服務)不需要程序遠距離或者封鎖操作(就像網絡呼叫或者計算循環),因為這将阻止程序中的所有其他元件。你可以使用标準的線程 類或者Android 的HandlerThread 類去對其它線程執行遠端操作。 這裡有一些關于建立線程規則的例外: · 呼叫IBinder 或者IBinder 實作的接口,如果該呼叫來自其他程序,你可以通過 線程發送的IBinder 或者本地程序中的線程池呼叫它們,從程序的主線程呼叫是不可以的。特殊情況下,,呼叫一個服務 的IBinder 可以這樣處理。(雖然在服務裡呼叫方法在主線程裡已經完成。)這意味着IBinder 接口的實作必須要有一種線程安全的方法,這樣任意線程才能同時通路它。 · 呼叫由正在被調用的線程或者主線程以及IBinder 派發的内容提供器 的主方法。 被指定的方法在内容提供器的類裡有記錄。這意味着實作這些方法必須要有一種 線程安全的模式,這樣任意其它線程同時可以通路它。 · 呼叫視圖以及由視圖裡正在運作的線程組成的子類。通常情況下,這會被作為程序的主線程,如果你建立一個線程并顯示一個視窗,那麼繼承的視窗視圖将從那個線程裡啟動。 Android 應用程式的生命周期 在大多數情況下,每個Android 應用程式都運作在自己的Linux 程序中。當應用程式的某些代碼需要運作時,這個程序就被建立并一直運作下去,直到系統認為該程序不再有用為止。然後系統将回收程序占用的記憶體以便配置設定給其它的應用程式。應用程式的開發人員必須了解不同的應用程式元件(尤其是Activity, Service, 和BroadcastReceiver)是如何影響應用程式程序生命周期的,這是很重要的一件事情。不正确地使用這些元件可能會導緻系統殺死正在執行重要任務的應用程式程序。一個常見的程序生命周期bug 的例子是BroadcastReceiver, 當BroadcastReceiver在BroadcastReceiver.onReceive()方法中接收到一個Intent 時,它會啟動一個線程,然後傳回。一旦它傳回,系統将認為BroadcastReceiver 不再處于活動狀态,因而BroadcastReceiver 所在的程序也就不再有用了(除非該程序中還有其它的元件處于活動狀态)。是以,系統可能會在任意時刻殺死程序以回收記憶體。這樣做的話,程序中建立(spawned)出的那個線程也将被終止。對這個問題的解決方法是從BroadcastReceiver 啟動一個服務,讓系統知道程序中還有處于活動狀态的工作。為了決定在記憶體不足時讓系統殺死哪個程序,Android 根據每個程序中運作的元件以及元件的狀态把程序放入一個”重要性分級(importance hierarchy)”中。程序的類型包括(按重要程度排序): 1. 前台(foreground)程序,與使用者目前正在做的事情密切相關。不同的應用程式元件能夠通過不同的方法使它的宿主程序移到前台。當下面任何一個條件滿足時,可以考慮将程序移到前台: 1. 程序正在螢幕的最前端運作一個與使用者互動的Activity (它的onResume() 方法被調用) 2. 程序有一正在運作的BroadcastReceiver (它的 BroadcastReceiver.onReceive()方法正在執行) 3. 程序有一個Service,并且在Service 的某個回調函數(Service.onCreate(), Service.onStart(), 或 Service.onDestroy())内有正在執行的代碼。 1. 可見(visible)程序,它有一個可以被使用者從螢幕上看到的Activity,但不在前台(它的onPause()方法被調用)。舉例來說,如果前台的Activity 是一個對話框,以前的Activity 隐藏在對話框之後,就可能出現這種程序。這樣的程序特别重要,一般不允許被殺死,除非為了保證前台程序的運作不得不這樣做。 2. 服務(service)程序,有一個已經用startService() 方法啟動的Service。雖然這些程序使用者無法直接看到,但它們做的事情卻是使用者所關心的(例如背景MP3回放或背景網絡資料的上傳下載下傳)。是以,系統将一直運作這些程序除非記憶體不足以維持所有的前台程序和可見程序。 3. 背景(background)程序, 擁有一個目前使用者看不到的Activity(它的onStop() 方法被調用)。這些程序對使用者體驗沒有直接的影響。如果它們正确執行了Activity生命期(詳細資訊可參考Activity),系統可以在任意時刻殺死程序來回收記憶體,并提供給前面三種類型的程序使用。系統中通常有很多個這樣的程序在運作,是以要将這些程序儲存在LRU 清單中,以確定當記憶體不足時使用者最近看到的程序最後一個被殺掉。 4. 空(empty)程序,不包含任何處于活動狀态的應用程式元件。保留這種程序的唯一原因是,當下次應用程式的某個元件需要運作時,不需要重新建立程序,這樣可以提高啟動速度。 系統将以程序中目前處于活動狀态元件的重要程度為基礎對程序進行分類。請參考Activity, Service 和 BroadcastReceiver 文檔來獲得有關這些元件在程序整個生命期中是如何起作用的詳細資訊。每個程序類别的文檔較長的描述了它們是怎樣影響應用程式整個生命周期的。程序的優先級可能也會根據該程序與其它程序的依賴關系而增長。例如,如果程序A 通過在程序B 中設定Context.BIND_AUTO_CREATE 标記或使用ContentProvider 被綁定到一個服務(Service),那麼程序B 在分類時至少要被看成與程序A 同等重要。 二、開發應用程式 Android 應用構成 Android 應用是由各種各樣的元件來構成。 這些元件大部分都是松散連接配接的,準确 的說你可以把它們看成元件的聯合而非是一個單一的應用。 通常,這些元件運作在同一個系統程序裡面。你也可以在這個程序裡面建立多個線程(這是很常見的),如果必要你也可以建立獨立的子程序。不過這種情況是非常少見的,因為Android 盡力使代碼程序間透明。 以下部分是很重要的Android APIs: AndroidManifest.xml AndroidManifest.xml 是系統的控制檔案,它告訴系統如何處理你所建立的所有 頂層元件(尤其是activities,服務,Intent 接收器和後面描述的内容管理器)。舉例 來說,控制檔案就是把你的活動(Activities)要接收的Intents 連接配接在一起的“膠 水”。 活動(Activities) 活動(Activity)就是一個有生命周期的對象。 一個Activity 就是完成某些工作 的代碼塊, 如必要的話,這部分工作還可能包括對使用者UI 界面的顯示。不過 這不是必須的,有些活 動從不顯示UI 界面。典型地,你将會指定你的應用程 序中的一個活動為整個程式的入口點。 視圖(Views) 視圖(Views)可以将其自身繪制到螢幕上。Android 的使用者界面由一系列的視圖 樹 (trees of views)構成。接口都是由一組以樹的形式出現的視圖組成的。開 發者可 以通過建立一個新的視圖的方法來使用自定義的圖形處理技術(比如開 發遊戲,或者是 使用了不常用的使用者圖形(UI)視窗界面(widget))。 Intents Intents 是一個簡單的消息對象,它表示程式想做某事的“意圖”(intention)。比 如如果你的應用程式想要顯示一個網頁,那麼它通過建立一個Intent 執行個體并将其 傳遞給 系統來表示意圖浏覽這個URI。系統将定位于知道如何能處理這一 Intent 的代碼(在當 前情況下就是浏覽器),并運作之。Intents 也可以用于廣 播系統範圍内的有效事件 (例如通知事件)。 服務(Services) 服務是運作在背景的一段代碼。它可以運作在它自己的程序,也可以運作在其他 應用程 序程序的上下文(context)裡面,這取決于自身的需要.。其它的元件 可以綁定到一個服 務(Service)上面,通過遠端過程調用(RPC)來調用這 個方法。例如媒體播放器的服務, 當使用者退出媒體選擇使用者界面,她仍然希望 音樂依然可以繼續播放,這時就是由服務 (service)來保證當使用者界面關閉時 音樂繼續播放的。 通知(Notifications) 通知将以小圖示的形式呈現在狀态欄裡,使用者通過與圖示的互動式操來接收消 息。最常見 的通知包括短資訊,通話記錄,語音郵件,但是應用程式也可以創 建它們自己的通知事件。 我們推薦采用通知事件實作提醒使用者的注意。 内容管理器(ContentProviders) 内容管理器(ContentProvider)提供對裝置上資料進行通路的資料倉庫。典型 的例子就 是使用内容管理器來通路聯系人清單。你的應用程式也可以使用其它 程式通過内容管理器提 供的資料,同時你也可以定義你自己的内容管理器來向 其它應用提供資料通路服務。 存、取、提供資料 典型的桌面作業系統一般能提供一種通用的檔案系統,所有應用程式都能儲存和讀檔案,并且其他應用程式也能通路該檔案(可能需要一些通路控制設定). Android 使用不同的方式:在平台上,所有應用程式的資料(包括檔案),對該應用程式是私有的。當然,Android 也提供一種标準方法将自己的私有資料提供給其他應用程式通路。這一章節講了很多方法,描述應用如何存取資料,以及将資料提供給其他程式通路,當然,你也可以向其他應用程式請求并獲得它的資料。 Android 提供下面的方式來存取資料: 參數選擇 使用一個輕量級機制來存取基本資料類型的資料對,這是典型應用程式參數的 存儲模式。 檔案 你可以将你的檔案存儲在裝置上或者其他移動媒介上,預設情況下,其他應用 程式是不能通路這些檔案的。 資料庫 Android 有直接SQLite 資料庫的API。應用程式可以建立以及使用SQLite 資料 庫。 每個包建立的資料庫都是私有的。 資料提供 資料提供是應用程式的一個可選元件,它可以提供讀/寫應用程式私有資料的方 法。内容提供元件實作了一種标準請求資料的文法,和一種标準處理傳回值的 機制。Android 提供很多标準資料的提供方式,例如私有聯系人。 網絡 不要忘記,我們還可以使用網絡存取資料。 Android 的安全與權限 Android 是一個多程序系統,每一個應用程式(和系統的組成部分)都運作在自己的程序中。在應用程式和系統間的安全通過标準的Linux 裝置在程序級被執行,例如被配置設定給應用程式的使用者群組ID。額外的細粒度安全特性通過“許可”機制來提供,該機制能夠對一個指定程序可實作的特定操作進行限制。 安全結構 Android 安全學中的一個重要的設計點是在預設情況下應用程式沒有權限執行對其它應用程式、作業系統或使用者有害的操作。這些操作包括讀/寫使用者的隐私資料(例如聯系方式或e-mail),讀/寫其它應用程式的檔案,執行網絡通路,保持裝置活動,等等。 應用程式的程序是一個安全的沙箱。它不能幹擾其它應用程式,除非在它需要添加原有沙箱不能提供的功能時明确聲明權限。這些權限請求能夠被不同方式的操作所處理,特别的要基于證書和使用者的提示被自動的允許或禁止。權限的請求在那個應用程式中通過一個應用程式被聲明為靜态的,是以在此之後在安裝時或沒有改變時它們會預先知道。 應用程式簽名 所有的Android 應用程式(.apk 檔案)必須通過一個證書的簽名,此證書的私鑰必須被開發者所掌握。這個證書的辨別是應用程式的作者。這個證書不需要通過證書組織的簽署:Android 應用程式對于使用自簽署的證書是完全允許的和特别的。這個證書僅僅被用于與應用程式建立信任關系,不是為了大規模的控制應用程式可否被安裝。最重要的方面是通過确定能夠通路原始簽名權限和能夠共享使用者ID 的簽名來影響安全。 使用者辨別和檔案通路 安裝在裝置中的每一個Android封包件(.apk)都會被配置設定給一個屬于自己的統一的Linux使用者ID,并且為它建立一個沙箱以防止影響其它應用程式(或者其它應用程式影響它)。 使用者ID 在應用程式安裝到裝置中時被配置設定,并且在這個裝置中保持它的永久性。 因為安全執行發生在程序級,是以一些不同包中的代碼在相同程序中不能正常的運作,自從他們需要以不同Linux 使用者身份運作時。你可以使用每一個包中的 AndroidManifest.xml 檔案中的manifest 标簽屬性sharedUserId 擁有它們配置設定的相同使用者ID。通過這樣做,兩個包被視為相同的應用程式的安全問題被解決了,注意為了保持安全,僅有相同簽名(和請求相同sharedUserId 标簽)的兩個應用程式簽名将會給相同的使用者ID。 應用建立的任何檔案都會被賦予應用的使用者辨別,并且,正常情況下不能被其它包通路。當你通過getSharedPreferences(String, int), openFileOutput(String, int) 或者openOrCreateDatabase(String, int, SQLiteDatabase.CursorFactory)建立一個新檔案時, 你可以同時或分别使用 MODE_WORLD_READABLE 和 MODE_WORLD_WRITEABLE 标志允許其它包讀/寫此檔案。當設定了這些标志時,這個檔案仍然屬于你的應用程式,但是它的全局讀、寫和讀寫權限已經設定是以其它任何應用程式可以看到它。 權限命名 一個基本的Android 應用程式沒有與其相關聯的權限,意味着它不能做任何影響使用者體驗或裝置中的資料的有害操作。要利用這個裝置的保護特性,在你的應用程式需要時,你必須在AndroidManifest.xml 檔案中包含一個或更多的<uses-permission> 标簽來聲明此權限。 例如:需要監聽來自SMS 消息的應用程式将要指定如下内容: <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.app.myapp" > <uses-permission android:name="android.permission.RECEIVE_SMS" /> </manifest> 在安裝應用程式時,通過包安裝器應用程式要通過權限請求的許可,使建立在與應用程式簽名的核對下聲明對于使用者的那些權限和影響。在應用運作期間對使用者不做檢查:它要麼在安裝時被授予特定的許可,并且使用想用的特性;要麼不被授予許可,并且使得一切使用特性的嘗試失敗而不提示使用者。 例如,sendBroadcast(Intent) 方法就是當資料被發送給到每個接收器時檢查許可的, 在方法調用傳回之後,是以當許可失敗時你不會收到一個異常。然而,幾乎在所有例子中,許可失敗都會被列印到系統日志中。通常,多次的許可錯誤會産生抛回至應用程式的SecurityException 異常。 Android 系統提供的許可可以在Manifest.permission 中找到。每個引用也可以定義和Enforce 它自己的許可,是以這不是全面的所有可能的清單。 在程式操作期間,個别權限在一些地方可能被強制: · 在系統接到呼叫的時候,預防一個應用程式去執行特定的函數。 · 在啟動Activity 時,防止一個應用啟動其它應用程式的Activities。 · 發送和接收Intent 廣播時,控制誰能接收你的廣播或者誰能發送廣播給你。 · 在一個内容提供器上通路和操作時。 · 綁定或開始一個服務時。 權限的聲明和支援 為了執行你自己的權限,你必須首先在你的AndroidManifest.xml 中使用一個或多個<permission> 标簽聲明它們。 <protectionLevel> 屬性是必需的,告訴系統使用者應如何處理應用程式接到請求此權限的通知,或者在這個文檔中對這個權限的許可的描述。 <permissionGroup> 屬性是可選的,僅僅用于幫助系統為使用者顯示權限。通常,你要設定這些,向一個标準的系統組(列在android.Manifest.permission_group 中),或者在更多的情況下要自定義。它更偏向于使用一個已經存在的組,做為簡化的權限使用者界面顯示給使用者。 注意:應該為每個權限提供标簽(label) 和描述(description)。當使用者浏覽權限清單時,它們可以為使用者展示字元資源,如(android:label) 或者一個許可的詳細資訊( android:description) 。标簽(label)比較短,用幾個詞來描述該權限保護的關鍵功能。描述(description)應該是一組句子,用于描述獲得權限的使用者可以做什麼。我們寫描述的習慣是兩句話,第一句聲明權限,第二句警告使用者如果應用許可該權限時,會發生什麼不好的事情。 你可以在系統中通過shell 指令 adb shell pm list permissions 檢視權限目前 在AndroidManifest.xml 檔案中支援權限 通過 AndroidManifest.xml 檔案可以設定進階權限,以限制通路系統的所有元件或者使用應用程式。所有的這些請求都包含在你所需要的元件中的 android:permission屬性,命名這個權限可以控制通路此元件。 Activity 權限 (使用 <activity> 标簽) 限制能夠啟動與 Activity 權限相關聯的元件或應用程式。此權限在 Context.startActivity() 和 Activity.startActivityForResult() 期間要經過檢查;如果調用者沒有請求權限,那麼會為調用抛出一個安全異常 ( SecurityException )。 Service 權限(應用 <service> 标簽)限制啟動、綁定或啟動和綁定關聯服務的元件或應用程式。此權限在 Context.startService(), Context.stopService() 和 Context.bindService() 期間要經過檢查;如果調用者沒有請求權限,那麼會為調用抛出一個安全異常( SecurityException )。 BroadcastReceiver 權限(應用 <receiver> 标簽)限制能夠為相關聯的接收者發送廣播的元件或應用程式。在 Context.sendBroadcast() 傳回後此權限将被檢查,同時系統設法将廣播遞送至相關接收者。是以,權限失敗将會導緻抛回給調用者一個異常;它将不能遞送到目的地。在相同方式下,可以使 Context.registerReceiver() 支援一個權限,使其控制能夠遞送廣播至已登記節目接收者的元件或應用程式。其它的,當調用 Context.sendBroadcast() 以限制能夠被允許接收廣播的廣播接收者對象一個權限(見下文)。 ContentProvider 權限(使用 <provider> 标簽)用于限制能夠通路 ContentProvider中的資料的元件或應用程式。(Content providers 有一個重要的附加安全設施可用于它們調用被描述後的URI 權限。) 不同于其它元件,它有兩個不相連系的權限屬性要設定:android:readPermission 用于限制能夠讀取提供器的元件或應用程式,android:writePermission 用于限制能夠寫入提供器的元件或應用程式。注意,如果一個提供者的讀寫權限受保護,意思是你隻能從提供器中讀,而沒有寫權限。當你首次收回提供者(如果你沒有任何權限,将會抛出一個SecurityException 異常),那麼權限要被檢查,并且做為你在這個提供者上的執行操作。 使用 ContentResolver.query() 請求擷取讀權限; 使用 ContentResolver.insert(), ContentResolver.update() 和ContentResolver.delete() 請求擷取寫權限。在所有這些情況下,一個SecurityException異常從一個調用者那裡抛出時不會存儲請求權限結果。 發送廣播時支援權限 當發送一個廣播時你能總指定一個請求權限,此權限除了權限執行外,其它能發送Intent到一個已注冊的BroadcastReceiver 的權限均可以。 通過調用 Context.sendBroadcast() 及一些權限字元串, 為了接收你的廣播,你請求一個接收器應用程式必須持有那個權限。 注意,接收者和廣播者都能夠請求一個權限。當這樣的事發生了,對于Intent 來說,這兩個權限檢查都必須通過,為了傳遞到共同的目的地。 其它權限支援 任意一個好的粒度權限都能夠在一些調用者的一個服務中被執行。 和 Context.checkCallingPermission() method. 方法一起被完成。調用并産生一個需要的權限字元串,它将傳回一個整型,以确定目前調用程序是否被許可。注意,僅僅當你執行的調用進入到其它程序的時候這些才會被使用,通常,通過IDL 接口從一個服務釋出,或從一些其它方式通知其它程序。 這有許多其它有益的方式去檢查權限。如果你有一個其它程序的PID,你可以使用上下文的方法 Context.checkPermission(String, int, int) 檢查權限違反PID。 如果你有一個其它應用程式的包名, 你可以使用直接的包管理器方法 PackageManager.checkPermission(String, String) 去檢視特定的包是否被指定的權限所許可。 URI 權限 迄今為止,在與内容提供器共同使用時,标準權限系統描述通常是不充分的。一個内容提供器要保護它自己及讀和寫權限,當為了它們産生作用,它的直接用戶端總是需要手動的對其它應用程式指定URI。一個典型的例子是郵件應用程式中的附件。通路郵件的權限應該被保護,因為這是敏感使用者資料。可以,如果一個網址圖檔附件提供給一個圖檔檢視器,那個圖檔檢視器将沒有權限打開這個附件,因為它沒有原因去擁有一個權限進而不能通路所有的電子郵件。 對于這個問題的解決是通過網址權限:當開始一個活動或對一個活動傳回一個結果,調用者可通過設定Intent.FLAG_GRANT_READ_URI_PERMISSION 和 Intent.FLAG_GRANT_WRITE_URI_PERMISSION 中的一個或者兩個。允許接收活動權限通路在Intent 中特定的資料位址,不論它有權限通路資料在内容提供器相應的Intent 中。 這種機制允許一個公用的功能性模型使使用者互相互動(打開一個附件,從一個清單中選擇一個聯系人,等等)驅動ad-hoc 在優粒度權限的許可下。這可能是一個主要裝置,應用程式為了減少這個權限而需要,僅僅直接關系到它們的行為。 這優粒度URI 權限的許可工作,然而,請求一些協作和内容提供者保持那些URI。強烈推薦内容提供者實作這種裝置,并且通過android:grantUriPermissions 屬性或者<grant-uri-permissions> 标簽聲明支援它。 更多資訊可以在Context.grantUriPermission(), Context.revokeUriPermission(), 和Context.checkUriPermission() 方法中找到。 資源管理和多國版本 資源是外部檔案(不含代碼的檔案),它被代碼使用并在編譯時編入應用程式。Android支援不同類型的資源檔案,包括XML,PNG 以及JPEG 檔案XML 檔案根據描述的不同有不同格式。這份文檔描述可以支援什麼樣的檔案,文法,以及各種格式.源代碼以及XML 檔案将資源打包并編譯進二進制檔案,這種模式能使得資源更快得被加載。字元串也同樣被壓縮成更高效的模式。由于這些原因, Android 平台上存在不同的資源類型. 資源 Android 資源系統能跟蹤所有非代碼相關的應用程式。你可以使用 資源 類來通路應用程式的資源,資源的執行個體通常和應用程式聯系在一起,你可以通過Context.getResources()來通路。 應用程式的資源在編譯時就被編譯到應用程式二進制代碼裡。為了使用某個資源,你需要将它在代碼目錄結構裡放正确,然後編譯。作為編譯過程的一部分,産生的資源代号你可以在源代碼裡使用 -- 這允許編譯器驗證你的程式代碼和你定義的資源是否相符。 建立資源 Android 支援字元串,圖檔以及很多其他類型的資源。每個對象文法、格式以及它們存儲位置的支援,都是取決于不同類型的對象? 通常,你可以通過三種類型的檔案來建立資源:XML 檔案(除位圖以及原資料檔案),位圖檔案(對于圖檔)以及原始資料(其它類型,例如聲音檔案,等等。)。事實上,有兩種不同類型的XML 檔案,一種是編譯到包裡的,另外一種是通過aapt 來産生的資源檔案,這裡有一張包含所有資源類型, 檔案格式,檔案描述以及所有XML 檔案的詳細資訊的清單。 在項目裡,你可以在子目錄res/下建立和存儲資源檔案。Android 有一個資源編譯工具(aapt),它可以編譯在這個目錄下所有的子目錄中的資源,這裡有個各種資源的清單。你可以從 資源引用 這裡看到各種類型的對象,包含其文法以及格式。 路徑 資源類型res/anim/ XML 檔案被編譯進 逐幀動畫 或 補間動畫 的對象res/drawable/.png, .9.png, .jpg files 這些類型的檔案被編譯進下列這些圖表資源清單為了獲得這些資源的類型,使用Resource.getDrawable(id) · 位圖檔案 · 9-patches (可改變尺寸的圖像) res/layout/ 可編譯成螢幕布局的XML 檔案 (或者螢幕的一部分). 檢視 布局 res/values/ 可編譯成多種類型資源的檔案 注意: 不像其他 res/ 檔案夾,它能容納任何數量的檔案,但隻是描述其建立而不是資源本身. XML 的元素類型可以決定這些資源在R.class 裡什麼位置被替換 .檔案可以被命名為任何名字,檔案夾裡有一些典型的檔案(一般約定檔案以定義的元素類型後面部分為檔案名):: · arrays.xml 定義數組 · colors.xml 定義 顔色 和 顔色字串數值. 你可以使用 Resources.getDrawable() 以及Resources.getColor(), respectively, 取得這些資源.· dimens.xml 定義 尺寸資料 . 使用Resources.getDimension() 取得這些資源。· strings.xml 定義字元串數值 (使用Resources.getString 或Resources.getText()取得資源,(後者更好一點)getText() 能取到在使用者界面上顯示的文本框裡的文本。· styles.xml 定義類型 對象。res/xml/ 任何XML 檔案可以進行編譯,并能在運作時調用Resources.getXML() 顯示XML 原檔案。 res/raw/ 這裡的任何檔案都将直接被複制到裝置上。編譯産品時,這些數 據不會被編譯,它們被直接加入到程式包裡。 為了在程式中使用這些資源,你可以調用Resources.openRawResource() , 參數為 ID: R.raw.somefilename. 資源最終會被編譯成APK 檔案,Android 建立一個包裝類,命名為R,這樣你能做你的代碼裡使用這些資源類。根據資源路徑和檔案名的不同,R 包含很多子類。 全局資源 · 一些資源類允許你定義顔色。它能接受多種網絡類型的值 -- 你可以寫成 #RGB, #ARGB, #RRGGBB, #AARRGGBB 這樣16 進制常數都可以。 · 所有的顔色都可以設定一個阿爾法值,開始的兩個16 進制數指定為透明。 0 在阿爾法值裡意味着透明。當然,預設值是不透明的。 使用資源 編譯時,Android 産生一個叫R 的類,它指向你程式中所有的資源。這個類包含很多子類。每一種都是Android 支援的,同時,編譯後會産生一個資源檔案。每個類提供一個或多個編譯後資源的辨別符,你可以在代碼中使用。下面是個源代碼的檔案,裡面包含了字元串,布局檔案(全屏或者部分螢幕),以及圖像資源。 注意: 這個R 類是自動産生的,你不能手動編寫。當資源變化的時候它會自動更新。 在代碼中使用資源 隻要知道資源的ID 以及你編譯進目标檔案的資源類型就可以在代碼裡使用它來。下面是一些文法: R.resource_type.resource_name 或者 android.R.resource_type.resource_name resource_type 是R 子類的一種類型。 resource_name 是定義在XML 檔案裡的資源名或者為其他檔案類型定義的資源檔案(沒有字尾)名。每種類型的資源會被加入到一個特定的R 的子類中;為了學習哪種R 的子類裡有你編譯的資源類型,參考資源引用 文檔。被編譯進應用程式的資源不需要包的名字就可以直接被通路到(像這樣: R.resource_type.resource_name). Android 包含一些标準資源,如螢幕的類型,按鈕的背景。要使用這些代碼,你需要包含 android, 如 android.R.drawable.button_background. 這裡有一些好的和糟糕的例子說明如何在代碼裡使用編譯後的資源: // 從畫圖資源類裡裝載一個目前螢幕背景。 this.getWindow().setBackgroundDrawableResource(R.drawable.my_background_image); // 錯誤! 将一個資源ID 裝入一個需要字元串的方法中 this.getWindow().setTitle(R.string.main_title); //正确!需要從資源封裝類裡取得标題。 this.getWindow().setTitle(Resources.getText(R.string.main_title)); // 從目前螢幕中裝載布局資料。 setContentView(R.layout.main_screen); //從ViewFlipper 對象中設定動畫中一幀 。 mFlipper.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.hyperspace_in)); // 在TextView 對象中設定文本内容。 TextView msgTextView = (TextView)findViewByID(R.id.msg); msgTextView.setText(R.string.hello_message); 資源引用 一個在屬性(或者資源)裡提供的數值可以被指向一個具體的資源。這常常被使用在布局檔案中用于字元串(可以被本地化) 以及圖檔(存在于其他檔案中的),通過一個引用可以是包括顔色和整數的任何資源類型。 例如,如果有 顔色資源, 我們可以将文本的顔色值寫在布局檔案中,顔色值可以從資源檔案裡取得: <?xml version="1.0" encoding="utf-8"?> <EditText id="text" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:textColor="@color/opaque_red" android:text="Hello, World!" /> 注意這裡使用‘@’的字首是說明資源引用 -- 後面的文本是資源的名字 @[package:]type/name. 這裡我們不需要指定包,因為我們在我們自己的包裡引用資源。為了指定一個系統資源,你需要這樣寫: <?xml version="1.0" encoding="utf-8"?> <EditText id="text" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:textColor="@android:color/opaque_red" android:text="Hello, World!" /> 另外一個例子,當你在布局檔案裡使用字元串,你必須做資源引用,這樣字元串才能被使用: <?xml version="1.0" encoding="utf-8"?> <EditText id="text" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:textColor="@android:color/opaque_red" android:text="@string/hello_world" /> 這段代碼也能被用來建立資源間引用。例如,我們能這樣建立圖像資源: <?xml version="1.0" encoding="utf-8"?> <resources> <drawable id="my_background">@android:drawable/theme2_background</drawab le> </resources> 主題屬性引用 另一種資源數值允許你引用目前主題屬性值。這種屬性引用隻能被用于特殊的資源類以及XML 屬性中;它允許你根據現在主題風格将你定制的UI 變得更标準化,而不用使用大量的具體數值。 這裡有個例子,我們能在布局檔案中将文本顔色設定為基本系統主題中定義好的标準顔色: <?xml version="1.0" encoding="utf-8"?> <EditText id="text" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:textColor="?android:textDisabledColor" android:text="@string/hello_world" /> 注意除來我們将字首'?'代替了'@',其他非常像資源引用。當你使用這個标記,系統會自動查找你提供的屬性的名字 -- 資源工具知道肯定會有資源屬性相符合,你不需要詳細指定(?android:attr/android:textDisabledColor). 使用資源辨別符到主題裡去尋找相應的資料而不是直接使用原資料,其文法和'@'模式是一樣的: ?[namespace:]type/name 這裡的type 是可選擇的. 使用系統資源 許多系統資源應用程式是可以使用的。這樣的資源定義在"android.R"的類裡。 例如,你可以使用下面的代碼在螢幕上顯示一個标準的應用程式圖示: public class MyActivity extends Activity { public void onStart() { requestScreenFeatures(FEATURE_BADGE_IMAGE); super.onStart(); setBadgeResource(android.R.drawable.sym_def_app_icon); } } 用相似的方法,這段代碼能将你的螢幕變成系統定義的标準的“綠色背景”: public class MyActivity extends Activity { public void onStart() { super.onStart(); setTheme(android.R.style.Theme_Black); } } 對于不同的語言和設定支援不同的資源 你可以根據産品界面語言以及硬體配置設定不同的資源。注意,雖然你可以包含不同的字串,布局以及其他資源,但開發包(SDK)不會給你顯式的方法去指定不同的資源去加載。Android 檢測你的硬體以及位置資訊選擇合适的設定去加載。使用者可以到裝置上的設定界面去選擇不同的語言。 要包含不同的資源,在同一目錄下建立并行的檔案夾,在每個檔案夾後加上合适的名字,這個名字能表明一些配置資訊(如語言,原始螢幕等等)。例如,這裡的項目字元串檔案一個是英文版的,另一個是法文版的: MyApp/ res/ values-en/ strings.xml values-fr/ strings.xml Android 支援不同類型的修飾語,并可以加多條在檔案夾名的後面, 修飾語之間以破折号分開。例如:一個繪圖資源類指定全部配置名稱命名會像這樣: MyApp/ res/ drawable-en-rUS-port-160dpi-finger-keysexposed-qwerty-dpad-480 x320/ 更典型的,你可以僅僅指定部分特定的配置選項。隻要保證所有的數值都是按順序排列: MyApp/ res/ drawable-en-rUS-finger/ drawable-port/ drawable-port-160dpi/ drawable-qwerty/ 修飾語 值 語言 兩個小寫字母 ISO 639-1。例如: en, fr, es 地區 兩個大寫字母加上一個小寫字母'r' ISO 3166-1-alpha-2。 例如: rUS, rFR, rES 螢幕方向 port, land, square 螢幕像素 92dpi, 108dpi, 等等。 觸摸屏類型 notouch, stylus, finger 鍵盤是否有效 keysexposed, keyshidden 基本文本輸入模式 nokeys, qwerty, 12key 無觸摸屏的主要導航模式 notouch, dpad, trackball, wheel 螢幕分辨率 320x240, 640x480, 等等。大分辨率需要開始指定。 這個清單不包含一些特殊的參數,如載體,商标,裝置/硬體,制造商。任何應用程式需要知道的資訊都在資源修飾語裡有說明。 這裡有一些通用的關于資源目錄的命名指導: · 各個變量用破折号分開 (每個基本的目錄名後跟一個破折号) · 變量大小寫敏感(其大小寫法必須始終一緻)例如, o 一個drawable 的目錄必須命名為 drawable-port, 而不是drawable-PORT。 o 你不能有兩個目錄命名為 drawable-port 以及 drawable-PORT, 甚至故意将"port" 和 "PORT"指為不同的參數也不可以。 · 一個式子裡同一個類型修飾語中隻有一個值是有效的(你不能指定像這樣 drawable-rEN-rFR/) · 你可以指定多個參數去定義不同的配置,但是參數必須是上面表格裡的。例如, drawable-en-rUS-land 意思在US-English 的機器裡載入風景視圖。 · Android 會尋找最适合目前配置的目錄,這會在下面描述 · 表格裡所列的參數是用來打破平衡以防止多重路徑限制。 (看下面的例子) · 所有目錄,無論是限制的,還是不限制的,隻要在 res/ 目錄下.一些目錄是不能嵌套的(這樣 res/drawable/drawable-en 是不可以的) · 所有的資源在被代碼引用中最好都使用簡單的、不加修飾的名字,如果一個資源這樣命名: MyApp/res/drawable-port-92dp/myimage.png 它将這樣被引用: R.drawable.myimage (code) @drawable/myimage (XML) Android 如何找到最合适的目錄 Android 将會挑出哪些基本資源檔案在運作時會被使用,這依靠目前的配置。 選擇過程如下: 1. 删去一些和目前裝置配置不符合的資源。例如,如果螢幕的像素是108dpi,這可以删除 MyApp/res/drawable-port-92dpi/. 2. MyApp/res/drawable/myimage.png 3. MyApp/res/drawable-en/myimage.png 4. MyApp/res/drawable-port/myimage.png 5. MyApp/res/drawable-port-92dpi/myimage.png 6. 挑出一些最經常符合配置的資源。例如,如果我們的地區是 en-GB, 方向是 port,那我們有兩個符合配置的選項: MyApp/res/drawable-en/ 和 MyApp/res/drawable-port/. 這個目錄 MyApp/res/drawable/ 可以被 删除了,因為當另外一個有一次比對正确,而它沒有。 7. MyApp/res/drawable/myimage.png 8. MyApp/res/drawable-en/myimage.png 9. MyApp/res/drawable-port/myimage.png 10. 根據配置的優先級選取最終适合的檔案,它們按順利被排列在上面的表格裡。更确切得說,語言比對比方位比對更重要, 是以我們可以通過選擇語言檔案來平衡,MyApp/res/drawable-en/. 11. MyApp/res/drawable-en/myimage.png 12. MyApp/res/drawable-port/myimage.png 術語 資源系統将一系列分散内容集合在一起形成最終的完整的資源功能,去幫助我們了解整個系統。這裡有一些核心概念以及元件的概要說明,你在開發中将可能使用到: 最終檔案: 應用程式的獨立的資料包。這包含所有從java 程式編譯成的目标檔案,圖像(例如PNG 圖檔), XML 檔案等等。這些檔案以一種特定的方式組織在一起,在程式打包最後時,它們被打包成一個獨立的ZIP 檔案。 aapt: Android 最終檔案打包工具。這個工具産生最終程式的ZIP 檔案。除了将最終的中繼資料檔案打包在一起,它也解析資源定義到最終的二進制資料裡。 資源表:aapt 工具産生的特殊的檔案,描述了所有在程式/包裡的資源。這個檔案可以通過資源類來通路;它不能直接和應用程式接觸。 資源: 資源表裡一條記錄描述的是單一的命名值。大體上, 資源分成兩種:基本的和包裝的.資源辨別符: 在資源表裡所有的資源都被唯一的整數辨別着。所有的代碼中(資源描述,XML 檔案,Java 源代碼)你可以直接使用符号名代替真實的整數數值。 基本資源: 所有基本資源都可以被寫成一個簡單的字串,使用一定的格式可以描述資源系統裡各種不同的基本類型:整數,顔色,字串,其他資源的引用,等等。像圖檔以及XML 描述檔案這些複雜資源,被以基本字串資源儲存,它們的值就是相關最終資料檔案的路徑。 包裝資源: 有一種特殊類型的資源,不是簡單的字元串,而是有一個随意的名字/數值配對清單。每個數值可以對應它本身的資源辨別,每個值可以持相同類型的字元串格式的資料作為一個正常的資源。包裝資源支援繼承:一個包裡的資料能從其他包裡繼承,有選擇地替換或者擴充能産生你自己需要的内容。 種類: 資源種類是對于不同需求的資源辨別符而言的。例如,繪制資源類常常執行個體化繪制類的對象,是以這些包含顔色以及指向圖檔或XML 檔案的字元串路徑資料是原始資料。其它常見資源類型是字元串(本地化字元串),顔色(基本顔色),布局(一個指向XML 檔案的字串路徑,它描述的是一個使用者界面)以及風格(一個描述使用者接口屬性的包裝資源)。還有一個标準的“attr”資源類型,它定義了命名包裝資料以及XML 屬性的資源辨別符。 風格: 包含包裝資源類型的名字常常用來描述一系列使用者接口屬性。例如,一個 TextView 的類可能會有一個描述界面風格的類來定義文本大小,顔色以及對齊方式。 在一個界面布局的XML 檔案中,可以使用“風格” 屬性來确定整體界面風格,它的值就是風格資源的名字。 風格類: 這裡将詳述一些屬性資源類。其實資料不會被放在資源表本身,通常在源代碼裡它以常量的形式出現,這也可以使你在風格類或者XML 的标簽屬性裡友善找到它的值。例如,Android 平台裡定義了一個“視圖”的風格類,它包含所有标準視圖的屬性: 畫圖區域,可視區域,背景等。這個視圖被使用時,它就會借助風格類去從XML 檔案取得資料并将其載入到執行個體中。 配置: 對許多特殊的資源辨別符,根據目前的配置,可以有多種不同的值。配置包括地區(語言和國家),螢幕方向,螢幕分辨率,等等。目前的配置用來選擇當資源表載入時哪個資源值生效。 主題: 一個标準類型的資源能為一個特殊的上下文提供全局的屬性值。例如,當應用工程師寫一個活動時,他能選擇一個标準的主題去使用,白色的或者黑色的;這個類型能提供很多資訊,如螢幕背景圖檔/顔色,預設文本顔色,按鈕類型,文本編輯框類型,文本大小,等。當布置一個資源布局時,控件(文本顔色,選中後顔色,背景)的大部分設定值取自目前主題;如果需要,布局中的風格以及屬性也可以從主題的屬性中獲得。 覆寫層: 資源表不能定義新類型的資源,但是你可以在其他表裡替換資源值。就像配置值,這可以在裝載時候進行;它能加入新的配置值(例如,改變字串到新的位置),替換現有值(例如,将标準的白色背景替換成"Hello Kitty"的背景圖檔),修改資源包(例如修改主題的字型大小。白色主題字型大小為18pt)。這實際上允許使用者選擇裝置不同的外表,或者下載下傳新的外表檔案。 資源引用 資源引用 這份文檔提供了不同類型資源的詳細清單,并提供了如何在Java 代碼中使用資源以及如何引用資源的描述。 國際化和本地化 即将完成: 國際化和本地化是非常關鍵的,但現在的SDK 還沒有完全支援好。當SDK成熟時,這個章節會包含Android 平台國際化和本地化的相關資訊。 那時,外部字串以及良好的結構将會使得建立和使用資源變得更省事。 |