關於Android四大組件最權威最深入最準確的解讀(毫不標題黨)

回覆「1024」,送你一個特別推送程序員


投稿做者:milter
原文連接:http://www.jianshu.com/p/07b87084337f

特別聲明:本文爲milter原創並受權發佈,未經原做者容許請勿轉載,轉載請聯繫原做者。數據庫

這篇文章翻譯自Aannie Hackborn發表在google+上的一篇post,她是google資深大牛,2005年就進入Android Framework團隊。即便在google內部,論起對Android系統的理解把握,鮮有出其右者。在文章中,她深入地闡明瞭Android設計四大組件的初衷,各個組件的目的做用,適用情景。我相信,讀完此文,你會以爲從新認識了Android。若是想閱讀原文,請在google+上搜索Aannie Hackborn。設計模式


「我應該怎樣設計個人APP?我應該採用什麼樣的架構模式?我須要使用event bus嗎?」promise


咱們常常看到Android平臺開發者詢問在APP中採用什麼設計模式和架構之類的問題。可是答案極可能會令你驚訝,那就是,咱們(咱們指的是Android Platform Team)對此並無一個明確的觀點,甚至能夠說咱們壓根就沒有觀點。緩存


你應該使用MVC仍是MVP仍是MVVM?我不知道。實際上,我在學校時只知道MVC,其餘的架構模式是我臨時google搜索後才寫在這裏的。安全


這也許使人吃驚,由於Android給人的感受是,它對應當怎麼寫APP有着本身很強烈的見解,這種見解體如今它的Java APIs和四大組件等一些高級概念上,這些東西看上去組成了一個典型的應用開發框架,告訴咱們開發者,你應當這樣去實現你的功能。但實際狀況是,根本不是這樣。性能優化


咱們能夠將Android核心APIs(core APIs)叫作「系統框架」(system framework),而平臺APIs(platform APIs)最主要的功能是定義APP應如何與操做系統交互,與APP內部運行邏輯絕不相關。微信


我的理解:能夠簡單地將core APIs看作操做系統內核,而將platform APIs看作咱們常說的Android Framework。架構


這就是說,對於platform APIs,從開發者角度與從操做系統角度看其功能做用常常是不一致的,這很容易讓人們在使用中感到困惑。框架


舉例來講,咱們來考慮一下操做系統是怎樣定義「怎樣運行一個APP」的。在一個經典的系統裏,最基本的就是要求APP包含一個main方法,裏面定義了本身要怎樣運行:



我的理解:本文的核心思想就是說明,所謂的四大組件,只是讓你的APP告訴操做系統,本身要怎樣運行而已,跟怎樣設計本身的APP,壓根沒有關係。傳統的應用經過一個main方法,告訴操做系統:「嘿哥們,main方法就是個人入口,請從這個方法開始運行我。」而Android卻給了你四個選擇,每個組件都是讓操做系統運行你的APP的一種入口。


好了,操做系統要運行你的APP了,因而它調用你的main方法,而後你的應用就開始運行了,你能夠作任何你想作的事情,直到你認爲本身完成任務爲止。請注意,這裏要求你定義main方法,並非要求你去作什麼事,或是完成一個叫作main的功能,main方法全部的做用僅僅是提供一個APP運行入口而已。


可是在Android的世界,咱們決定,咱們不要一個明確的main方法做爲APP的入口。由於咱們須要讓系統對APP怎樣運行有更多的控制權。尤爲是,咱們但願構建一個這樣的系統,在該系統中,用戶永遠不須要考慮開啓和中止一個APP,而把這些事交給系統去管理。因此,系統須要知道更多的每一個APP的內部運行狀況,以便可以在須要的時候,以定義好的方式啓動APP,即便該APP當時並不在運行。


我的理解:這個系統所須要瞭解的每一個APP的內部運行狀況,其實就是Manifest.xml文件中的內容。


爲了達到這一點,咱們將一個APP的main方法分解成幾種系統能夠與之交互的形式。這幾種形式就是Activity,BroadcastReceiver,Service和ContentProvider APIs,廣大的Android開發者都很熟悉它們。


這些類好像在告訴你,你的APP內部應當怎樣工做,但這是一種誤解!事實上,這些類只是定義你的APP須要怎樣與系統交互(以及系統怎樣協調你的APP與其餘APP進行交互)。這種與系統的交互一旦開始,系統就再也不關心你的APP內部是怎樣運行了。


爲了更好地說明這一點,讓咱們簡要地看看這些APIs對於Android系統來講到底意味着什麼。


Activity

