2015年2月11日 星期三

(Android API Guides系列)Android 應用程式架構基礎(上)


  好久沒有更新啦~寒假到了人就特別閒一點。因為本人最近剛好在學Android,坊間的教材參差不齊,入門用雖有好書,但我個人覺得還是Google自己寫的比較完整一點(廢話...),且坊間參考書也多是參考Google寫的內容加以編纂。所以才突發奇想來翻譯一下(不過因為原來的也是有些冗長,我會把重要的地方盡量挑出來省自己和大家的時間),不知道可以翻幾篇(眼神死)。不過我是完全不專業的非本科系大四生一枚,只能希望有錯誤的地方大家多多指正,有問題的話....應該是幾乎沒辦法回答XDD
  
  以下內容都是針對Google API Guide - Application Fundamentals做翻譯。


  進入正題,相信在學習程式語言的朋友們應該都有聽過Java,雖然我完全沒有學過它,但靠著大二時修的C#(念作"see sharp,"Visual C系列的一種結合C與C++的物件導向語言,詳見維基百科),關於物件導向我於是也能略懂略懂。Android app就是以Java語言編寫而成的,寫好的程式碼會交給Google開源釋放出的SDK(Software Development Kit, 軟體開發套件)去編譯(Compile, 把人用的原始語言轉換成電腦看得懂的目標語言的動作),同時也會把程式需要用到的外部資料(例如圖片、音訊檔,或是版面配置檔等xml資源檔案)一起包含進去變成一個附檔名為.apk的APK(Android Package,應用程式封裝),也就是平常我們下載APP時會在手機裡的檔案夾看到的安裝檔。

  APP一旦安裝好了,該APP就會建立起自己的一塊安全領域(security sandbox):
  • Android的作業系統(OS, operating system)是支援全多工的 Linux-based系統,每一個APP都能夠獨立運作。 
  • Android系統預設上,會指派一組唯一的Linux ID給每一個APP(這組ID只會被此系統使用,即APP本身並不會看到),而這組ID會被Android系統用來識別出那些檔案是屬於哪個APP使用。 
  • 每個應用程式程序都擁有獨立的虛擬機器 (VM, Visual Machine), 所以APP的程式碼會獨立地運作,在它自身的Linux程序中。 
  • Android會在任一個有關於此APP的組件(Activities, Services...等,後面會再提到)被要求執行時啟動這個Linux程序,然後在不再需要或是系統必須空出記憶體給其他APP使用時關閉。
  因此,我們說Android系統滿足了最小許可權原則. 也就是說,每個APP在預設上只能利用其所必要之系統部分。 這使得APP在其使用上有保護整體裝置運作的安全性,只在使用者給予權限時能夠作動。

  不過其實,還是有方法可以讓APP之間分享資料或是主動存取系統服務的:
  • 我們其實可以安排讓兩個不同的APP分享同樣的Linux ID,藉此讓它們都能存取各自的資料。為了保留系統資源,共有同樣ID的APP甚至還能夠在同一個Linux程序下執行並共有同一台虛擬機器(惟這兩個APP必須也使用同樣的憑證) 
  • 我們都知道,APP可以要求使用者提供權限以存取使用者的裝置資訊,例如通訊錄、簡訊、SD卡資料、相機、藍芽...等等。這些權限必須在安裝時被使用者允許,否則將無法進行安裝程序。
  以上大概已經介紹了Android app存在於系統中的基本樣貌,在剩下來的文章中我們將繼續看到:
  • 定義APP的核心架構組件(Core Framework Components) 
  • 清單檔(Manifest file),可用來宣告APP之組成內容及必要之裝置特性。 
  • 分離於APP程式碼,且讓使用者的APP能夠在不同的裝置組態中最佳化的資源檔(Resource files)

App核心架構組件(App Components)


  App組件是組成Android應用程式的積木,每一個組件都可以是一個不同的程式進入點,但這僅是對手機系統而言;對使用者來說,這些進入點可能必須互相關聯(也就是說使用者無法直接跳過某個組件進到下個組件)。但是,這些組件在系統中確實是獨立存在的個體且各自擁有明確的定位:每一個組件都能幫助定義開發者設計的應用程式之整體表現。

  架構中共有四種不同的組件,各自有不同的目的及生命週期(Life cycle, 定義該組件如何創生及消滅):
