小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

android 研究筆記

 勤奮不止 2013-08-06

一般情況Android 應(yīng)用程序是由以下四種組件構(gòu)造而成的:

· 活動
· 廣播接收器
· 服務(wù)
· 內(nèi)容提供器
需要注意的是,并不是每個Andorid 應(yīng)用程序都必須構(gòu)建這4 個組件,有些可能由這些組件的組合而成。
一旦你確定了你的應(yīng)用程序中需要的組件,那么你就應(yīng)該在AndroidManifest.xml 中列出他們。 這是一個XML 配置文件,它用于定義應(yīng)用程序中需要的組件、組件的功能及必要條件等。這個文件是必須的。詳情參見Android manifest file documentation
四種組件說明如下:
活動
活動是最基本的Andorid 應(yīng)用程序組件,應(yīng)用程序中,一個活動通常就是一個單獨的屏幕。每一個活動都被實現(xiàn)為一個獨立的類,并且從活動基類中繼承而來, 活動類將會顯示由視圖控件組成的用戶接口,并對事件做出響應(yīng)。 大多數(shù)的應(yīng)用是由多屏幕顯示組成。例如,一個文本信息的應(yīng)用也許有一個顯示發(fā)送消息的聯(lián)系人列表屏幕,第二個屏幕用來寫文本消息和選擇收件人, 再來一個屏幕查看消息歷史或者消息設(shè)置操作等。
這里每一個這樣的屏幕就是一個活動,很容易實現(xiàn)從一個屏幕到一個新的屏幕并且完成新的活動。 在某些情況下當(dāng)前的屏幕也許需要向上一個屏幕動提供返回值--比如讓用戶從手機中挑選一張照片返回通訊錄做為電話撥入者的頭像。
當(dāng)打開一個新的屏幕時,之前一個屏幕會被置為暫停狀態(tài)并且壓入歷史堆棧中。用戶可以通過回退操回到以前打開過的屏幕。我們可以選擇性的移除一些沒有必要保留的屏幕,因為Android 會把每個從桌面打開的程序保留在堆棧中。
Intent 和 Intent Filters
調(diào)用Android 專有類 Intent 進行構(gòu)屏幕之間的切換。 Intent 是描述應(yīng)用想要做什么。Intent 數(shù)據(jù)結(jié)構(gòu)兩最重要的部分是動作和動作對應(yīng)的數(shù)據(jù)。典型的動作類型有:MAIN(活動的門戶)、VIEW、PICK、EDIT 等。而動作對應(yīng)的數(shù)據(jù)則以URI 的形式進行表示。例如:要查看某一個人的聯(lián)系方式,你需要創(chuàng)建一個動作類型為VIEW 的intent,以及一個表示這個人的URI。
與之有關(guān)系的一個類叫IntentFilter。當(dāng)intent 被要求做某事的時候,intent filter 用于描述一個活動(或者BroadcastReceiver,看下面)能夠操作哪些intent。一個活動如果要顯示一個人的聯(lián)系方式時,需要聲明一個IntentFilter,這個IntentFilter 要知道怎么去處理VIEW 動作和表示一個人的URI。 IntentFilter 需要在AndroidManifest.xml 中定義。
通過解析各種intent,從一個屏幕切換到另一個屏幕是很簡單的。當(dāng)向前導(dǎo)航時,活動將會調(diào)用startActivity(myIntent)方法。然后,系統(tǒng)會在所有安裝的應(yīng)用程序定義的IntentFilter 中查找,找到最匹配myIntent 的Intent 對應(yīng)的活動。新的活動接收到myIntent 的通知后,開始運行。當(dāng)start 活動方法被調(diào)用將觸發(fā)解析myIntent 的動作,這個機制提供了兩個關(guān)鍵好處:
· 活動能夠重復(fù)利用從其它組件中以Intent 的形式產(chǎn)生的一個請求
· 活動可以在任何時候被一個具有相同IntentFilter 的新的活動取代
廣播接收器
你可以使用BroadcastReceiver 來讓你的應(yīng)用對一個外部的事件做出響應(yīng)。比如:當(dāng)電話呼入時,數(shù)據(jù)網(wǎng)絡(luò)可用時,或者到了晚上時。BroadcastReceivers 不能顯示UI,它只能通過 NotificationManager 來通知用戶這些有趣的事情發(fā)生了。
BroadcastReceivers 既可以在AndroidManifest.xml 中注冊,也可以在代碼中使用Context.registerReceiver()進行注冊。但這些有趣的事情發(fā)生時,你的應(yīng)用不必對請求調(diào)用BroadcastReceivers,系統(tǒng)會在需要的時候啟動你的應(yīng)用,并在必要情況下觸發(fā)BroadcastReceivers。各種應(yīng)用還可以通過使用Context.sendBroadcast()將它們自己的intent broadcasts 廣播給其它應(yīng)用程序。
服務(wù)
一個服務(wù)是具有一段較長生命周期且沒有用戶界面的程序。比較好的一個例子就是一個正在從播放列表中播放歌曲的媒體播放器。在一個媒體播放器的應(yīng)用中,應(yīng)該會有多個活動,讓使用者可以選擇歌曲并播放歌曲。然而,音樂重放這個功能并沒有對應(yīng)的活動,因為使用者當(dāng)然會認(rèn)為在導(dǎo)航到其它屏幕時音樂應(yīng)該還在播放的。在這個例子中,媒體播放器這個活動會使用Context.startService() 來啟動一個服務(wù),從而可以在后臺保持音樂的播放。同時,系統(tǒng)也將保持這個服務(wù)一直執(zhí)行,直到這個service 運行結(jié)束。(你可以通過閱讀Life Cycle of an Android Application 獲取更多關(guān)于服務(wù)的介紹). 另外,我們還可以通過使用Context.bindService() 方法,連接到一個服務(wù)上(如果這個服務(wù)還沒有運行將啟動它)。當(dāng)連接到一個服務(wù)之后,我們還可以通過服務(wù)提供的接口與它進行通訊。拿媒體播放器這個例子來說,我們還可以進行暫停、重播等操作。
教程:一個記事本應(yīng)用程序范例
本教程通過手把手教你的方式,講解如何利用Android 框架和諸多工具建立自己的手機應(yīng)用。從一個預(yù)先配置好的工程文件開始,該教程通過一個簡單記事本應(yīng)用程序完整的開發(fā)過程,并輔以貫穿始終的詳盡例子,指導(dǎo)你如何搭建工程、組織應(yīng)用邏輯以及UI,乃至接下來的編譯及運行可執(zhí)行程序等等。
該教程將這個記事本應(yīng)用的開發(fā)過程視作一組練習(xí)(見如下),每一個練習(xí)都由若干步驟組成。你可以亦步亦趨地完成每個練習(xí)步驟,逐步建立并完善自己的應(yīng)用程序。這些練習(xí)提供了你實現(xiàn)此應(yīng)用所需的——細到每一步驟的——具體范例代碼。
當(dāng)你完成此教程后,一個具有實際功能的Android 應(yīng)用就從你手上誕生了,并且你對Android 應(yīng)用開發(fā)中的一些極為重要的概念也會有更加深刻的理解。若你想為你這個簡單的記事本應(yīng)用添加更多復(fù)雜功能的話,你可以用另一方式實現(xiàn)的記事本程序比照你的練習(xí)代碼,具體可參看 Sample Code 文檔部分。
本教程目標(biāo)讀者
該教程主要是面向有一定經(jīng)驗,尤其是那些具備一定Java 編程語言知識的開發(fā)者。如果你之前從未寫過一個Java 應(yīng)用程序的話,仍可以使用此教程,只是學(xué)習(xí)進度稍稍慢一點罷了。
本教程假定你已熟悉了一些基本的Android 應(yīng)用概念和術(shù)語。如果你對這些還不夠熟稔的話,你得將 Overview of an Android Application 好好溫故一下,才能繼續(xù)下面的學(xué)習(xí)。
同時需注意的時,該教程的集成開發(fā)環(huán)境是預(yù)裝Android 插件的Eclips教程e。如果你不用Eclipse,仍可做下面的這些練習(xí)和建立應(yīng)用,但你屆時將不得不面對一些涉及Eclipse的步驟在非Eclipse IDE 中如何實現(xiàn)的問題。
Android 應(yīng)用程序模塊: 應(yīng)用, 任務(wù), 進程, 和線程
在大多數(shù)操作系統(tǒng)里,存在獨立的一個1 對1 的可執(zhí)行文件(如Windows 里的exe 文件),它可以產(chǎn)生進程,并能和界面圖標(biāo)、應(yīng)用進行用戶交互。但在Android 里,這是不固定的,理解將這些分散的部分如何進行組合是非常重要的。
由于Android 這種可靈活變通的,在實現(xiàn)一個應(yīng)用不同部分時你需要理解一些基礎(chǔ)技術(shù):
· 一個android 包 (簡稱 .apk ) ,里面包含應(yīng)用程序的代碼以及資源。這是一個應(yīng)用發(fā)布,用戶能下載并安裝他們設(shè)備上的文件。
· 一個 任務(wù) ,通常用戶能當(dāng)它為一個“應(yīng)用程序”來啟動:通常在桌面上會有一個圖標(biāo)可以來啟動任務(wù),這是一個上層的應(yīng)用,可以將你的任務(wù)切換到前臺來。
· 一個 進程 是一個底層的代碼運行級別的核心進程。通常.apk 包里所有代碼運行在一個進程里,一個進程對于一個.apk 包;然而, 進程 標(biāo)簽常用來改變代碼運行的位置,可以是 全部的.apk 包 或者是獨立的 活動, 接收器, 服務(wù), 或者 提供器組件。
任務(wù)
記住關(guān)鍵的一點:當(dāng)用戶看到的“應(yīng)用”,無論實際是如何處理的,它都是一個任務(wù)。如果你僅僅通過一些活動來創(chuàng)建一個.apk 包,其中有一個肯定是上層入口(通過動作的intent-filter 以及分android.intent.category.LAUNCHER),然后你的.apk 包就創(chuàng)建了一個單獨任務(wù),無論你啟動哪個活動都會是這個任務(wù)的一部分。
一個任務(wù),從使用者的觀點,他是一個應(yīng)用程序;對開發(fā)者來講,它是貫穿活動著任務(wù)的一個或者多個視圖,或者一個活動棧。當(dāng)設(shè)置Intent.FLAG_ACTIVITY_NEW_TASK標(biāo)志啟動一個活動意圖時,任務(wù)就被創(chuàng)建了;這個意圖被用作任務(wù)的根用途,定義區(qū)分哪個任務(wù)。如果活動啟動時沒有這個標(biāo)記將被運行在同一個任務(wù)里(除非你的活動以特殊模式被啟動,這個后面會討論)。如果你使用 FLAG_ACTIVITY_NEW_TASK 標(biāo)記并且這個意圖的任務(wù)已經(jīng)啟動,任務(wù)將被切換到前臺而不是重新加載。
FLAG_ACTIVITY_NEW_TASK 必須小心使用:在用戶看來,一個新的應(yīng)用程序由此啟動。如果這不是你期望的,你想要創(chuàng)建一個新的任務(wù)。另外,如果用戶需要從桌面退出到他原來的地方然后使用同樣的意圖打開一個新的任務(wù),你需要使用新的任務(wù)標(biāo)記。否則,如果用戶在你剛啟動的任務(wù)里按桌面(HOME)鍵,而不是退出(BACK)鍵,你的任務(wù)以及任務(wù)的活動將被放在桌面程序的后面,沒有辦法再切換過去。
任務(wù)親和力(Affinities)
一些情況下Android 需要知道哪個任務(wù)的活動附屬于一個特殊的任務(wù),即使該任務(wù)還沒有被啟動。這通過任務(wù)親和力來完成,它為任務(wù)中一個或多個可能要運行的活動提供一個獨一無二的靜態(tài)名字。默認(rèn)為活動命名的任務(wù)親和力的名字,就是實現(xiàn)該活動.apk 包的名字。這提供一種通用的特性,對用戶來說,所有在.apk 包里的活動都是單一應(yīng)用的一部分。
當(dāng)不帶 Intent.FLAG_ACTIVITY_NEW_TASK 標(biāo)記啟動一個新的活動,任務(wù)親和力對新啟動的活動將沒有影響作用:它將一直運行在它啟動的那個任務(wù)里。然而,如果使用NEW_TASK 標(biāo)記,親和力會檢測已經(jīng)存在的任務(wù)是否具有相同的親和力。如果是,該任務(wù)會被切換到前臺,新的活動會在任務(wù)的最上面被啟動。
你可以在你的表現(xiàn)文件里的應(yīng)用程序標(biāo)簽里為.apk 包里所有的活動設(shè)置你自己的任務(wù)親和力,當(dāng)然也可以為單獨的活動設(shè)置標(biāo)簽。這里有些例子演示如何使用:
· 如果你的.apk 包里包含多個用戶可啟動的上層應(yīng)用程序,那么你可能想要為每個活動分配不同的親和力。這里有一個不錯的協(xié)定,你可以將不同的名字字串加上冒號附加在.apk 包名字的后面。 例如,"com.android.contacts"的親和力命名可以是"com.android.contacts:Dialer"and"com.android.contacts:ContactsList"。
· 如果你想替換一個通知,快捷鍵,或者其它能從外部啟動的應(yīng)用程序的內(nèi)部活動,你需要在你想替換的活動里明確的設(shè)置任務(wù)親和力(taskAffinity)。例如,如果你想替換聯(lián)系人詳細信息瀏覽界面(用戶可以直接操作或者通過快捷方式調(diào)用),你需要設(shè)置任務(wù)親和力(taskAffinity)為“com.android.contacts”。
啟動模式以及啟動標(biāo)記
你控制活動和任務(wù)通信的最主要的方法是通過設(shè)置啟動模式的屬性以及意圖相應(yīng)的標(biāo)記。這兩個參數(shù)能以不同的組合來共同控制活動的啟動結(jié)果,這在相應(yīng)的文檔里有描述。
這里我們只描述一些通用的用法以及幾種不同的組合方式。
你最通常使用的模式是singleTop(除了默認(rèn)為standard 模式)。這不會對任務(wù)產(chǎn)生什么影響;僅僅是防止在棧頂多次啟動同一個活動。
singleTask 模式對任務(wù)有一些影響:它能使得活動總是在新的任務(wù)里被打開(或者將已經(jīng)打開的任務(wù)切換到前臺來)。使用這個模式需要加倍小心該進程是如何和系統(tǒng)其他部分交互的,它可能影響所有的活動。這個模式最好被用于應(yīng)用程序入口活動的標(biāo)記中。
(支持MAIN 活動和LAUNCHER 分類)。
singleInstance 啟動模式更加特殊,該模式只能當(dāng)整個應(yīng)用只有一個活動時使用。有一種情況你會經(jīng)常遇到,其它實體(如搜索管理器SearchManager 或者 通知管理器NotificationManager)會啟動你的活動。這種情況下,你需要使用Intent.FLAG_ACTIVITY_NEW_TASK 標(biāo)記,因為活動在任務(wù)(這個應(yīng)用/任務(wù)還沒有被啟動)之外被啟動。就像之前描述的一樣,這種情況下標(biāo)準(zhǔn)特性就是當(dāng)前和任務(wù)和新的活動的親和性匹配的任務(wù)將會切換到前臺,然后在最頂端啟動一個新的活動。當(dāng)然,你也可以實現(xiàn)其它類型的特性。
一個常用的做法就是將Intent.FLAG_ACTIVITY_CLEAR_TOP 和NEW_TASK 一起使用。這樣做,如果你的任務(wù)已經(jīng)處于運行中,任務(wù)將會被切換到前臺來, 在棧里的所有的活動除了根活動,都將被清空,根活動的onNewIntent(Intent) 方法傳入意圖參數(shù)后被調(diào)用。當(dāng)使用這種方法的時候 singleTop 或者 singleTask 啟動模式經(jīng)常被使用,這樣當(dāng)前實例會被置入一個新的意圖,而不是銷毀原先的任務(wù)然后啟動一個新的實例。
另外你可以使用的一個方法是設(shè)置活動的任務(wù)親和力為空字串(表示沒有親和力),然后設(shè)置finishOnBackground 屬性。 如果你想讓用戶給你提供一個單獨的活動描述的通知,倒不如返回到應(yīng)用的任務(wù)里,這個比較管用。要指定這個屬性,不管用戶使用BACK還是HOME,活動都會結(jié)束;如果這個屬性沒有指定,按HOME 鍵將會導(dǎo)致活動以及任務(wù)還留在系統(tǒng)里,并且沒有辦法返回到該任務(wù)里。
請確保閱讀過文檔啟動模式屬性(launchMode attribute) 以及 意圖標(biāo)記(Intent
flags) ,關(guān)注這些選項的詳細信息。
進程
在Android 中,進程是應(yīng)用程序的完整實現(xiàn),而不是用戶通常了解的那樣。他們主要用途很簡單:
· 提高穩(wěn)定性和安全性,將不信任或者不穩(wěn)定的代碼移動到其他進程。
· 可將多個.apk 包運行在同一個進程里減少系統(tǒng)開銷。
· 幫助系統(tǒng)管理資源,將重要的代碼放在一個單獨的進程里,這樣就可以單獨銷毀應(yīng)用程序的其他部分。
像前面描述的一樣,進程的屬性被用來控制那些有特殊應(yīng)用組件運行的進程。注意這個屬性不能違反系統(tǒng)安全: 如果兩個.apk 包不能共享同一個用戶ID,卻試圖運行在通一個進程里,這種情況是不被允許的,事實上系統(tǒng)將會創(chuàng)建兩個不同的進程。請查看安全相關(guān)文檔以獲取更多關(guān)于安全限制方面的信息。
線程
每個進程包含一個或多個線程。多數(shù)情況下,Android 避免在進程里創(chuàng)建多余的線程,除非它創(chuàng)建它自己的線程,我們應(yīng)保持應(yīng)用程序的單線程性。一個重要的結(jié)論就是所有呼叫實例, 廣播接收器, 以及 服務(wù)的實例都是由這個進程里運行的主線程創(chuàng)建的。
注意新的線程不是為活動,廣播接收器,服務(wù)或者內(nèi)容提供器實例創(chuàng)建:這些應(yīng)用程序的組件在進程里被實例化(除非另有說明,都在同一個進程處理),實際上是進程的主線程。這說明當(dāng)系統(tǒng)調(diào)用時這些組件(包括服務(wù))不需要進程遠距離或者封鎖操作(就像網(wǎng)絡(luò)呼叫或者計算循環(huán)),因為這將阻止進程中的所有其他組件。你可以使用標(biāo)準(zhǔn)的線程 類或者Android 的HandlerThread 類去對其它線程執(zhí)行遠程操作。
這里有一些關(guān)于創(chuàng)建線程規(guī)則的例外:
· 呼叫IBinder 或者IBinder 實現(xiàn)的接口,如果該呼叫來自其他進程,你可以通過線程發(fā)送的IBinder 或者本地進程中的線程池呼叫它們,從進程的主線程呼叫是不可以的。特殊情況下,,呼叫一個服務(wù) 的IBinder 可以這樣處理。(雖然在服務(wù)里呼叫方法在主線程里已經(jīng)完成。)這意味著IBinder 接口的實現(xiàn)必須要有一種線程安全的方法,這樣任意線程才能同時訪問它。
· 呼叫由正在被調(diào)用的線程或者主線程以及IBinder 派發(fā)的內(nèi)容提供器 的主方法。被指定的方法在內(nèi)容提供器的類里有記錄。這意味著實現(xiàn)這些方法必須要有一種線程安全的模式,這樣任意其它線程同時可以訪問它。
· 呼叫視圖以及由視圖里正在運行的線程組成的子類。通常情況下,這會被作為進程的主線程,如果你創(chuàng)建一個線程并顯示一個窗口,那么繼承的窗口視圖將從那個線程里啟動。Android 應(yīng)用程序的生命周期。
在大多數(shù)情況下,每個Android 應(yīng)用程序都運行在自己的Linux 進程中。當(dāng)應(yīng)用程序的某些代碼需要運行時,這個進程就被創(chuàng)建并一直運行下去,直到系統(tǒng)認(rèn)為該進程不再有用為止。然后系統(tǒng)將回收進程占用的內(nèi)存以便分配給其它的應(yīng)用程序。應(yīng)用程序的開發(fā)人員必須理解不同的應(yīng)用程序組件(尤其是Activity, Service, 和BroadcastReceiver)是如何影響應(yīng)用程序進程生命周期的,這是很重要的一件事情。不正確地使用這些組件可能會導(dǎo)致系統(tǒng)殺死正在執(zhí)行重要任務(wù)的應(yīng)用程序進程。一個常見的進程生命周期bug 的例子是BroadcastReceiver, 當(dāng)BroadcastReceiver在BroadcastReceiver.onReceive()方法中接收到一個Intent 時,它會啟動一個線程,然后返回。一旦它返回,系統(tǒng)將認(rèn)為BroadcastReceiver 不再處于活動狀態(tài),因而BroadcastReceiver 所在的進程也就不再有用了(除非該進程中還有其它的組件處于活動狀態(tài))。因此,系統(tǒng)可能會在任意時刻殺死進程以回收內(nèi)存。這樣做的話,進程中創(chuàng)建(spawned)出的那個線程也將被終止。對這個問題的解決方法是從BroadcastReceiver 啟動一個服務(wù),讓系統(tǒng)知道進程中還有處于活動狀態(tài)的工作。為了決定在內(nèi)存不足時讓系統(tǒng)殺死哪個進程,Android 根據(jù)每個進程中運行的組件以及組件的狀態(tài)把進程放入一個”重要性分級(importance hierarchy)”中。進程的類型包括(按重要程度排序):
1. 前臺(foreground)進程,與用戶當(dāng)前正在做的事情密切相關(guān)。不同的應(yīng)用程序組件能夠通過不同的方法使它的宿主進程移到前臺。當(dāng)下面任何一個條件滿足時,可以考慮將進程移到前臺:
1. 進程正在屏幕的最前端運行一個與用戶交互的Activity (它的onResume()方法被調(diào)用)
2. 進程有一正在運行的BroadcastReceiver (它的BroadcastReceiver.onReceive()方法正在執(zhí)行)
3. 進程有一個Service,并且在Service 的某個回調(diào)函數(shù)(Service.onCreate(),Service.onStart(), 或 Service.onDestroy())內(nèi)有正在執(zhí)行的代碼。
1. 可見(visible)進程,它有一個可以被用戶從屏幕上看到的Activity,但不在前臺(它的onPause()方法被調(diào)用)。舉例來說,如果前臺的Activity 是一個對話框,以前的Activity 隱藏在對話框之后,就可能出現(xiàn)這種進程。這樣的進程特別重要,一般不允許被殺死,除非為了保證前臺進程的運行不得不這樣做。
2. 服務(wù)(service)進程,有一個已經(jīng)用startService() 方法啟動的Service。雖然這些進程用戶無法直接看到,但它們做的事情卻是用戶所關(guān)心的(例如后臺MP3回放或后臺網(wǎng)絡(luò)數(shù)據(jù)的上傳下載)。因此,系統(tǒng)將一直運行這些進程除非內(nèi)存不足以維持所有的前臺進程和可見進程。
3. 后臺(background)進程, 擁有一個當(dāng)前用戶看不到的Activity(它的onStop()
方法被調(diào)用)。這些進程對用戶體驗沒有直接的影響。如果它們正確執(zhí)行了Activity生命期(詳細信息可參考Activity),系統(tǒng)可以在任意時刻殺死進程來回收內(nèi)存,并提供給前面三種類型的進程使用。系統(tǒng)中通常有很多個這樣的進程在運行,因此要將這些進程保存在LRU 列表中,以確保當(dāng)內(nèi)存不足時用戶最近看到的進程最后一個被殺掉。
4. 空(empty)進程,不包含任何處于活動狀態(tài)的應(yīng)用程序組件。保留這種進程的唯一原因是,當(dāng)下次應(yīng)用程序的某個組件需要運行時,不需要重新創(chuàng)建進程,這樣可以提高啟動速度。
系統(tǒng)將以進程中當(dāng)前處于活動狀態(tài)組件的重要程度為基礎(chǔ)對進程進行分類。請參考Activity, Service 和 BroadcastReceiver 文檔來獲得有關(guān)這些組件在進程整個生命期中是如何起作用的詳細信息。每個進程類別的文檔詳細描述了它們是怎樣影響應(yīng)用程序整個生命周期的。進程的優(yōu)先級可能也會根據(jù)該進程與其它進程的依賴關(guān)系而增長。例如,如果進程A 通過在進程B 中設(shè)置Context.BIND_AUTO_CREATE 標(biāo)記或使用ContentProvider 被綁定到一個服務(wù)(Service),那么進程B 在分類時至少要被看成與進程A 同等重要。
二、開發(fā)應(yīng)用程序
Android 應(yīng)用構(gòu)成
Android 應(yīng)用是由各種各樣的組件來構(gòu)成。 這些組件大部分都是松散連接的,準(zhǔn)確 的說你可以把它們看成組件的聯(lián)合而非是一個單一的應(yīng)用。
通常,這些組件運行在同一個系統(tǒng)進程里面。你也可以在這個進程里面創(chuàng)建多個線程(這是很常見的),如果必要你也可以創(chuàng)建獨立的子進程。不過這種情況是非常少見的,因為Android 盡力使代碼進程間透明。
以下部分是很重要的Android APIs:
AndroidManifest.xml
AndroidManifest.xml 是系統(tǒng)的控制文件,它告訴系統(tǒng)如何處理你所創(chuàng)建的所有頂層組件(尤其是activities,服務(wù),Intent 接收器和后面描述的內(nèi)容管理器)。舉例來說,控制文件就是把你的活動(Activities)要接收的Intents 連接在一起的“膠水”。
活動(Activities)
活動(Activity)就是一個有生命周期的對象。 一個Activity 就是完成某些工作的代碼塊, 如必要的話,這部分工作還可能包括對用戶UI 界面的顯示。不過這不是必須的,有些活 動從不顯示UI 界面。典型地,你將會指定你的應(yīng)用程序中的一個活動為整個程序的入口點。
視圖(Views)
視圖(Views)可以將其自身繪制到屏幕上。Android 的用戶界面由一系列的視圖樹 (trees of views)構(gòu)成。接口都是由一組以樹的形式出現(xiàn)的視圖組成的。開發(fā)者可 以通過創(chuàng)建一個新的視圖的方法來使用自定義的圖形處理技術(shù)(比如開發(fā)游戲,或者是 使用了不常用的用戶圖形(UI)窗口界面(widget))。
Intents
Intents 是一個簡單的消息對象,它表示程序想做某事的“意圖”(intention)。比如如果你的應(yīng)用程序想要顯示一個網(wǎng)頁,那么它通過創(chuàng)建一個Intent 實例并將其傳遞給 系統(tǒng)來表示意圖瀏覽這個URI。系統(tǒng)將定位于知道如何能處理這一Intent 的代碼(在當(dāng) 前情況下就是瀏覽器),并運行之。Intents 也可以用于廣播系統(tǒng)范圍內(nèi)的有效事件 (例如通知事件)。
服務(wù)(Services)
服務(wù)是運行在后臺的一段代碼。它可以運行在它自己的進程,也可以運行在其他應(yīng)用程序進程的上下文(context)里面,這取決于自身的需要。其它的組件可以綁定到一個服務(wù)(Service)上面,通過遠程過程調(diào)用(RPC)來調(diào)用這個方法。例如媒體播放器的服務(wù), 當(dāng)用戶退出媒體選擇用戶界面,她仍然希望音樂依然可以繼續(xù)播放,這時就是由服務(wù) (service)來保證當(dāng)用戶界面關(guān)閉時音樂繼續(xù)播放的。
通知(Notifications)
通知將以小圖標(biāo)的形式呈現(xiàn)在狀態(tài)欄里,用戶通過與圖標(biāo)的交互式操來接收消息。最常見的通知包括短信息,通話記錄,語音郵件,但是應(yīng)用程序也可以創(chuàng)建它們自己的通知事件。 我們推薦采用通知事件實現(xiàn)提醒用戶的注意。
內(nèi)容管理器(ContentProviders)
內(nèi)容管理器(ContentProvider)提供對設(shè)備上數(shù)據(jù)進行訪問的數(shù)據(jù)倉庫。典型的例子就 是使用內(nèi)容管理器來訪問聯(lián)系人列表。你的應(yīng)用程序也可以使用其它程序通過內(nèi)容管理器提供的數(shù)據(jù),同時你也可以定義你自己的內(nèi)容管理器來向其它應(yīng)用提供數(shù)據(jù)訪問服務(wù)。
存、取、提供數(shù)據(jù)
典型的桌面操作系統(tǒng)一般能提供一種通用的文件系統(tǒng),所有應(yīng)用程序都能儲存和讀文件,并且其他應(yīng)用程序也能訪問該文件(可能需要一些訪問控制設(shè)置). Android 使用不同的方式:在平臺上,所有應(yīng)用程序的數(shù)據(jù)(包括文件),對該應(yīng)用程序是私有的。當(dāng)然,Android 也提供一種標(biāo)準(zhǔn)方法將自己的私有數(shù)據(jù)提供給其他應(yīng)用程序訪問。這一章節(jié)講了很多方法,描述應(yīng)用如何存取數(shù)據(jù),以及將數(shù)據(jù)提供給其他程序訪問,當(dāng)然,你也可以向其他應(yīng)用程序請求并獲得它的數(shù)據(jù)。
Android 提供下面的方式來存取數(shù)據(jù):
參數(shù)選擇
使用一個輕量級機制來存取基本數(shù)據(jù)類型的數(shù)據(jù)對,這是典型應(yīng)用程序參數(shù)的
存儲模式。
文件
你可以將你的文件存儲在設(shè)備上或者其他移動媒介上,默認(rèn)情況下,其他應(yīng)用程序是不能訪問這些文件的。
數(shù)據(jù)庫教程
Android 有直接SQLite 數(shù)據(jù)庫的API。應(yīng)用程序可以創(chuàng)建以及使用SQLite 數(shù)據(jù)庫。每個包創(chuàng)建的數(shù)據(jù)庫都是私有的。
數(shù)據(jù)提供
數(shù)據(jù)提供是應(yīng)用程序的一個可選組件,它可以提供讀/寫應(yīng)用程序私有數(shù)據(jù)的方法。內(nèi)容提供組件實現(xiàn)了一種標(biāo)準(zhǔn)請求數(shù)據(jù)的語法,和一種標(biāo)準(zhǔn)處理返回值的機制。Android 提供很多標(biāo)準(zhǔn)數(shù)據(jù)的提供方式,例如私有聯(lián)系人。
網(wǎng)絡(luò)
不要忘記,我們還可以使用網(wǎng)絡(luò)存取數(shù)據(jù)。
Android 的安全與權(quán)限
Android 是一個多進程系統(tǒng),每一個應(yīng)用程序(和系統(tǒng)的組成部分)都運行在自己的進程中。在應(yīng)用程序和系統(tǒng)間的安全通過標(biāo)準(zhǔn)的Linux 設(shè)備在進程級被執(zhí)行,例如被分配給應(yīng)用程序的用戶和組ID。額外的細粒度安全特性通過“許可”機制來提供,該機制能夠?qū)σ粋€指定進程可實現(xiàn)的特定操作進行約束。
安全結(jié)構(gòu)
Android 安全學(xué)中的一個重要的設(shè)計點是在默認(rèn)情況下應(yīng)用程序沒有權(quán)限執(zhí)行對其它應(yīng)用程序、操作系統(tǒng)或用戶有害的操作。這些操作包括讀/寫用戶的隱私數(shù)據(jù)(例如聯(lián)系方式或e-mail),讀/寫其它應(yīng)用程序的文件,執(zhí)行網(wǎng)絡(luò)訪問,保持設(shè)備活動,等等。
應(yīng)用程序的進程是一個安全的沙箱。它不能干擾其它應(yīng)用程序,除非在它需要添加原有沙箱不能提供的功能時明確聲明權(quán)限。這些權(quán)限請求能夠被不同方式的操作所處理,特別的要基于證書和用戶的提示被自動的允許或禁止。權(quán)限的請求在那個應(yīng)用程序中通過一個應(yīng)用程序被聲明為靜態(tài)的,所以在此之后在安裝時或沒有改變時它們會預(yù)先知道。
應(yīng)用程序簽名
所有的Android 應(yīng)用程序(.apk 文件)必須通過一個證書的簽名,此證書的私鑰必須被開發(fā)者所掌握。這個證書的標(biāo)識是應(yīng)用程序的作者。這個證書不需要通過證書組織的簽署:Android 應(yīng)用程序?qū)τ谑褂米院炇鸬淖C書是完全允許的和特別的。這個證書僅僅被用于與應(yīng)用程序建立信任關(guān)系,不是為了大規(guī)模的控制應(yīng)用程序可否被安裝。最重要的方面是通過確定能夠訪問原始簽名權(quán)限和能夠共享用戶ID 的簽名來影響安全。
用戶標(biāo)識和文件訪問
安裝在設(shè)備中的每一個Android包文件(.apk)都會被分配給一個屬于自己的統(tǒng)一的Linux用戶ID,并且為它創(chuàng)建一個沙箱以防止影響其它應(yīng)用程序(或者其它應(yīng)用程序影響它)。
用戶ID 在應(yīng)用程序安裝到設(shè)備中時被分配,并且在這個設(shè)備中保持它的永久性。
因為安全執(zhí)行發(fā)生在進程級,所以一些不同包中的代碼在相同進程中不能正常的運行,自從他們需要以不同Linux 用戶身份運行時。你可以使用每一個包中的
AndroidManifest.xml 文件中的manifest 標(biāo)簽屬性sharedUserId 擁有它們分配的相同用戶ID。通過這樣做,兩個包被視為相同的應(yīng)用程序的安全問題被解決了,注意為了保持安全,僅有相同簽名(和請求相同sharedUserId 標(biāo)簽)的兩個應(yīng)用程序簽名將會給相同的用戶ID。
應(yīng)用創(chuàng)建的任何文件都會被賦予應(yīng)用的用戶標(biāo)識,并且,正常情況下不能被其它包訪問。當(dāng)你通過getSharedPreferences(String, int), openFileOutput(String, int) 或者openOrCreateDatabase(String, int, SQLiteDatabase.CursorFactory)創(chuàng)建一個新文件時, 你可以同時或分別使用 MODE_WORLD_READABLE 和
MODE_WORLD_WRITEABLE 標(biāo)志允許其它包讀/寫此文件。當(dāng)設(shè)置了這些標(biāo)志時,這個文件仍然屬于你的應(yīng)用程序,但是它的全局讀、寫和讀寫權(quán)限已經(jīng)設(shè)置所以其它任何應(yīng)用程序可以看到它。
權(quán)限命名
一個基本的Android 應(yīng)用程序沒有與其相關(guān)聯(lián)的權(quán)限,意味著它不能做任何影響用戶體驗或設(shè)備中的數(shù)據(jù)的有害操作。要利用這個設(shè)備的保護特性,在你的應(yīng)用程序需要時,你必須在AndroidManifest.xml 文件中包含一個或更多的<uses-permission>
標(biāo)簽來聲明此權(quán)限。
例如:需要監(jiān)聽來自SMS 消息的應(yīng)用程序?qū)⒁付ㄈ缦聝?nèi)容:
<manifest
xmlns:android="http://schemas./apk/res/android"
package="com.android.app.myapp" >
<uses-permission
android:name="android.permission.RECEIVE_SMS" />
</manifest>
在安裝應(yīng)用程序時,通過包安裝器應(yīng)用程序要通過權(quán)限請求的許可,使建立在與應(yīng)用程序簽名的核對下聲明對于用戶的那些權(quán)限和影響。在應(yīng)用運行期間對用戶不做檢查:它要么在安裝時被授予特定的許可,并且使用想用的特性;要么不被授予許可,并且使得一切使用特性的嘗試失敗而不提示用戶。
例如,sendBroadcast(Intent) 方法就是當(dāng)數(shù)據(jù)被發(fā)送給到每個接收器時檢查許可的,
在方法調(diào)用返回之后,因此當(dāng)許可失敗時你不會收到一個異常。然而,幾乎在所有例子中,許可失敗都會被打印到系統(tǒng)日志中。通常,多次的許可錯誤會產(chǎn)生拋回至應(yīng)用程序的SecurityException 異常。
Android 系統(tǒng)提供的許可可以在Manifest.permission 中找到。每個引用也可以定義和Enforce 它自己的許可,因此這不是全面的所有可能的列表。
在程序操作期間,個別權(quán)限在一些地方可能被強制:
· 在系統(tǒng)接到呼叫的時候,預(yù)防一個應(yīng)用程序去執(zhí)行特定的函數(shù)。
· 在啟動Activity 時,防止一個應(yīng)用啟動其它應(yīng)用程序的Activities。
· 發(fā)送和接收Intent 廣播時,控制誰能接收你的廣播或者誰能發(fā)送廣播給你。
· 在一個內(nèi)容提供器上訪問和操作時。
· 綁定或開始一個服務(wù)時。
權(quán)限的聲明和支持
為了執(zhí)行你自己的權(quán)限,你必須首先在你的AndroidManifest.xml 中使用一個或多個<permission> 標(biāo)簽聲明它們。
<protectionLevel> 屬性是必需的,告訴系統(tǒng)用戶應(yīng)如何處理應(yīng)用程序接到請求此權(quán)限的通知,或者在這個文檔中對這個權(quán)限的許可的描述。
<permissionGroup> 屬性是可選的,僅僅用于幫助系統(tǒng)為用戶顯示權(quán)限。通常,你要設(shè)置這些,向一個標(biāo)準(zhǔn)的系統(tǒng)組(列在android.Manifest.permission_group 中),或者在更多的情況下要自定義。它更偏向于使用一個已經(jīng)存在的組,做為簡化的權(quán)限用戶界面顯示給用戶。
注意:應(yīng)該為每個權(quán)限提供標(biāo)簽(label) 和描述(description)。當(dāng)用戶瀏覽權(quán)限列表時,它們可以為用戶展示字符資源,如(android:label) 或者一個許可的詳細信息( android:description) 。標(biāo)簽(label)比較短,用幾個詞來描述該權(quán)限保護的關(guān)鍵功能。描述(description)應(yīng)該是一組句子,用于描述獲得權(quán)限的用戶可以做什么。我們寫描述的習(xí)慣是兩句話,第一句聲明權(quán)限,第二句警告用戶如果應(yīng)用許可該權(quán)限時,會發(fā)生什么不好的事情。
你可以在系統(tǒng)中通過shell 命令 adb shell pm list permissions 查看權(quán)限當(dāng)前
在AndroidManifest.xml 文件中支持權(quán)限
通過 AndroidManifest.xml 文件可以設(shè)置高級權(quán)限,以限制訪問系統(tǒng)的所有組件或者使用應(yīng)用程序。所有的這些請求都包含在你所需要的組件中的 android:permission屬性,命名這個權(quán)限可以控制訪問此組件。
Activity 權(quán)限 (使用 <activity> 標(biāo)簽) 限制能夠啟動與 Activity 權(quán)限相關(guān)聯(lián)的組件或應(yīng)用程序。此權(quán)限在 Context.startActivity() 和 Activity.startActivityForResult() 期間要經(jīng)過檢查;如果調(diào)用者沒有請求權(quán)限,那么會為調(diào)用拋出一個安全異常
( SecurityException )。
Service 權(quán)限(應(yīng)用 <service> 標(biāo)簽)限制啟動、綁定或啟動和綁定關(guān)聯(lián)服務(wù)的組件或應(yīng)用程序。此權(quán)限在 Context.startService(), Context.stopService() 和
Context.bindService() 期間要經(jīng)過檢查;如果調(diào)用者沒有請求權(quán)限,那么會為調(diào)用拋出一個安全異常( SecurityException )。
BroadcastReceiver 權(quán)限(應(yīng)用 <receiver> 標(biāo)簽)限制能夠為相關(guān)聯(lián)的接收者發(fā)送廣播的組件或應(yīng)用程序。在 Context.sendBroadcast() 返回后此權(quán)限將被檢查,同時系統(tǒng)設(shè)法將廣播遞送至相關(guān)接收者。因此,權(quán)限失敗將會導(dǎo)致拋回給調(diào)用者一個異常;它將不能遞送到目的地。在相同方式下,可以使 Context.registerReceiver() 支持一個權(quán)限,使其控制能夠遞送廣播至已登記節(jié)目接收者的組件或應(yīng)用程序。其它的,當(dāng)調(diào)用
Context.sendBroadcast() 以限制能夠被允許接收廣播的廣播接收者對象一個權(quán)限(見下文)。
ContentProvider 權(quán)限(使用 <provider> 標(biāo)簽)用于限制能夠訪問 ContentProvider中的數(shù)據(jù)的組件或應(yīng)用程序。(Content providers 有一個重要的附加安全設(shè)施可用于它們調(diào)用被描述后的URI 權(quán)限。) 不同于其它組件,它有兩個不相連系的權(quán)限屬性要設(shè)置:android:readPermission 用于限制能夠讀取提供器的組件或應(yīng)用程序,android:writePermission 用于限制能夠?qū)懭胩峁┢鞯慕M件或應(yīng)用程序。注意,如果一個提供者的讀寫權(quán)限受保護,意思是你只能從提供器中讀,而沒有寫權(quán)限。當(dāng)你首次收回提供者(如果你沒有任何權(quán)限,將會拋出一個SecurityException 異常),那么權(quán)限要被檢查,并且做為你在這個提供者上的執(zhí)行操作。 使用 ContentResolver.query() 請求獲取讀權(quán)限; 使用 ContentResolver.insert(), ContentResolver.update() 和ContentResolver.delete() 請求獲取寫權(quán)限。在所有這些情況下,一個SecurityException異常從一個調(diào)用者那里拋出時不會存儲請求權(quán)限結(jié)果。
發(fā)送廣播時支持權(quán)限
當(dāng)發(fā)送一個廣播時你能總指定一個請求權(quán)限,此權(quán)限除了權(quán)限執(zhí)行外,其它能發(fā)送Intent到一個已注冊的BroadcastReceiver 的權(quán)限均可以。 通過調(diào)用
Context.sendBroadcast() 及一些權(quán)限字符串, 為了接收你的廣播,你請求一個接收器應(yīng)用程序必須持有那個權(quán)限。
注意,接收者和廣播者都能夠請求一個權(quán)限。當(dāng)這樣的事發(fā)生了,對于Intent 來說,這兩個權(quán)限檢查都必須通過,為了交付到共同的目的地。
其它權(quán)限支持
任意一個好的粒度權(quán)限都能夠在一些調(diào)用者的一個服務(wù)中被執(zhí)行。 和
Context.checkCallingPermission() method. 方法一起被完成。調(diào)用并產(chǎn)生一個需要的權(quán)限字符串,它將返回一個整型,以確定當(dāng)前調(diào)用進程是否被許可。注意,僅僅當(dāng)你執(zhí)行的調(diào)用進入到其它進程的時候這些才會被使用,通常,通過IDL 接口從一個服務(wù)發(fā)布,或從一些其它方式通知其它進程。
這有許多其它有益的方式去檢查權(quán)限。如果你有一個其它進程的PID,你可以使用上下文的方法 Context.checkPermission(String, int, int) 檢查權(quán)限違反PID。 如果你有一個其它應(yīng)用程序的包名, 你可以使用直接的包管理器方法
PackageManager.checkPermission(String, String) 去查看特定的包是否被指定的權(quán)限所許可。
URI 權(quán)限
迄今為止,在與內(nèi)容提供器共同使用時,標(biāo)準(zhǔn)權(quán)限系統(tǒng)描述通常是不充分的。一個內(nèi)容提供器要保護它自己及讀和寫權(quán)限,當(dāng)為了它們產(chǎn)生作用,它的直接客戶端總是需要手動的對其它應(yīng)用程序指定URI。一個典型的例子是郵件應(yīng)用程序中的附件。訪問郵件的權(quán)限應(yīng)該被保護,因為這是敏感用戶數(shù)據(jù)??梢?,如果一個網(wǎng)址圖片附件提供給一個圖片查看器,那個圖片查看器將沒有權(quán)限打開這個附件,因為它沒有原因去擁有一個權(quán)限從而不能訪問所有的電子郵件。
對于這個問題的解決是通過網(wǎng)址權(quán)限:當(dāng)開始一個活動或?qū)σ粋€活動返回一個結(jié)果,調(diào)用者可通過設(shè)置Intent.FLAG_GRANT_READ_URI_PERMISSION 和
Intent.FLAG_GRANT_WRITE_URI_PERMISSION 中的一個或者兩個。允許接收活動權(quán)限訪問在Intent 中特定的數(shù)據(jù)地址,不論它有權(quán)限訪問數(shù)據(jù)在內(nèi)容提供器相應(yīng)的Intent 中。
這種機制允許一個公用的功能性模型使用戶相互交互(打開一個附件,從一個列表中選擇一個聯(lián)系人,等等)驅(qū)動ad-hoc 在優(yōu)粒度權(quán)限的許可下。這可能是一個主要設(shè)備,應(yīng)用程序為了減少這個權(quán)限而需要,僅僅直接關(guān)系到它們的行為。
這優(yōu)粒度URI 權(quán)限的許可工作,然而,請求一些協(xié)作和內(nèi)容提供者保持那些URI。強烈推薦內(nèi)容提供者實現(xiàn)這種設(shè)備,并且通過android:grantUriPermissions 屬性或者<grant-uri-permissions> 標(biāo)簽聲明支持它。
更多信息可以在Context.grantUriPermission(), Context.revokeUriPermission(), 和Context.checkUriPermission() 方法中找到。
資源管理和多國版本
資源是外部文件(不含代碼的文件),它被代碼使用并在編譯時編入應(yīng)用程序。Android支持不同類型的資源文件,包括XML,PNG 以及JPEG 文件XML 文件根據(jù)描述的不同有不同格式。這份文檔描述可以支持什么樣的文件,語法,以及各種格式.源代碼以及XML 文件將資源打包并編譯進二進制文件,這種模式能使得資源更快得被加載。字符串也同樣被壓縮成更高效的模式。由于這些原因, Android 平臺上存在不同的資源類型.
資源
Android 資源系統(tǒng)能跟蹤所有非代碼相關(guān)的應(yīng)用程序。你可以使用 資源 類來訪問應(yīng)用程序的資源,資源的實例通常和應(yīng)用程序聯(lián)系在一起,你可以通過Context.getResources()來訪問。
應(yīng)用程序的資源在編譯時就被編譯到應(yīng)用程序二進制代碼里。為了使用某個資源,你需要將它在代碼目錄結(jié)構(gòu)里放正確,然后編譯。作為編譯過程的一部分,產(chǎn)生的資源代號你可以在源代碼里使用 -- 這允許編譯器驗證你的程序代碼和你定義的資源是否相符。
創(chuàng)建資源
Android 支持字符串,圖片以及很多其他類型的資源。每個對象語法、格式以及它們存儲位置的支持,都是取決于不同類型的對象? 通常,你可以通過三種類型的文件來創(chuàng)建資源:XML 文件(除位圖以及原數(shù)據(jù)文件),位圖文件(對于圖片)以及原始數(shù)據(jù)(其它類型,例如聲音文件,等等。)。事實上,有兩種不同類型的XML 文件,一種是編譯到包里的,另外一種是通過aapt 來產(chǎn)生的資源文件,這里有一張包含所有資源類型,
文件格式,文件描述以及所有XML 文件的詳細信息的列表。
在項目里,你可以在子目錄res/下創(chuàng)建和存儲資源文件。Android 有一個資源編譯工具(aapt),它可以編譯在這個目錄下所有的子目錄中的資源,這里有個各種資源的列表。你可以從 資源引用 這里看到各種類型的對象,包含其語法以及格式。
路徑 資源類型res/anim/ XML 文件被編譯進 逐幀動畫 或 補間動畫 的對象res/drawable/.png, .9.png, .jpg files 這些類型的文件被編譯進下列這些圖表資源列表為了獲得這些資源的類型,使用Resource.getDrawable(id)
· 位圖文件
· 9-patches (可改變尺寸的圖像)
res/layout/ 可編譯成屏幕布局的XML 文件 (或者屏幕的一部分). 查看 布局 res/values/ 可編譯成多種類型資源的文件
注意: 不像其他 res/ 文件夾,它能容納任何數(shù)量的文件,但只是描述其創(chuàng)建而不是資源本身. XML 的元素類型可以決定這些資源在R.class 里什么位置被替換 .文件可以被命名為任何名字,文件夾里有一些典型的文件(一般約定文件以定義的元素類型后面部分為文件名)::
· arrays.xml 定義數(shù)組
· colors.xml 定義 顏色 和 顏色字串?dāng)?shù)值. 你可以使用
Resources.getDrawable() 以及Resources.getColor(), respectively, 取得這些資源.· dimens.xml 定義 尺寸數(shù)據(jù) . 使用Resources.getDimension() 取得這些資源。· strings.xml 定義字符串?dāng)?shù)值 (使用Resources.getString 或Resources.getText()取得資源,(后者更好一點)getText() 能取到在用戶界面上顯示的文本框里的文本?!?styles.xml 定義類型 對象。res/xml/ 任何XML 文件可以進行編譯,并能在運行時調(diào)用Resources.getXML() 顯示XML 原文件。
res/raw/ 這里的任何文件都將直接被復(fù)制到設(shè)備上。編譯產(chǎn)品時,這些數(shù)
據(jù)不會被編譯,它們被直接加入到程序包里。 為了在程序中使用這些資源,你可以調(diào)用Resources.openRawResource() , 參數(shù)為
ID: R.raw.somefilename.
資源最終會被編譯成APK 文件,Android 創(chuàng)建一個包裝類,命名為R,這樣你能做你的代碼里使用這些資源類。根據(jù)資源路徑和文件名的不同,R 包含很多子類。
全局資源
· 一些資源類允許你定義顏色。它能接受多種網(wǎng)絡(luò)類型的值 -- 你可以寫成 #RGB,
#ARGB, #RRGGBB, #AARRGGBB 這樣16 進制常數(shù)都可以。
· 所有的顏色都可以設(shè)置一個阿爾法值,開始的兩個16 進制數(shù)指定為透明。 0 在阿爾法值里意味著透明。當(dāng)然,默認(rèn)值是不透明的。
使用資源
編譯時,Android 產(chǎn)生一個叫R 的類,它指向你程序中所有的資源。這個類包含很多子類。每一種都是Android 支持的,同時,編譯后會產(chǎn)生一個資源文件。每個類提供一個或多個編譯后資源的標(biāo)識符,你可以在代碼中使用。下面是個源代碼的文件,里面包含了字符串,布局文件(全屏或者部分屏幕),以及圖像資源。
注意: 這個R 類是自動產(chǎn)生的,你不能手動編寫。當(dāng)資源變化的時候它會自動更新。
 
在代碼中使用資源
只要知道資源的ID 以及你編譯進目標(biāo)文件的資源類型就可以在代碼里使用它來。下面是一些語法:
R.resource_type.resource_name或者android.R.resource_type.resource_name
resource_type 是R 子類的一種類型。 resource_name 是定義在XML 文件里的資源名或者為其他文件類型定義的資源文件(沒有后綴)名。每種類型的資源會被加入到一個特定的R 的子類中;為了學(xué)習(xí)哪種R 的子類里有你編譯的資源類型,參考資源引用 文檔。被編譯進應(yīng)用程序的資源不需要包的名字就可以直接被訪問到(像這樣:
R.resource_type.resource_name). Android 包含一些標(biāo)準(zhǔn)資源,如屏幕的類型,按鈕的背景。要使用這些代碼,你需要包含 android, 如
android.R.drawable.button_background.
這里有一些好的和糟糕的例子說明如何在代碼里使用編譯后的資源:
// 從畫圖資源類里裝載一個當(dāng)前屏幕背景。
this.getWindow().setBackgroundDrawableResource(R.drawable.my_background_image);
// 錯誤! 將一個資源ID 裝入一個需要字符串的方法中
this.getWindow().setTitle(R.string.main_title);
//正確!需要從資源封裝類里取得標(biāo)題。
this.getWindow().setTitle(Resources.getText(R.string.main_title));
// 從當(dāng)前屏幕中裝載布局?jǐn)?shù)據(jù)。
setContentView(R.layout.main_screen);
//從ViewFlipper 對象中設(shè)置動畫中一幀 。
mFlipper.setInAnimation(AnimationUtils.loadAnimation(this,
R.anim.hyperspace_in));
// 在TextView 對象中設(shè)置文本內(nèi)容。
TextView msgTextView = (TextView)findViewByID(R.id.msg);
msgTextView.setText(R.string.hello_message);
資源引用
一個在屬性(或者資源)里提供的數(shù)值可以被指向一個具體的資源。這常常被使用在布局文件中用于字符串(可以被本地化) 以及圖片(存在于其他文件中的),通過一個引用可以是包括顏色和整數(shù)的任何資源類型。
例如,如果有 顏色資源, 我們可以將文本的顏色值寫在布局文件中,顏色值可以從資源文件里取得:
<?xml version="1.0" encoding="utf-8"?>
<EditText id="text"
xmlns:android="http://schemas./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. 這里我們不需要指定包,因為我們在我們自己的包里引用資源。為了指定一個系統(tǒng)資源,你需要這樣寫:
<?xml version="1.0" encoding="utf-8"?>
<EditText id="text"
xmlns:android="http://schemas./apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:textColor="@android:color/opaque_red"
android:text="Hello, World!" />
另外一個例子,當(dāng)你在布局文件里使用字符串,你必須做資源引用,這樣字符串才能被使用:
<?xml version="1.0" encoding="utf-8"?>
<EditText id="text"
xmlns:android="http://schemas./apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:textColor="@android:color/opaque_red"
android:text="@string/hello_world" />
這段代碼也能被用來創(chuàng)建資源間引用。例如,我們能這樣創(chuàng)建圖像資源:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<drawable
id="my_background">@android:drawable/theme2_background</drawab
le>
</resources>
主題屬性引用
另一種資源數(shù)值允許你引用當(dāng)前主題屬性值。這種屬性引用只能被用于特殊的資源類以及XML 屬性中;它允許你根據(jù)現(xiàn)在主題風(fēng)格將你定制的UI 變得更標(biāo)準(zhǔn)化,而不用使用大量的具體數(shù)值。
這里有個例子,我們能在布局文件中將文本顏色設(shè)置為基本系統(tǒng)主題中定義好的標(biāo)準(zhǔn)顏色:
<?xml version="1.0" encoding="utf-8"?>
<EditText id="text"
xmlns:android="http://schemas./apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:textColor="?android:textDisabledColor"
android:text="@string/hello_world" />
注意除來我們將前綴'?'代替了'@',其他非常像資源引用。當(dāng)你使用這個標(biāo)記,系統(tǒng)會自動查找你提供的屬性的名字 -- 資源工具知道肯定會有資源屬性相符合,你不需要詳細指定(?android:attr/android:textDisabledColor).
使用資源標(biāo)識符到主題里去尋找相應(yīng)的數(shù)據(jù)而不是直接使用原數(shù)據(jù),其語法和'@'模式是一樣的: ?[namespace:]type/name 這里的type 是可選擇的.
使用系統(tǒng)資源
許多系統(tǒng)資源應(yīng)用程序是可以使用的。這樣的資源定義在"android.R"的類里。 例如,你可以使用下面的代碼在屏幕上顯示一個標(biāo)準(zhǔn)的應(yīng)用程序圖標(biāo):
public class MyActivity extends Activity
{
public void onStart()
{
requestScreenFeatures(FEATURE_BADGE_IMAGE);
super.onStart();
setBadgeResource(android.R.drawable.sym_def_app_icon);
}
}
用相似的方法,這段代碼能將你的屏幕變成系統(tǒng)定義的標(biāo)準(zhǔn)的“綠色背景”:
public class MyActivity extends Activity
{
public void onStart()
{
super.onStart();
setTheme(android.R.style.Theme_Black);
}
}
對于不同的語言和設(shè)置支持不同的資源
你可以根據(jù)產(chǎn)品界面語言以及硬件配置設(shè)置不同的資源。注意,雖然你可以包含不同的字串,布局以及其他資源,但開發(fā)包(SDK)不會給你顯式的方法去指定不同的資源去加載。Android 檢測你的硬件以及位置信息選擇合適的設(shè)置去加載。用戶可以到設(shè)備上的設(shè)置界面去選擇不同的語言。
要包含不同的資源,在同一目錄下創(chuàng)建并行的文件夾,在每個文件夾后加上合適的名字,這個名字能表明一些配置信息(如語言,原始屏幕等等)。例如,這里的項目字符串文件一個是英文版的,另一個是法文版的:
MyApp/
res/
values-en/
strings.xml
values-fr/
strings.xml
Android 支持不同類型的修飾語,并可以加多條在文件夾名的后面, 修飾語之間以破折號分開。例如:一個繪圖資源類指定全部配置名稱命名會像這樣:
MyApp/
res/
drawable-en-rUS-port-160dpi-finger-keysexposed-qwerty-dpad-480
x320/
更典型的,你可以僅僅指定部分特定的配置選項。只要保證所有的數(shù)值都是按順序排列:
MyApp/
res/
drawable-en-rUS-finger/
drawable-port/
drawable-port-160dpi/
drawable-qwerty/
修飾語值
語言 兩個小寫字母 ISO 639-1。例如: en, fr, es
地區(qū) 兩個大寫字母加上一個小寫字母'r' ISO 3166-1-alpha-2。 例如:
rUS, rFR, rES
屏幕方向 port, land, square
屏幕像素
92dpi, 108dpi, 等等。
觸摸屏類型 notouch, stylus, finger
鍵盤是否有效 keysexposed, keyshidden
基本文本輸入模式
nokeys, qwerty, 12key
無觸摸屏的主要導(dǎo)航模式
notouch, dpad, trackball, wheel
屏幕分辨率
320x240, 640x480, 等等。大分辨率需要開始指定。
這個列表不包含一些特殊的參數(shù),如載體,商標(biāo),設(shè)備/硬件,制造商。任何應(yīng)用程序需要知道的信息都在資源修飾語里有說明。
這里有一些通用的關(guān)于資源目錄的命名指導(dǎo):
· 各個變量用破折號分開 (每個基本的目錄名后跟一個破折號)
· 變量大小寫敏感(其大小寫法必須始終一致)例如,
o 一個drawable 的目錄必須命名為 drawable-port, 而不是drawable-PORT。
o 你不能有兩個目錄命名為 drawable-port 以及 drawable-PORT,
甚至故意將"port" 和 "PORT"指為不同的參數(shù)也不可以。
· 一個式子里同一個類型修飾語中只有一個值是有效的(你不能指定像這樣
drawable-rEN-rFR/)
· 你可以指定多個參數(shù)去定義不同的配置,但是參數(shù)必須是上面表格里的。例如,
drawable-en-rUS-land 意思在US-English 的機器里載入風(fēng)景視圖。
· Android 會尋找最適合當(dāng)前配置的目錄,這會在下面描述
· 表格里所列的參數(shù)是用來打破平衡以防止多重路徑限制。 (看下面的例子)
· 所有目錄,無論是限制的,還是不限制的,只要在 res/ 目錄下.一些目錄是不能嵌套的(這樣 res/drawable/drawable-en 是不可以的)
· 所有的資源在被代碼引用中最好都使用簡單的、不加修飾的名字,如果一個資源這樣命名:
MyApp/res/drawable-port-92dp/myimage.png
它將這樣被引用:
R.drawable.myimage (code)
@drawable/myimage (XML)
Android 如何找到最合適的目錄
Android 將會挑出哪些基本資源文件在運行時會被使用,這依靠當(dāng)前的配置。 選擇過程如下:
1. 刪去一些和當(dāng)前設(shè)備配置不符合的資源。例如,如果屏幕的像素是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. 挑出一些最經(jīng)常符合配置的資源。例如,如果我們的地區(qū)是 en-GB, 方向是
port,那我們有兩個符合配置的選項: MyApp/res/drawable-en/ 和
MyApp/res/drawable-port/. 這個目錄 MyApp/res/drawable/ 可以被
刪除了,因為當(dāng)另外一個有一次匹配正確,而它沒有。
7. MyApp/res/drawable/myimage.png
8. MyApp/res/drawable-en/myimage.png
9. MyApp/res/drawable-port/myimage.png
10. 根據(jù)配置的優(yōu)先級選取最終適合的文件,它們按順利被排列在上面的表格里。更確切得說,語言匹配比方位匹配更重要, 所以我們可以通過選擇語言文件來平衡,MyApp/res/drawable-en/.
11. MyApp/res/drawable-en/myimage.png
12. MyApp/res/drawable-port/myimage.png
術(shù)語
資源系統(tǒng)將一系列分散內(nèi)容集合在一起形成最終的完整的資源功能,去幫助我們了解整個系統(tǒng)。這里有一些核心概念以及組件的概要說明,你在開發(fā)中將可能使用到:
最終文件: 應(yīng)用程序的獨立的數(shù)據(jù)包。這包含所有從java 程序編譯成的目標(biāo)文件,圖像(例如PNG 圖片), XML 文件等等。這些文件以一種特定的方式組織在一起,在程序打包最后時,它們被打包成一個獨立的ZIP 文件。
aapt: Android 最終文件打包工具。這個工具產(chǎn)生最終程序的ZIP 文件。除了將最終的元數(shù)據(jù)文件打包在一起,它也解析資源定義到最終的二進制數(shù)據(jù)里。
資源表:aapt 工具產(chǎn)生的特殊的文件,描述了所有在程序/包里的資源。這個文件可以通過資源類來訪問;它不能直接和應(yīng)用程序接觸。
資源: 資源表里一條記錄描述的是單一的命名值。大體上, 資源分成兩種:基本的和包裝的.資源標(biāo)識符: 在資源表里所有的資源都被唯一的整數(shù)標(biāo)識著。所有的代碼中(資源描述,XML 文件,Java 源代碼)你可以直接使用符號名代替真實的整數(shù)數(shù)值。
基本資源: 所有基本資源都可以被寫成一個簡單的字串,使用一定的格式可以描述資源系統(tǒng)里各種不同的基本類型:整數(shù),顏色,字串,其他資源的引用,等等。像圖片以及XML 描述文件這些復(fù)雜資源,被以基本字串資源儲存,它們的值就是相關(guān)最終數(shù)據(jù)文件的路徑。
包裝資源: 有一種特殊類型的資源,不是簡單的字符串,而是有一個隨意的名字/數(shù)值配對列表。每個數(shù)值可以對應(yīng)它本身的資源標(biāo)識,每個值可以持相同類型的字符串格式的數(shù)據(jù)作為一個正常的資源。包裝資源支持繼承:一個包里的數(shù)據(jù)能從其他包里繼承,有選擇地替換或者擴展能產(chǎn)生你自己需要的內(nèi)容。
種類: 資源種類是對于不同需求的資源標(biāo)識符而言的。例如,繪制資源類常常實例化繪制類的對象,所以這些包含顏色以及指向圖片或XML 文件的字符串路徑數(shù)據(jù)是原始數(shù)據(jù)。其它常見資源類型是字符串(本地化字符串),顏色(基本顏色),布局(一個指向XML 文件的字串路徑,它描述的是一個用戶界面)以及風(fēng)格(一個描述用戶接口屬性的包裝資源)。還有一個標(biāo)準(zhǔn)的“attr”資源類型,它定義了命名包裝數(shù)據(jù)以及XML 屬性的資源標(biāo)識符。
風(fēng)格: 包含包裝資源類型的名字常常用來描述一系列用戶接口屬性。例如,一個
TextView 的類可能會有一個描述界面風(fēng)格的類來定義文本大小,顏色以及對齊方式。
在一個界面布局的XML 文件中,可以使用“風(fēng)格” 屬性來確定整體界面風(fēng)格,它的值就是風(fēng)格資源的名字。
風(fēng)格類: 這里將詳述一些屬性資源類。其實數(shù)據(jù)不會被放在資源表本身,通常在源代碼里它以常量的形式出現(xiàn),這也可以使你在風(fēng)格類或者XML 的標(biāo)簽屬性里方便找到它的值。例如,Android 平臺里定義了一個“視圖”的風(fēng)格類,它包含所有標(biāo)準(zhǔn)視圖的屬性:
畫圖區(qū)域,可視區(qū)域,背景等。這個視圖被使用時,它就會借助風(fēng)格類去從XML 文件取得數(shù)據(jù)并將其載入到實例中。
配置: 對許多特殊的資源標(biāo)識符,根據(jù)當(dāng)前的配置,可以有多種不同的值。配置包括地區(qū)(語言和國家),屏幕方向,屏幕分辨率,等等。當(dāng)前的配置用來選擇當(dāng)資源表載入時哪個資源值生效。
主題: 一個標(biāo)準(zhǔn)類型的資源能為一個特殊的上下文提供全局的屬性值。例如,當(dāng)應(yīng)用工程師寫一個活動時,他能選擇一個標(biāo)準(zhǔn)的主題去使用,白色的或者黑色的;這個類型能提供很多信息,如屏幕背景圖片/顏色,默認(rèn)文本顏色,按鈕類型,文本編輯框類型,文本大小,等。當(dāng)布置一個資源布局時,控件(文本顏色,選中后顏色,背景)的大部分設(shè)置值取自當(dāng)前主題;如果需要,布局中的風(fēng)格以及屬性也可以從主題的屬性中獲得。
覆蓋層: 資源表不能定義新類型的資源,但是你可以在其他表里替換資源值。就像配置值,這可以在裝載時候進行;它能加入新的配置值(例如,改變字串到新的位置),替換現(xiàn)有值(例如,將標(biāo)準(zhǔn)的白色背景替換成"Hello Kitty"的背景圖片),修改資源包(例如修改主題的字體大小。白色主題字體大小為18pt)。這實際上允許用戶選擇設(shè)備不同的外表,或者下載新的外表文件。
資源引用
資源引用 這份文檔提供了不同類型資源的詳細列表,并提供了如何在Java 代碼中使用資源以及如何引用資源的描述。
國際化和本地化
即將完成: 國際化和本地化是非常關(guān)鍵的,但現(xiàn)在的SDK 還沒有完全支持好。當(dāng)SDK成熟時,這個章節(jié)會包含Android 平臺國際化和本地化的相關(guān)信息。 那時,外部字串以及良好的結(jié)構(gòu)將會使得創(chuàng)建和使用資源變得更省事。
三、開發(fā)工具箱
Android 設(shè)計哲學(xué)
即使平臺之間有很大的不同,但是如何利用API 創(chuàng)建應(yīng)用程序的學(xué)習(xí)過程是大同小異的。一般來說,有兩個步驟:首先,應(yīng)該知道怎么用API 實現(xiàn)你的功能。其次,要了解平臺間的細微差別。換句話說,首先你應(yīng)該學(xué)會如何創(chuàng)建應(yīng)用程序(了解應(yīng)用程序的基本結(jié)構(gòu)等),然后就要學(xué)會根據(jù)具體情況實現(xiàn)這個應(yīng)用程序。
相比而言,第二階段(學(xué)習(xí)使用正確的方法來實現(xiàn)應(yīng)用程序)通常需要很長一段時間,在這個過程中你會不斷地寫代碼,犯錯誤,然后從錯誤中吸取教訓(xùn)。顯然,這不是一個有效的學(xué)習(xí)方法,本小節(jié)和下面的一些連接針對這向你伸出援助之手,教你怎么學(xué)習(xí)創(chuàng)建你的應(yīng)用程序。
在此之前,先講一個要點:成功的應(yīng)用程序往往提供一個突出的用戶體驗。當(dāng)Android團隊構(gòu)建了一個有著健壯核心的系統(tǒng)時,大多數(shù)的用戶體驗將來源于用戶和應(yīng)用程序之間的的交互。因此,我們鼓勵你們花時間去構(gòu)建應(yīng)用程序優(yōu)秀的用戶體驗。
顯著的用戶體驗體現(xiàn)在三個核心特征上:1、快速;2、響應(yīng);3、無縫。當(dāng)然,自從計算機出現(xiàn)以后,每一個平臺都曾經(jīng)有過類似的三種性質(zhì)。盡管如此,每個平臺實現(xiàn)這些特性的方式也有所不同;下面將會簡單的介紹在Android 平臺下面你的應(yīng)用程序?qū)⑷绾芜_到這些要求。
快速(Fast)
Android 程序執(zhí)行應(yīng)該是很快的。當(dāng)然,準(zhǔn)確來說它的程序應(yīng)該執(zhí)行的很有效率(有效率才會快)。在目前的計算機世界里喲這樣一個傾向:假設(shè)摩爾定律能夠最終解決我們所有的問題。當(dāng)這種傾向遇到嵌入式應(yīng)用程序的時候,摩爾定律就變得有些復(fù)雜了。
與桌面和服務(wù)應(yīng)用程序不一樣,摩爾定律在移動設(shè)備應(yīng)用程序上面不是真正適用的。摩爾定律實際上是關(guān)于電子晶體管集成密度的,它原本的含義是:隨著時間的流逝,在給定的電路尺寸上面可以集成更多的電路。對于桌面和服務(wù)應(yīng)用陳旭來說,它的含義是:
你可以打包更多的“速度”在一個大小差不多的芯片上面,速度的提高,其他相應(yīng)的一些性能也會顯著的提高。對以像手機這樣的嵌入式應(yīng)用程序來說,相反的,使用摩爾定律是為了使芯片變得更小。這樣,隨著芯片密度的提高,相同功能的芯片會變得越來越小,功耗會越來越低,從而使手機做的越來越小,電池的持續(xù)的時間越來越長。這樣就導(dǎo)致手持嵌入式設(shè)備相對于桌面系統(tǒng)來說正在以一種比較慢二實際的速度在增漲。因而,對于嵌入式設(shè)備來說,摩爾定律就意味著更多的功能和更少的功耗,速度只是次要的。這就是我們要寫出高效代碼的原因:你不能天真的認(rèn)為電話在速度上面的增漲速度和桌面、服務(wù)應(yīng)用程序是一樣的。一般來說,高效的代碼意味著最小的內(nèi)存占用,意味著緊湊的風(fēng)格,意味著避免了因為某種語言和編碼習(xí)慣對性能的影響。我們用面向?qū)ο筮@個概念來理解,大部分這樣的工作都是在方法層面上,與實際的代碼,循環(huán)等等相類似。
在 “編寫高效Android” 一文中,我們會對此做詳細的介紹。
響應(yīng)(Responsive)
我們有可能能夠編寫贏得世界上所有的性能測試的代碼,但是用戶使用起來會感到很惱火,這是因為應(yīng)用程序沒有足夠的響應(yīng)性——讓人感覺反映遲鈍,在關(guān)鍵的時刻失靈,或者處理輸入太慢。在Android 平臺下,那些響應(yīng)性不夠的應(yīng)用程序會經(jīng)常彈出"Application Not Responding" (ANR)這樣的致命消息。
通常,這會在應(yīng)用程序不能響應(yīng)用戶的輸入的情況下發(fā)生。例如,你的應(yīng)用程序在一些I/O 操作上(如網(wǎng)絡(luò)接口調(diào)用)阻塞,這是主線程將不會處理用戶的輸入事件,一段時間之后應(yīng)用系統(tǒng)就會認(rèn)為你的程序掛起了,就會給一個選項給用戶詢問是否結(jié)束它。同樣的,如果你的應(yīng)用程序花費很多時間去構(gòu)建內(nèi)存中的一個結(jié)構(gòu)、或者計算游戲的下一步,這時系統(tǒng)同樣會認(rèn)為程序已經(jīng)掛起。當(dāng)碰到上面情況的時候,要確保計算的高效性,但是即使是最高效的代碼也需要花費時間。
在上面兩個例子中,問題的解決方案是建立一個子線程來處理大部分的工作,這樣就能保證你的主線程(響應(yīng)用戶界面事件)一直運行,這樣防止系統(tǒng)認(rèn)為你的程序已經(jīng)僵化。因為這種線程的實現(xiàn)一般在“類”(CLASS)這個層次上,你可以把響應(yīng)當(dāng)成是類的問題來處理(這里,可以和方法層次描述的基本性能相比較)。
這里只是一個簡單的介紹,在 “構(gòu)建響應(yīng)Android 應(yīng)用程序” 一文中對于應(yīng)用程序的響應(yīng)性有詳細的介紹。
無縫性(Seamless)
即使是你的應(yīng)用程序執(zhí)行很快,并且具有很高的響應(yīng)性,它仍然有可能讓用戶苦惱。一個常見的例子是后臺進程(比如Android 的 Service 和 BroadcastReceiver)對某些事件可能會突然彈出一個UI 響應(yīng)。這似乎是無關(guān)緊要的,并且一般開發(fā)者會認(rèn)為這這是正常的,因為他們花費了戴亮?xí)r間去測試和使用自己的應(yīng)用程序??墒?,Android 應(yīng)用程序模型的構(gòu)建是能夠允許用戶在不同的應(yīng)用程序之間進行流暢的切換。這就意味著,當(dāng)你的后臺進程實際上彈出那個UI 的時候,用戶可能正在系統(tǒng)的其他部分中,做一些其他的事情,如在接電話。想像一下,如果SMS 服務(wù)每次都會在文本消息傳入時彈出一個對話框,這很快就會使用戶崩潰。這就是為什么Android 標(biāo)準(zhǔn)對于這些事件使用的是通知(Notifications)機制;這使用戶能夠自己控制。
這僅僅是一個例子,相似的例子數(shù)不勝數(shù)。比如,如果Activities 沒有正確的實現(xiàn)onPause()方法和其他生命周期方法,這將會導(dǎo)致數(shù)據(jù)丟失?;蛘呷绻愕膽?yīng)用程序有意的暴露數(shù)據(jù)給其他應(yīng)用程序使用,你應(yīng)該使用一個ContentProvider,而不是用一個路人皆可見的未加工過的文件或者數(shù)據(jù)庫。
這些例子有一個共同的特點,他們都涉及到程序與程序或則程序與系統(tǒng)之間的交互。系統(tǒng)被設(shè)計為將多個應(yīng)用程序視為一種松耦合組件的聯(lián)合,而不是大塊的代碼黑盒。這就允許作為開發(fā)人員的你將整個系統(tǒng)看作是一個這些組件的大聯(lián)合。這允許你干凈地封裝,無縫地和其他應(yīng)用程序結(jié)合,因而你能設(shè)計自己喜歡的程序。
這使一種組件層次的概念(與性能和響應(yīng)的類層次和方法層次相對應(yīng))。至于怎樣編寫無縫性能很高的代碼, “與系統(tǒng)相結(jié)合” 一文中將會對此做出介紹,提供代碼提示和最佳實例。
構(gòu)建自定義組件
Android 中,你的應(yīng)用程序程序與View 類組件有著一種固定的聯(lián)系,例如按鈕(Button)、文本框(TextView), 可編輯文本框(EditText), 列表框(ListView), 復(fù)選框(CheckBox),單選框(RadioButton), 滾動條(Gallery), 微調(diào)器(Spinner), 等等,還有一些比較先進的有著特殊用途的View 組件,例如 AutoCompleteTextView, ImageSwitcher 和TextSwitcher。除此之外,種類繁多的像 線性布局(LinearLayout), 框架布局(FrameLayout), 這樣的布局組件(Layout)也被認(rèn)為是View 組件,他們是從View類派生過來的。
你的應(yīng)用程序就是這些控制組件和布局組件以某種方式結(jié)合顯示在屏幕上,一般來說這些組件對你來說基本夠用,但是你也應(yīng)該知道你是可以通過類繼承創(chuàng)建屬于自己的組件,一般可以繼承像View、Layouts(布局組件)這樣的組件,甚至可以是一些比較高級的控制類組件。下面我們說一下為什么要繼承:
· 你可以為實現(xiàn)某種功能創(chuàng)建一個完全自定義風(fēng)格的組件,例如用二維的圖形創(chuàng)建控制組件實現(xiàn)聲音的控制,就像電子控制一樣。
· 你可以把幾種組件結(jié)合形成一個新的組件,你的組件可能同時包含ComboBox
(一個能輸入的文本列表)和dual-pane selector control(左右兩個List 窗口,
你可以分配窗口每一項的從屬關(guān)系)等等。
· 你可以創(chuàng)建自己的布局組件(Layout)。SDK 中的布局組件已經(jīng)提供了一系列的選項讓你打造屬于自己的應(yīng)用程序,但是高級的開發(fā)人員會發(fā)現(xiàn)根據(jù)現(xiàn)有的
Layout 組件開發(fā)新的Layout 組件是很有必要的,甚至是完全從底層開發(fā)新的組
件。
· 你可以覆蓋一個現(xiàn)有組件的顯示或功能。例如,改變EditText(可編輯文本)組件在屏幕上的顯示方式(可以參考Notepad 的例子,里面教你如何創(chuàng)建一個下劃線的顯示頁面)。
· 你可以捕獲像按鍵按下這樣的事件,以一些通用的方法來處理這些事件(一個游戲的例子)。
為了實現(xiàn)某種目標(biāo)你可能很有必要擴展一個已經(jīng)存在的View 組件,下面我們結(jié)合一些例子教你如何去做。
基本方法(The Basic Approach )
下面的一些步驟都比較概括,教你如何創(chuàng)建自己的組件:
1. 讓你的類(Class)繼承一個現(xiàn)有的View 類或View 的子類。
2. 重載父類的一些方法:需要重載的父類方法一般以‘on’開頭,如onDraw(),onMeasure()和 onKeyDown()等等。 這個在Activity 或則 ListActivity 派生中同樣適用,你需要重載一些生命周期函數(shù)和一些其他功能性的HOOK 函數(shù)。
3. 使用你的繼承類:一旦你的繼承類創(chuàng)建完成,你可以在基類能夠使用的地方使用你的繼承類,但完成功能就是你自己編寫的了。
繼承類能夠定義在activities 里面,這樣你能夠方便的調(diào)用,但是這并不是必要的(或許在你的應(yīng)用程序中你希望創(chuàng)建一個所有人都可以使用的組件)。
完全自定義組件(Fully Customized Components)
完全自定義組件的方法可以創(chuàng)建一些用于顯示的圖形組件(graphical components),也許是一個像電壓表的圖形計量器,或者想卡拉OK 里面顯示歌詞的小球隨著音樂滾動。
無論那種方式,你也不能單純的利用組件的結(jié)合完成,無論你怎么結(jié)合這些現(xiàn)有的組件。
幸運的是,你可以以你自己的要求輕松地創(chuàng)建完全屬于自己的組件,你會發(fā)現(xiàn)不夠用的只是你的想象力、屏幕的尺寸和處理器的性能(記住你的應(yīng)用程序最后只會在那些性能低于桌面電腦的平臺上面運行)。
下面簡單介紹如何打造完全自定義的組件:
1. 最為通用的VIEW 類的父類毫無疑問是View 類,因此,最開始你要創(chuàng)建一個基于此類的一個子類。
2. 你可以寫一個構(gòu)造函數(shù)從XML 文件中提取屬性和參數(shù),當(dāng)然你也可以自己定義這些屬性和參數(shù)(也許是圖形計量器的顏色和尺寸,或者是指針的寬度和幅度等等)
3. 你可能有必要寫自己的事件監(jiān)聽器,屬性的訪問和修改函數(shù)和一些組件本身的功能上的代碼。
4. 如果你希望組件能夠顯示什么東西,你很有可能會重載 onMeasure() 函數(shù),因而你就不得不重載 onDraw() 函數(shù)。當(dāng)兩個函數(shù)都用默認(rèn)的,那么 onDraw()函數(shù)將不會做任何事情,并且默認(rèn)的 onMeasure() 函數(shù)自動的設(shè)置了一個100x100 —的尺寸,這個尺寸可能并不是你想要的。
5. 其他有必要重載的on... 系列函數(shù)都需要重新寫一次。
onDraw()和onMeasure() onDraw()函數(shù)將會傳給你一個 Canvas 對象,通過它你可以在二維圖形上做任何事情,包括其他的一些標(biāo)準(zhǔn)和通用的組件、文本的格式,任何你可以想到的東西都可以通過它實現(xiàn)。
注意: 這里不包括三維圖形如果你想使用三維的圖形,你應(yīng)該把你的父類由View 改為SurfaceView 類,并且用一個單獨的線程??梢詤⒖糋LSurfaceViewActivity 的例子。
onMeasure() 函數(shù)有點棘手,因為這個函數(shù)是體現(xiàn)組件和容器交互的關(guān)鍵部分,onMeasure()應(yīng)該重載,讓它能夠有效而準(zhǔn)確的表現(xiàn)它所包含部分的測量值。這就有點復(fù)雜了,因為我們不但要考慮父類的限制(通過onMeasure()傳過來的),同時我們應(yīng)該知道一旦測量寬度和高度出來后,就要立即調(diào)用setMeasuredDimension() 方法。
概括的來講,執(zhí)行onMeasure()函數(shù)分為一下幾個階段:
1. 重載的onMeasure()方法會被調(diào)用,高度和寬度參數(shù)同時也會涉及到
(widthMeasureSpec 和heighMeasureSpec 兩個參數(shù)都是整數(shù)類型),同時你應(yīng)該考慮你產(chǎn)品的尺寸限制。這里詳細的內(nèi)容可以參考View.onMeasure(int,int) (這個連接內(nèi)容詳細的解釋了整個measurement 操作)。
2. 你的組件要通過onMeasure()計算得到必要的measurement 長度和寬度從而來顯示你的組件,它應(yīng)該與規(guī)格保持一致,盡管它可以實現(xiàn)一些規(guī)格以外的功能(在這個例子里,父類能夠選擇做什么,包括剪切、滑動、提交異?;蛘哂貌煌膮?shù)又一次調(diào)用onMeasure()函數(shù))。
3. 一旦高度和寬度計算出來之后,必須調(diào)用setMeasuredDimension(int
width, int height),否則就會導(dǎo)致異常。
一個自定義組件的例子(A Customized Component Example)
在 API Demos 中的CustomView 提供了以一個自定義組件的例子,這個自定義組件在LabelView 類中定義。
LabelView 例子涉及到了自定義組件的方方面面:
· 首先讓自定義組件從View 類中派生出來。
· 編寫帶參數(shù)的構(gòu)造函數(shù)(參數(shù)可以來源于XML 文件)。這里面的一些處理都已經(jīng)在View 父類中完成,但是任然有些Labelview 使用的自定義組件特有的新的參數(shù)需要處理。
· 一些標(biāo)準(zhǔn)的Public 函數(shù),例如setText(), setTextSize(),
setTextColor()
· 重載onMeasure()方法來確定組件的尺寸(注意:在LabelView 中是通過一個私有函數(shù)measureWidth()來實現(xiàn)的)
· 重載onDraw()函數(shù)把Lable 顯示在提供的canvas 上。
在例子中,你可以通過custom_view_1.xml 看到自定義組件LabelView 的用法。在XML文件中特別要注意的是android:和app:兩個參數(shù)的混合運用,app:參數(shù)表示應(yīng)用程序中被認(rèn)為是LabelView 組件的個體,這些也會作為資源在R 類中定義。
組件混合技術(shù)Compound Components (or Compound
Controls)
如果你不想創(chuàng)建一個完全自定義的組件,而是由幾個現(xiàn)有組件的組合產(chǎn)生的新的組件,那么混合組件技術(shù)就更加適合。簡單的來說,這樣把幾個現(xiàn)有的組件融合到一個邏輯組合里面可以封裝成一個新的組件。例如,一個Combo Box 組件可以看作是是一個EditText 和一個帶有彈出列表的Button 組件的混合體。如果你點擊按鈕為列表選擇一項,
在Android 中,其實還有其他的兩個View 類可以做到類似的效果: Spinner 和
AutoCompleteTextView,,但是Combo Box 作為一個例子更容易讓人理解。
下面簡單的介紹如何創(chuàng)建組合組件:
1. 一般從Layout 類開始,創(chuàng)建一個Layout 類的派生類。也許在Combo box 我們會選擇水平方向的LinearLayout 作為父類。記住,其他的Layout 類是可以嵌套到里面的,因此混合組件可以是任何組件的混合。注意,正如Activity 一樣,你既可以使用外部XML 文件來聲明你的組件,也可以嵌套在代碼中。
2. 在新的混合組件的構(gòu)造函數(shù)中,首先,調(diào)用所有的父類的構(gòu)造函數(shù),傳入對應(yīng)的參數(shù)。然后可以設(shè)置你的混合組件的其他的一些方面,在哪創(chuàng)建EditText 組件,又在哪創(chuàng)建PopupList 組件。注意:你同時也可以在XML 文件中引入一些自己的屬性和參數(shù),這些屬性和參數(shù)也可以被你的混合組件所使用。
3. 你也可以創(chuàng)建時間監(jiān)聽器去監(jiān)聽新組件中View 類觸發(fā)的事件,例如,對List 選項單擊事件的監(jiān)聽,你必須在此時間發(fā)生后更新你EditText 的值。
4. 你可能創(chuàng)建自己的一些屬性,帶有訪問和修改方法。例如,允許設(shè)置EditText
初始值并且提供訪問它的方法。
5. 在Layout 的派生類中,你沒有必要去重載onDraw()和onMeasure()方法,因為Layout 會有比較好的默認(rèn)處理。但是,如果你覺得有必要你也可以重載它。
6. 你也可能重載一些on 系列函數(shù),例如通過onKeyDown()的重載,你可以通過按某個鍵去選擇列表中的對應(yīng)的值。
總之,把Layout 類作為基類有下面幾個優(yōu)點:
· 正如activity 一樣,你也可以通過XML 文件去聲明你的新組件,或者你也可以在代碼中嵌套。
· onDraw()函數(shù)和onMeasure()函數(shù)是沒有必要重載的,兩個函數(shù)已經(jīng)做得很好了。
· 你可以很快的創(chuàng)建你的混合組件,并且可以像單一組件那樣使用。
混合組件的例子(Examples of Compound Controls)
In the API Demos project 在API Demos 工程中,有兩個List 類的例子——Example 4
和Example 6,里面的SpeechView 組件是從LinearLayout 類派生過來,實現(xiàn)顯示演講顯示功能,對應(yīng)的原代碼是List4.java 和List6.java。
調(diào)整現(xiàn)有組件(Tweaking an Existing Component)
在某些情況下,你可能有更簡單的方法去創(chuàng)建你的組件。如果你應(yīng)經(jīng)有了一個非常類似的組件,你所要做的只是簡單的從這個組件派生出你的組件,重在其中一些有必要修改的方法。通過完全自定義組件的方法你也可以同樣的實現(xiàn),但通過沖View 派生產(chǎn)生新的組件,你可以簡單獲取一些已經(jīng)存在的處理機制,這些很可能是你所想要的,而沒有必要從頭開始。
例如,在SDK 中有一個NotePad 的例子(NotePad application )。該例子演示了很多Android 平臺實用的細節(jié),例如你會學(xué)到從EditView 派生出能夠自動換行的記事本。這還不是一個完美的例子,因為相比早期的版本來說,這些API 已經(jīng)感變了很多,但它確實說明了一些問題。
如果你還未查看該程序,現(xiàn)在你就可以在Eclipse 中導(dǎo)入記事本例程(或僅通過提供的鏈接查看相應(yīng)的源代碼)。特別是查看NoteEditor.java 中的MyEditText 的定義。
下面有幾點要注意的地方:
1. 聲明(The Definition)
這個類是通過下面一行代碼來定義的:
public static class MyEditText extends EditText
o 它是定義在NoteEditor activity 類里面的,但是它是共有的
(public),因此如果有必要,它可以通過NoteEditor.MyEditText 從
NoteEditor 外面來調(diào)用。
o 它是static 類(靜態(tài)類),意味著不會出現(xiàn)所謂的通過父類訪問數(shù)據(jù)的
“虛態(tài)方法”, 這樣就使該類成為一個可以不嚴(yán)重依賴NoteEditor 的單獨
類。對于不需要從外部類訪問的內(nèi)聯(lián)類的創(chuàng)建,這是一個很清晰地思路,保
證所產(chǎn)生的類很小,并且允許它可以被其他的類方便的調(diào)用。
o 它是EditText 類的擴展,它是我們選擇的用來自定義的父類。當(dāng)我們
完成以后,新的類就可以作為一個普通的EditText 來使用。
2. 類的初始化
一般來說,父類是首先調(diào)用的。進一步來說,這不是一個默認(rèn)的構(gòu)造函數(shù),而是
一個帶參數(shù)的構(gòu)造函數(shù)。因為EditText 是使用從XML 布局文件提取出來的參
數(shù)進行創(chuàng)建,因此我們的構(gòu)造函數(shù)也要取出參數(shù)并且將這些參數(shù)傳遞給父類。
3. 方法重載
在本例中,僅對onDraw()一個方法進行重載。但你可以很容易地為你的定制組
件重載其他需要的方法。
對于記事本例子來說,通過重載onDraw()方法我們可以在EidtView 的畫布
(canvas)上繪制藍色的線條(canvas 類是通過重寫的onDraw()方法傳遞)。
該函數(shù)快要結(jié)束時要調(diào)用super.onDraw()函數(shù)。父類的方法是應(yīng)該調(diào)用,但
是在這個例子里面,我們是在我們劃好了藍線之后調(diào)用的。
4. 使用定制組件
現(xiàn)在,我們已經(jīng)有自己定制的組件了,但是應(yīng)該怎樣使用它呢?在記事本例子中,
定制的組件直接在預(yù)定義的布局文件中使用,讓我們看一看res/layout 目錄
中的note_editor.xml 文件。
<view
xmlns:android="http://schemas./apk/res/android"
class="com.android.notepad.NoteEditor$MyEditText"
id="@+id/note"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@android:drawable/empty"
android:padding="10dip"
android:scrollbars="vertical"
android:fadingEdge="vertical" />
o 該自定義組件在XML 中是作為一個一般的View 類來創(chuàng)建的,并且是通過全路徑包來描述的。注意這里內(nèi)聯(lián)類是通過NoteEditor$MyEditText 來表示的,這是Java 編程中引用內(nèi)聯(lián)類的標(biāo)準(zhǔn)方法。
o 在定義中的其他屬性和參數(shù)將傳遞給定制組件的構(gòu)造函數(shù),然后才傳到EditText 構(gòu)造函數(shù)中,因此這些參數(shù)也是你使用EditText 組件的參數(shù)。注意,這里你也可以增加你自己的參數(shù),我們將在下面討論這個問題。這就是你全部需要做的,誠然這是一個簡單的例子。但問題的關(guān)鍵是:你的需求有多復(fù)雜,那么你的自定義組件就有多么復(fù)雜。
一個更為復(fù)雜的組件可能需要重載更多的on 系列函數(shù),并且還要很多特有的函數(shù)來充分實現(xiàn)自定義組件的功能。唯一的限制就是你的想象力和你需要組件去執(zhí)行什么工作。
現(xiàn)在開始你的組件化之旅吧
如你所見,Android 提供了一種精巧而又強大的組件模型,讓你盡可能的完成你的工作。從簡單的組件調(diào)整到組件混合,甚至完全自定義組件,靈活的運用這些技術(shù),你應(yīng)該可以得到一個完全符合你外觀要求的的Android 程序
Android 平臺的可選API
Android 適用于各種各樣的手機,從最低端直到最高端的智能手機。核心的Android API在每部手機上都可使用,但任然有一些API 接口有一些特別的適用范圍:這就是所謂的“可選API”。這些API 之所以是“可選的”,主要是因為一個手持設(shè)備并不一定要完全支持這類API,甚至于完全不支持。例如,一個手持設(shè)備可能沒有GPS 或Wi-FI 的硬件。在這個條件下,這類功能的API 任然存在,但不會以相同的方式來工作。例如Location API 任然在沒有GPS 的設(shè)備上存在,但極有可能完全沒有安裝功能提供者,意味著這類API 就不能有效地使用。
你的應(yīng)用應(yīng)該無障礙地運行或連接在一個可能不支持你API 的設(shè)備,因為你的設(shè)備上有這些上層接口(the classes)。當(dāng)然執(zhí)行起來可能什么也不會做,或者拋出一個異常。
每個API 會做些什么我們可以參考這些API 的說明文檔,你應(yīng)該編寫你的程序來適當(dāng)?shù)奶幚磉@類問題。
Wi-Fi API
Wi-Fi API 為應(yīng)用程序提供了一種與那些帶有Wi-FI 網(wǎng)絡(luò)接口的底層無線堆棧相互交流的手段。幾乎所有的請求設(shè)備信息都是可利用的,包括網(wǎng)絡(luò)的連接速度、IP 地址、當(dāng)前狀態(tài)等等,還有一些其他可用網(wǎng)絡(luò)的信息。一些可用的交互操作包括掃描、添加、保存、結(jié)束和發(fā)起連接。Wi-Fi API 在 android.net.wifi 包中。
定位服務(wù)(Location-Based Services)
定位服務(wù)允許軟件獲取手機當(dāng)前的位置信息。這包括從全球定位系統(tǒng)衛(wèi)星上獲取地理位置,但相關(guān)信息不限于此。例如,未來其他定位系統(tǒng)可能會運營,屆時,對其相應(yīng)的API 接口也會加入到系統(tǒng)中。定位服務(wù)的API 在android.location 包中。
多媒體API(Media APIs)
多媒體API 主要用于播放媒體文件。這同時包括對音頻(如播放MP3 或其他音樂文件以及游戲聲音效果等)和視頻(如播放從網(wǎng)上下載的視頻)的支持,并支持"播放URI地址"(Note:URI 即是統(tǒng)一資源識別地址)模式-在網(wǎng)絡(luò)上直接播放的流媒體。技術(shù)上來說,多媒體API 并不是“可選的”,因為它總是要用到。但是不同的硬件環(huán)境上面可能有不同的編解碼的硬件機制,因而它又是“可選的”。多媒體API 在 android.media 包中。
基于OpenGL 的3D 圖形(3D Graphics with OpenGL)
Android 的主要用戶接口框架是一個典型的面向控件的類繼承系統(tǒng)。但不要讓表面的情況迷惑了你,因為在它下面是一種非??斓?D 和3D 組合的圖形引擎,并且支持硬件加速。用來訪問平臺3D 功能的API 接口是OpenGL ES API。和多媒體API 一樣,

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多