這是一個APP與用戶交互的入口。從系統的角度看,系統爲Activity提供的關鍵交互動做是:

  1. 持續跟蹤用戶當前正在關心的(也就是顯示在屏幕上的東西),以確保當前進程保持運行。


    我的理解:這裏,做者實際上的含義是,當你的應用被系統從Activity啓動時,在Activity的start與stop狀態之間,系統會確保這個Activity始終佔據着設備的屏幕,而且確保你的應用毫不會被系統殺死。這是你從Activity啓動本身的APP時,系統給予你的APP的一種承諾(just a promise)。


  2. 知道那些以前使用過的進程,這些進程包含着用戶可能會返回獲取的東西(stopped activities),並所以給予這些進程更高的優先級。


  3. 幫助應用處理進程被殺死的狀況,以便用戶可以返回到以前的activities,而且這些activities可以加載本身以前的狀態


    我的理解:很顯然,系統所承諾的這種狀態恢復能力,是依靠Activity的 onSaveInstanceState和onRestoreInstanceState方法,也就是說,你在Save方法中保存好你想在進程被殺死時想要保存的Activity狀態,而後你就能夠在Restore方法中獲取這些狀態以恢復Activity。當你把這些作完後,剩下的就是系統的事情了,系統會承諾,若是因爲內存壓力殺死了你的Activity所在的進程,那麼當你返回時,系統會重建你的應用進程,並幫助你恢復以前Activity的狀態。


  4. 提供一種在不一樣應用之間的用戶流(user flow)的方式,固然這要靠系統來協調。最經典的例子就是分享功能的實現。


對於Activity來講,系統並不關心的是:

一旦系統從Activity入口進入到你的APP UI之中,系統將再也不關心Activity內部邏輯的組織。你能夠將全部的應用邏輯全放入這一個Activity中,好比你能夠手動地改變它的views,使用fragments或者其餘框架,你也能夠把你的應用邏輯分拆成額外的內部activities。你也能夠三者同時使用(指的是改變views,使用fragemnts,分拆成額外的activities)。這些事情繫統是絕不關心的,只要你遵循Activity與系統之間的約定(在適當的狀態下啓動它,正確地保存/恢復它的狀態)。


BroadcastReceiver

這是一種讓系統在正常的用戶流(user flow)以外,傳遞事件給APP的機制。最重要的是,由於這是另外一個被精心定義的APP的入口,即便APP當前並不在運行,系統也能夠將broadcasts傳遞給APP。因此,舉例來講,一個APP能夠提早調度一個alarm,以便通知用戶一個立刻到來的事件,經過將這個alarm傳遞給該APP的一個BroadcastReceiver,在alarm發生以前,APP都不必運行。


對於BroadcastReceiver來講,系統並不關心的是:

在APP內部分發事件是一個與BroadcastReceiver接收事件徹底不一樣的事,無論你是使用一些eventbus 框架,實現你本身的回調系統,仍是任何其餘方法...你都沒有理由使用系統的廣播機制,由於你並非在App之間分發事件。(事實上,不使用系統的廣播機制還有一個很好的緣由,這會帶來許多沒必要要的負擔,並且使用全局廣播機制來實現APP內部的事件分發會引起許多安全問題)。固然,咱們也提供了一個LocalBroadcastManager便利類,它實現了一個純粹的進程內的intent分發系統,並且它的API與系統BroadcastReceiver API很類似,若是你喜歡固然也可使用。但再次強調,你沒有理由在僅僅發生在APP中的事情上使用BroadcastReceiver機制。


Service

當因爲各類各樣的緣由須要APP在後臺運行時,Service就是一個這樣的入口。有兩種語義上大相徑庭的Services(一種是Started Service,一種是Bound Service)來告訴系統怎樣管理一個APP。


Started Service就至關於由於某種緣由你的APP告訴系統:「系統大哥,我有事要幹,請讓我一直運行,直到我告訴你我幹完了。」(這裏的我至關於APP,由於此時的Service就表明了APP,而系統是隻跟APP對話的)這裏的「事」多是在後臺同步數據或者在用戶離開APP後播放音樂。


同時,Started Service又有兩種,一種是用戶可感知的,一種是用戶沒法感知的。這兩種不一樣的Started Service會讓系統對它們採起不一樣的管理方式。


(此圖爲本人草畫,方便你們理解)

  1. 播放音樂的Service是用戶能夠直接感知的,因此Service會對系統說:「我想成爲前臺(foreground),而且在通知欄掛一個通知,讓用戶可以感到個人存在。」這種狀況下,系統知道,必須使出吃奶的勁保證這個service進程的運行,由於若是這個進程宕掉,用戶會不高興。


  2. 另外一種後臺Service是用戶沒法直接感知的,因此係統能夠更加靈活地處理這個Service的進程。在系統急需RAM以保證用戶眼前的事情正常運轉時,系統可能會容許該進程被殺死(而後能夠在以後有能力時再啓動該Service)。


Bound Service 之因此會運行,是由於其餘APP或者系統要使用它。一般狀況該Service都會給其餘進程提供一個API。在這種狀況下,系統知道這兩個進程之間存在一個依賴關係。因此,若是進程A綁定了進程B中的一個Service,系統就會知道,它要爲進程A保證進程B和它裏面的Service正常運行。進一步講,若是進程A是用戶當前正在關心的進程,系統將知道把進程B也看成用戶正在關心的進程。


