Android真正獨特的地方在於它允許多個任務同時運行。由於開發者們來自不同的平台,對這樣的運行機制可能會感到驚訝。深入理解它的行為對你的應用程序設計是很重要的,因為這樣可以無縫的(seamlessly)集成到android的其他版本/平台。本文涵蓋了android多任務設計的原因,和它是怎樣影響(impact on)你的應用程序工作的以及你要怎樣才能最好的發揮出android的優勢特色。
設計要素
移動設備有技術局限性,並且用戶體驗需求不同於桌面或web系統。下面是我們設計android多任務時的4個關鍵的約束條件(constraints):
我們不想要求用戶在“完成”的時候關閉程序。那樣的使用模式(指完成後立即關閉程序)在移動(mobile)環境工作的不是很好,特別是每天都要大頻率反復使用的程序。
移動設備沒有那麼奢侈(luxury)的交換空間(指的內存RAM),在內存使用上又有很嚴格的限制。Robert Love(此人寫了很多linux的書籍,本人有一本《linux系統編程》)寫過一篇關於本主題的非常好的文章。
應用程序在移動設備上切換啟動(switching on)是極其受到挑剔的,我們的目標是顯著的減少到少於1秒鐘來啟動一個新的程序。當用戶在很少的程序中切換時,這點尤為重要,比如說當觀看視頻時切換出來查看一個條新短信,然後再退回觀看視頻。一個顯而易見的(noticeable)等待在此種狀況下會很快引起用戶對你的憎恨。
可用的API對編寫內置的Google應用程序來說應該必須足夠(be sufficient for),這也是我們的“所有程序生來平等”哲學的一部分。這就是說後台的音樂播放,數據同步,GPS導航,應用下載等必須可以讓第三方開發人員用同樣的API來實現。
前兩點需求強調了一個有意思的沖突(conflict)。我們並不是要讓使用者擔心關閉他們的應用,寧可讓所有的應用一直運行著。但同時,移動設備的內存使用卻很有限,以至於系統會降低性能或者當它需要比可用更多的RAM時,很快就啟動失敗,而台式電腦,可以交換(指空間),相比起來就很簡單的啟動(運行)緩慢,來用頁面RAM交換空間。這些相互矛盾的約束就成了android設計的動機。
什麼時候應用程序“停止”呢?
一個Android多任務普遍的誤解是關於一個進程(process,也可以譯為程序)和一個應用(application,也可以譯為程序)之間的不同之處。在Android中這兩者並不是緊密聯系的一個實體:應用出現在用戶面前可能並不需要實際的進程運行在程序中;多個應用可以共享同一個進程,或者一個應用可能使用多個進程,這些都取決於你是否需要;當應用並不積極的做什麼事情的時候,它的進程就會由Android保存。
實際上你看一個應用的進程“運行”不是說這個應用在運行或做什麼事情。它簡單的在那裡可能只是因為Android在某些時候需要它,並且讓它最好還是在那裡因為可能還要再次需要它。同樣地,你可能會離開一個應用一會兒,並從停止的地方回來,在這段時間,Android可能需要取消(get rid of)進程的其他事情。
Android此時處理應用的一個關鍵是進程不會干淨的關閉。當用戶離開一個應用程序,它的進程一直在後台存在著,如果需要的話,允許它繼續運行(例如下載網頁),當用戶返回的時候能夠立刻回到前台。如果一個設備不出現內存溢出的話,Android將保證所有的進程都在,真正的讓所有應用永遠“運行”。
當然,這裡有個內存的限制,為了解決(accommodate)這個問題,Android必須決定何時移除不再需要的進程。這引出了Android進程的生命周期,這個規則用來決定每個進程的重要程度,哪些進程需要被拋棄。這些規則基於兩點,一個是用戶當前使用中的進程的重要級別,另一個是進程從上次用戶需要到現在經過了多長時間。
一旦Android決定要移除一個進程,它會殘忍的(brutally)、簡單的強制殺掉它。內核會立刻回收再利用(reclaim)進程的所有資源,不需要應用程序良好的編碼和禮貌的響應退出請求。允許內核立即回收再利用資源就可以很容易避免一系列的內存溢出問題。
如果一個用戶要回到一個程序,而這個程序恰巧被殺掉了,Android需要一種途徑重新加載它並回到最後看到的狀態,來保持“所有的程序永遠運行”的用戶體驗。通過記錄(is keep track)用戶知道(is aware of)的程序的一部分(activity)的信息,就是他們看到的最後狀態,重新啟動它們。這裡說的最後的狀態是指用戶離開程序時的狀態,並不是它被殺掉時的狀態,所以內核不用依賴程序在某一點的正確響應而自由的殺掉它。
有些情況,Android的進程管理可以看作為是某種形式的空間交換:程序的進程代表一定數量的(a certain amount of)在用內存;當內存過低時,殺掉一些進程(交換出去);當又需要那些進程時,它們可以從最後的狀態重新啟動(交換進來)。
明確地運行在後台
到目前為止,我們有一種不明確的方法令程序運行在後台,就是只要求進程不被Android的內存管理部分殺掉。這對於像在後台加載網頁等任務還行,如果有更高的要求怎麼樣?比如後台播放音樂,數據同步,定位、鬧鐘等。
對於這些任務,應用程序需要一種途徑來告訴Android“我在這一點上要明確的運行”。這裡有兩種主要的設施可用來為應用程序解決此問題,它們以兩種組件為代表,廣播和服務,可以在它們的清單中(manifest)注冊。
廣播接收器
廣播接收器允許一個應用程序運行,在短暫的時間裡,在後台處理一些發生的事情。它可以用多種途徑來構建高級別的設施:例如警告管理器允許程序在未來某個時間發送一個廣播,位置管理器可以在發現一個感興趣的位置變化時發送一個廣播。因為接收器的信息是程序清單(manifest)的一部分,Android可以在這個程序沒有運行時找到並加載它;當然,如果它的進程在後台可用,廣播就可以高效的直接投遞給它。
在處理廣播時,程序會給定一個允許它工作的時間(當前是10秒鐘)。如果在這個時間內沒有完成,程序就會被認為是違反規定的(misbehaving),它的進程會立即被扔到(tossed)後台狀態,當內存需要時被殺掉。
廣播接收器擅長做一些需要響應外部刺激的工作,例如發送一個新的GPS位置報告後給用戶發布一個通知。在程序的進程因接收廣播而露面後,它們就變成了非常輕量級的。因為它們是再特定的時間活動,有很強的保證在它們運行時進程不會被殺掉。然後這並不適用於(appropriate for)一些不確定時間的情況,如網絡。
服務
服務允許應用程序實現長時間的後台操作。當然了,事實上服務也提供了很多其他方法,但這裡我們討論的是服務的最基本目的,一個應用程序說“嗨,我想要持續運行我的程序在後台,直到我說我完成了。”應用程序控制服務的運行,通過明確的啟動和停止它。
當服務提供富客戶服務器模型時,使用它是可選擇的。一旦啟動了程序的服務,Android就會實例化這個組件並在程序的進程中提供上下文。在這之後如何使用取決於程序:它可以在服務中放入所有需要的沒有和程序的其他部分交互的代碼,在其他單例(singleton)對象共享給程序的其他部分調用,直接恢復服務實例在其他需要的地方,或者在另一個進程中運行並在需要時做一個完全的(full-blown)遠程過程調用(rpc)。
服務的進程管理與廣播接收器的不同,因為不清楚數量的服務可以運行未知長度的時間。這裡可能沒有足夠的RAM來滿足服務運行的要求,所以不能夠強烈保證它們能夠永久運行。
如果只有特別少的RAM,進程維護的服務會像後台進程一樣立即被殺掉。然而,如果合適的話,Android會記下那些願意繼續運行的服務,在有足夠RAM後再重新啟動它們的進程。例如,如果用戶使用網頁需要大量的RAM,Android會同步的殺掉後台服務進程直到浏覽器對內存的需求降低。
服務可以更深層次的討論它們被認為是“前台”的行為。這裡服務就處在一個“請不要殺掉我”的狀態,但是這需要它包含一個給用戶的積極運行的通知。這對於後台播放音樂或車載導航的服務非常有用,因為用戶知道(aware of)這些;當你播放音樂和使用浏覽器時,你可以經常在狀態欄上看到音樂播放的標記(glyph)。Android不會嘗試去殺掉這些服務,不過作為一種交換(trade-off),要保證用戶知道它們並且可以在需要時明確的停止。
通用組件的值
Android的通用的廣播接收器和服務組件允許開發者創建一個廣泛的多種多樣的有效後台操作,包括一些最初從沒有考慮過的事情。在Android 1.0,它們被用來實現近乎所有內置的後台行為和所有的Google應用:
音樂播放運行在服務中,當用戶離開音樂程序時允許繼續對其進行操作。
鬧鐘通過鬧鐘管理器安排一個廣播接收器,在下一次設置時間響起(go off)。
日歷程序同樣地(likewise)在下一個日歷事件中安排一個鬧鐘在適當時間來顯示或更新它的通知。
當進程中有下載時,後台文件下載通過服務來實現。
電子郵件程序安排一個鬧鐘每隔一段時間或由新郵件到來時來喚醒一個服務。
Google應用程序維護一個服務來接收網絡上的通知,並依次(in turn)發送廣播給那些需要做工作的單獨的程序比如同步聯系人。
隨著平台的演化,這些基本的組件用來實現很多重要的新的開發特性:
輸入法被開發者用服務組件來實現了,Android管理和使用其為當前的輸入法。
程序的widget(窗體小部件)是廣播接收器,當其需要交互時,Android就給它發送一個廣播。這需要widget很輕量級的,並不需要它的程序進程一直保持運行。
可接入特性(Accessibility features)是用服務實現的,使用時由Android維持運行並發送適當的用戶交互的信息。
同步適配器是在Android2.0中引入的,它是運行在後台的服務,當特殊的數據同步需要時被執行。
活動壁紙是一個服務,當被用戶選擇時就會被Android啟動。