Activities(近似於網頁的頁面,或是C#之form元件)
  Activities代表著單一個視窗(screen)以及其使用者介面(UI, user interface)。舉例來說,一個郵件APP可能會有一個activity呈現出一列新的郵件,另一個activity讓使用者撰寫郵件,再一個讓使用者閱讀郵件。雖然他們可能呈現出一體感(介面上、使用者操作使用上),但其實他們是獨立運作的個體(透過intent來聯繫,透過bundle來傳遞資料,後面會再提)。 
  如此一來,就能夠讓activities達到共用的目的,使用者可以在不同的APP裡開啟同樣的activity(如果在郵件APP中我們允許此行為,可能可以利用public class的方式實作)。舉例來說,假設今天另有一個拍照APP想要把照片利用郵件傳遞給別的使用者,我們可以設計使拍照APP多一個撰寫郵件的功能,而這只需要連接到剛才提到的郵件APP中的撰寫郵件activity就能達成。
  最後要提的是,每個activity都是繼承(extend)於Activity類別的子類別(subclass),這點在實際撰寫程式時會注意到。有興趣的人可以進一步參閱Google Activities developer guide.
Services(用在通常會在背景運作且不會太耗電的程序)

  Services與Activities的差異在其背景工作/進行遠距程序的能力,且Service沒有UI。舉例來說,一個service可以讓你在播放音樂的同時,操作各種不同的APP;或是可以讓你在和某APP的activities互動時,繼續從網路獲取資料。Service可以由另一種組件,例如activity,啟動或是結合,使得這些組件互相關聯。
  同樣地,service是繼承於Service類別下的子類別,有興趣的人可以進一步參閱Google Services developer guide
Content providers(管理資料的部門)

  Content provider用來做資料管理,從字面上可以理解為一種提供資料、內容的部門。通常我們可以在Android系統中以檔案系統, SQLite資料庫, 網路, 或是其他你的APP能存取的常駐儲存位置(雲端、SD卡等)。利用content provider,其他APP可以查閱或甚至更改資料內容(如果該content provider提供權限的話)。舉例來說,有用來管理用戶的連絡資訊的content provider,當有任何APP需要存取或變更關於聯絡人的資訊時,就能夠透過適當的content provider(例如ContactsContract.Data)達到目的。因此當然也能設定為只能被特定的APP給存取,例如開發者專為其APP設計之資料庫,或是用來儲存筆記的Note Pad 範例程式。(Note Pad程式碼可以從Android Studio的Import sample取得,在選單列的File-->Import sample找尋即可)      
  利用content provider建立instance(屬於某類別的物件,關於物件導向介紹,請看這裡)時,此instance的類別也是隸屬於ContentProvider的子類別,且必須實作標準的應用程式介面(API, 軟體系統溝通的橋樑)來讓其他APP也能獲取此content provider的內容。更多訊息請參照這裡

Broadcast receivers(接收通知的捕手)


  Broadcast receiver是用來回應從系統其他部分傳來的通知(system-wide broadcast announcement)的組件,在手機裡,許多receivers都是來自系統,例如螢幕關閉通知、低電量通知、檔案已下載完成通知等等。在APP裡也可以啟動通知,舉例來說,在使用A程式時,你可能會收到來自B程式的broadcast,通知你某某檔案已經下載完成,可以使用。雖然broadcast receiver不會呈現UI,但可以建立狀態列通知(status bar notification, 就是螢幕上方那條)來告知使用者:有廣播事件(broadcast event)被觸發了。更普遍的情況是,broadcast receiver扮演著連接其他組件的"通道"的角色,且通常只用來執行小量的工作。例如根據被觸發的event來觸發一個service以完成某目標,但本身只是中介的角色。


  Broadcast receiver實作於BroadcastReceiver類別下,且每一個broadcast都是以Intent物件的形式傳遞。更多訊息請參照這裡

  Android系統的一個獨特部分是其可以讓任何一個APP啟動另一個APP的組件。舉例來說,如果你想要讓使用者拍張照,你可以找到另一個APP所開發的拍照activity,並利用它來為你的程式拍張照。神奇的是,你不需要合併或甚至連結該activity的程式碼,你只需要啟動該activity並拍張照。拍完之後,照片甚至可以傳回到你的APP裡頭供你使用。從使用者經驗的角度來說,這就好像相機內建在你的APP裡面一樣。

  當系統啟動一個Android組件時,同時也啟動了該APP的程序(如果該程序還沒執行)和實作了一個該組件類別之instance。舉例來說,如果你的APP開啟了拍照APP中的拍照activity,則該activity實際上是運作在拍照APP的程序之中。因此,有經驗的程式設計師會發現Android apps沒有一個單一的進入點(像是Arduino, Processing, C#等都會有的main()或setup()或loop()方法)。
  由於前面提到Android系統獨立運作各APP並設定存取權限的緣故,我們的APP無法直接活化另一APP裡的組件。但是這些APP上層的Android系統就可以。所以,如果想要做這件事,我們需要一個叫做Intent(坊間翻作意圖)的物件來乘載給系統的要求。如果系統認可,系統就會下海幫你完成這件事情。

活化組件(Activating Components)


  上面提到的四種組件中,有三種:Activities, services, and broadcast receivers能夠被Intent這種非同步(asynchronous message, 我的理解是因為其無法馬上要求回覆的特性,因此稱為非同步)的物件給活化。Intents會在運行時把獨立的組件們給連結起來(你可以把Intents想像為郵差或信使,他們會從一個組件傳遞要求到另一個組件)以傳遞資料或要求動作,因此也有參考書稱之為超連結元件。
  
  Intent的instance可以用Intent intent=new Intent();的方式產生,再由開發人員決定要賦予其甚麼樣的任務(此時再決定要活化甚麼組件,可以是開發人員自己編寫的activity);或是也可以直接定義intent本身的任務(直接指明要活化的組件,通常是內建的內容),如Intent intent=new Intent(動作,內容)的方式,其中Intent方法的參數分別是動作(例如瀏覽網站就用內建的ACTION_VIEW方法、撥號則是ACTION_DIAL...)及內容(例如瀏覽網站時就是Uri.parse("要瀏覽的網址")、撥號則是Uri.parse("tel:電話號碼"))。
  
  如上所述,對於activities和services而言,intent定義了要進行的動作(例如看某張圖片或傳某種資料),也可以指明將要進行操作的資料之統一資源標示符URI(Universal Resource Identifier,可以當作是資料的ID,如上面用到的Uri.parse()方法就是利用這個ID去解析所要的網頁、電話號碼)。舉例來說,一個intent也許會傳遞一個由某個activity發出的,要求show出一張照片或打開一個網頁的指令,這時候就會用到URI。在某些例子之中,我們可以讓一個由intent活化的activity再用intent的形式傳回結果(可能用bundle的形式打包資料),舉例來說,我們可以在activity中創造一個intent,來讓使用者挑選一個聯絡人,再把使用者挑選的結果用另一個intent傳回來給activity,這時這個intent也會包含了關於該聯絡人的URI。關於URI,這裡有更詳細的解說。
  
  對broadcast receiver來說,intent僅定義了需要被廣播的通知內容(例如低電量通知的intent中可能只有一個已知的字串"電源不足"。

  最後一個組件類型content provider,無法被intent活化。這類組件的活化條件是收到來自ContentResolver的要求時。Content resolver物件處理了所有跟content provider的資料交換,因此發出要求的組件不需要也無法直接觸及content provider,這種類似白手套的機制是為了系統的資訊安全,在計算機科學中被稱為抽象化(Abstraction),是一種把系統理想化進而提高可開發性的概念。在這裡的意義是我們可以只關心content resolver這個現成類別的方法如何被我們利用,不需去管在檯面下的資料如何被交換。
  Google提供了分別的方法以活化不同類別的組件:
  

想知道更多有關intent的資訊的人,可以參照Intents and Intent Filters (官方提供);更多關於活化特定組件的資訊也可以參照以下連結 : Activities, Services, BroadcastReceiver , Content Providers(官方提供)

  這篇上集就先到這裡,主要我是希望能詳實地呈現原汁原味,所以沒什麼刪減(最基本的東西是最難刪減的,因為最重要....)。有些地方可能翻得不太好,有些地方我引入自己學習Android半個月以來(無誤)的經驗,同時因為我把目標觀眾設定為"已經安裝好Android Studio或Eclipse,且已經看過一些參考書,實作過一點東西,但想知道更多更詳細的人",所以我不會教大家怎麼設置開發環境,請大家動動手指Google一下相信一定有成千上萬種教學XDD。

P.S.下集不知道甚麼時候會寫好XDD





沒有留言:

張貼留言