因爲Service的靈活性(有好也有壞),Service已經成爲各類類型的系統中一個很是有用的構建塊(building block)。實時牆紙,通知監聽器和許多其餘的系統核心特性都被構建爲Service,當它們須要運行時,系統再綁定它們。


對於Service,系統不關心的是:

Android不關心你的APP中那些不影響它怎樣對待你的進程的事,因此這些狀況下,是沒有理由使用Service的。舉例來講,若是你想在後臺爲你的UI下載數據,你不該該使用Service來作這件事----作這些事時,不告訴系統保持你的進程運行真的是很重要的,由於確實沒有必要!!這樣作也讓系統有更多的自由去管理你的進程,以便與用戶正在作的事情相協調(注:可讓系統在內存緊急的狀況下,殺死你的進程,優先保證用戶正在作的事情,這裏忍不住吐槽一句:每一個APP確定都會以爲本身是最重要的哈,Google開發Android的人也是典型的理想主義!


若是你只是簡單地開啓了一個後臺線程來作數據下載(或者其餘不是Service的辦法),你將會獲得你想要的結果:若是用戶在你的UI裏,系統將會確保你的進程運行,因此下載毫不會被中斷。當用戶離開了你的UI,你的進程仍將被保持(緩存)於是能夠繼續下載數據,只要RAM不告急就行。


一樣,爲了將你的APP的不一樣部分鏈接起來,你也沒有理由去綁定同一個進程中的Service。這樣作卻是沒有什麼明顯的害處,由於系統會看到,該進程對本身有一個依賴,它將會忽略這個依賴,仍將你的APP看成普通進程看待。可是這樣作對你和系統實際上都是沒有必要的。做爲替代,你可使用單例或者其餘進程內的模式來將你的APP的各部分鏈接到一塊兒。


ContentProvider

最後,ContentProvider是一個專用的辦法,用來將你的APP的數據公開到其餘地方。人們一般會將它們看成對數據庫的抽象,由於有許多的API和支持庫就是這樣使用ContentProvider的。可是從系統設計的角度,這並非ContentProvider的初衷。


對於系統來講,ContentProvider其實是一個入口,用於獲取一個APP內部的公開的被命名的數據項(data items),每一個數據項都被一個URI scheme所標識。這樣,APP就能夠決定怎樣將本身的數據項映射到一個URI scheme,怎樣將這個URI scheme公開給其餘APP或者系統,好讓APP或者系統使用這個URI scheme來獲取本身內部的數據。這將讓系統可以用一些很獨特的方式來管理你的APP:

  1. 將URI scheme公開出去並不要求你的APP一直保持運行,因此即便你的APP沒有運行,這些URI scheme也能夠公開給任何APP和系統。只有當某我的告訴系統:「請把這個URI表明的數據拿給我。」時,系統將會讓你的APP運行起來向你索要對應於URI的數據項並返回給請求者。


  2. 這些URIs也提供了一個很重要的細粒度的安全模型。好比,你的APP能夠將表明一張你的APP內的圖片的URI放在剪貼板上,可是讓它的ContendProvider 保持在鎖定狀態,因此沒有人可以自由地獲取它。當其餘APP從剪貼板上獲取了這個URI,並向系統請求獲取對應的圖片時,系統能夠給它一個臨時的「URI許可」,以便讓它僅能獲取該URI所對應的圖片,你的APP的其餘內容都是安全的。


對於ContentProvider,系統不關心的是:

在一個ContentProvider背後,你的APP如何管理你的數據,系統絕不關心;若是你不須要SQLite database中的結構化數據,你能夠不使用SQLite。好比,FileProvider幫助類可讓你的APP內的原始文件輕鬆經過一個ContentProvider獲取。


一樣,若是你不打算公開你的APP中的數據給其餘人使用,你也能夠不實現ContentProvider。是這樣的,由於圍繞着ContentProvider,有許多便利的方法,這些方法讓你很容易地將數據存入SQLite database,並用這些數據填充UI元素如ListView。可是若是這些方法讓你以爲實現本身的想法有許多困難,你能夠不使用它們,請自由地選擇一個對你的APP來講合適的數據模型。


好了,文章翻譯完了,爲了不直譯的生硬晦澀,我花費了整整一天的休息時間,儘量在不違背原文意思的狀況下,加入了一些個性化的語句表達,還加入了「我的理解」部分,目的就是方便你們的理解。


掘金是一個高質量的技術社區,從 RxJava 到 Android Studio,性能優化到優秀開源庫,讓你不錯過 Android 開發的每個技術乾貨。長按圖片二維碼識別或者各大應用市場搜索「掘金」,技術乾貨盡在掌握中。

本文分享自微信公衆號 - 非著名程序員(non-famous-coder)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索