標籤:
轉自 http://www.jianshu.com/p/5f6d79323923
一、Android系統底層研究
關於底層的知識點不是在一篇文章中能講解清楚,參見本人的Android底層研究系列,不斷更新中。
二、Android重要知識點
下面精選了較為常見的知識點,堅決杜絕簡單羅列答案的方式,因為那樣理解不了也記不住。所以盡量以層層遞進而簡單粗暴的方式來表達。耐心點看,一定能幫你應對大多數面試問題。
Tips:可以先閱讀自己熟悉的知識點,然後再去看那些不太熟悉的。
1、Activity啟動模式
什麼是啟動Activity?
一般而言是先建立一個Activity對象,然後把Activity放入一個Task堆棧裡進行管理(包括切換、關閉等)。那麼問題來了,如果該堆棧裡已經有這個Activity對象呢,我還要不要建立一個新的對象呢?Android提供了四種選擇方案:
- 不管堆棧裡有沒有,都建立一個新的activity放在堆棧頂部:
standard模式
- 如果我要開啟的activity正好在堆棧頂部,那就直接用它;否則也建立新的:
singleTop模式
- 這個比較霸道。即如果activity已經存在在堆棧裡,那麼當要開啟這個activity時,就算棧的上層還有其他activity,也統統移除,成為棧頂。而如果沒有,就也要建立。:
singleTask模式
- 這個更加霸道。即開啟這個activity時,直接建立一個堆棧,並建立這個activity放入新堆棧中,不去和別人擠一個堆棧。以後再要開啟這個activity時,直接跳轉到這個新堆棧裡去用。:
singleInstance模式(即只建立一個執行個體,單獨放在一個task堆棧裡給別的task棧共用)
啟動模式2、Activity(或組件)通訊機制
Activity(組件)之間何時需要通訊?
一般當需要從一個Activity(組件)向其他的Activity(組件)傳遞資料時,就會涉及到通訊機制。例如開啟一個新的Activity並向其傳遞資料。
如何來通訊/傳遞資料?
較常用的是android系統的信使:Intent。
Intent的作用是,帶上要傳遞的資料,然後出發前往指定的目的地。
那麼問題來了,如果帶上各種各樣的資料呢?又如何導航去找到目的地呢?
如何帶上各種各樣的資料?
就像發信一樣,你不可能把一堆淩亂的信紙直接塞給信使讓人家送,而會用信封把信都裝起來。
android裡也一樣,會把資料先打包,再給信使,這裡就涉及到 bundle。對,我們把一堆淩亂的資料放在一個bundle裡,然後把bundle給Intent就可以了。
如何導航找到目的地呢?
如果你想郵寄東西有兩種方式:1、在信封上寫好詳細地址;2、如果你想郵寄沒用的棉被書籍給災區,但你又無所謂寄給哪個災區,你就可以寫幫我隨便寄到一個災區去,然後郵局管理員會在全國範圍內查詢,如果查到了災區如汶川,那就郵寄到那。
android裡也一樣,有顯式Intent和隱式Intent,前者會指定特定組件名,如指定目標activity;後者不指定名稱,只給一個描述(Intent-Filter)。然後android會依據這個描述去搜尋。
但是,關於資料打包,有個問題是,如果傳遞普通資料可以直接放入bundle中,但 如果要傳遞對象呢。
如何在bundle中打包並傳遞對象呢!?
3、對象序列化機制
前面已經知道了,我們要在組件間通訊時會先用bundle對資料打包,然後交給信使intent。那麼問題來了?
bundle如何打包資料呢?
這裡的資料我們分成兩種,一種是原型資料,如int、string等,可以直接打包並傳遞。打包方式如下:
bundle.putInt("key1", 20);bundle.putString("key2", "hello");
另一種是java對象,這是不能直接給bundle的,而要通過序列化後再給bundle,然後對象會以二進位流形式傳輸,直到目標組件接受到bundle後,要對該對象位元據還原序列化才能擷取真實的的對象。
那麼如何對對象進行序列化呢?
兩種方法。
- 基於java語言的Serializable序列化方法:對象類要實現Serializable介面;
- 基於android系統提出的Parcel序列化方法:實現Parcelable介面。
只要類實現了這兩個介面,就可以把對象存入bundle中:
bundle.putSerializable(Key, Object);bundle.putParcelable(Key, Object);
那麼
這兩種機制有何區別?
- JAVA中的Serialize機制是將對象轉化為位元組流
儲存在外部裝置,在需要時重建對象(採用java反射機制)。主要用於外部裝置儲存對象狀態,網路傳輸對象等情境。缺點是產生很多中間對象及造成一定的GC(記憶體回收),簡而言之Serialize更慢;
- Android提供的Parcel機制是針對
行動裝置的輕量級高效對象序列化機制。整個過程均在記憶體進行,不涉及外部裝置,還原序列化時讀取的就是原對象,而不會建立新對象。簡單來說Parcel更快;不過它使用複雜。
這一步我們知道,要對對象進行序列化後才可以放入bundle由intent傳遞給其他組件,那麼,
為什麼需要對對象序列化後才能通過intent在組件間傳遞呢!?
以startActivity(intent)為例,該函數作用時從A Activity中啟動B Activity,同時把資料打包給intent並傳遞給B。也就是說,如果intent裡的資料有java對象,那就要序列化這個對象才能傳遞給B。
下面直接給出原因:(詳細可參見從源碼角度輕鬆學習Intent機制)
因為在啟動B Activity過程中,需要離開應用程式所在進程,轉而調用native方法,進入linux kernel進程中去執行activity切換的實際操作。完成後再重新把傳輸資料帶回到應用程式進程中,對原始打包資料進行解析。
也就是說,在啟動B Activity過程中,所打包的資料要經過不同進程:應用程式->linux kernel->應用程式,而java對象是無法直接在進程間傳輸的(見下文),所以,我們需要提前序列化java對象,才能讓它經得住後面跨進程傳輸的考驗。
4、處理序間通訊機制IPC
什麼是進程?
簡單來說,一個進程往往代表一個應用程式。作業系統會為每一個進程分配一定的記憶體空間等資源,然後進程就在這塊記憶體裡跑。
什麼是處理序間通訊?
一般而言,為了保證安全,進程獲得的記憶體空間時一塊抽象的記憶體,然後會映射到實際的某一塊實體記憶體,從而,每個進程都無法訪問其他進程所在記憶體裡的資料。比如手機上開了qq和某銀行用戶端,作業系統會為他倆建立兩個獨立的記憶體區供運行,而qq進程是無法訪問到銀行用戶端資料的,這就保證了資料的安全性。
但是,帶來的缺陷是,進程間無法直接進行資料轉送,所以要引入特有的處理序間通訊機制。
進程間可以直接傳遞對象嗎?
前面知道,不同處理序間通訊,也就意味著在兩塊不同的實體記憶體間傳遞資料。首先,原生的資料如int string資料是可以傳遞的,只要你說明這個資料時什麼類型,那麼就可以在目標進程裡複原。但是對象資料,如果直接把對象傳遞過去,目標進程是無法複原的,因而也無法識別。
記憶體區資料通訊
Android系統中不同應用程式之間通訊。一個進程代表一個應用程式。
處理序間通訊
附加:IPC機制中很重要的是Binder機制,利用Binder實現不同進程間傳遞。這部分內容面試一般不會問的太難。目前本人還在整理該部分內容,繼續更新中。
5、多線程管理
前面提到了處理序間通訊,下面講解下Android裡的多線程管理,後面會講解線程間通訊。
首先,線程基本概念此處不過多講解,只要知道線程是輕量級進程,在有限的進程記憶體空間資源中去執行實際操作。
什麼是Main Thread 和 Worker Thread?
當應用程式開啟時會自動建立一個主線程 (Main Thread),也叫UI線程,主要功能是完成UI介面繪製和與使用者互動。除主線程之外的其他線程稱為子線程或Worker Thread。這些線程往往用執行耗時操作如網路通訊或資料庫訪問等。
如果在主線程中執行耗時操作,那麼在執行過程中,就無法繪製介面或響應使用者操作,在使用者看來就是卡頓,可能造成ANR現象,破壞使用者體驗。
主線程是線程不安全的
網上很多地方解釋線程不安全並非解釋原因,而是在‘解釋’結果,即:(因為主線程是線程不安全的,所以)UI控制項只能在主線程中操作,而不能在其他線程中操作,否則多個線程同時操作一個控制項會出錯。
這種解釋不僅沒解釋為何主線程是線程不安全的,反而讓人感覺android系統裡限定了只能在主線程操作ui元素,容易誤解為那不是很安全嗎?
我的解釋是:
首先我們是的的確確可以通過代碼,在子線程裡操作UI元素,然後引發程式崩潰。假設主線程是安全的,那麼安全的情況是:即使我們不小心在子線程操作UI也不用擔心,因為系統會屏蔽這種操作。而實際上系統並未屏蔽,只要開發人員在子線程操作UI元素,很有可能會導致崩潰。所以我們才說android系統裡主線程並不安全。
附加知識:什麼是安全執行緒/不安全?在多線程環境裡,往往一段代碼會被多個線程分別執行。而常見的情況是一個線程執行了該段代碼的一部分後,會被另一個線程搶走時間片又去執行這段代碼,並修改其中變數。當原線程再次回來繼續運行時,其實裡面的變數已經被別人改動了但它卻不知,最後會導致錯誤。這種線程就是不安全的。而安全的線程在執行一段代碼時,只要沒執行完,其他線程就不能來執行這段代碼或修改變數知道它執行完。
那麼
多個線程間時如何通訊的呢?
這裡就涉及到另一套機制handler、looper、MessageQueue機制,在本人另一篇文章中有生動詳細的描述,見Handler實現機制
簡單來說,每個線程擁有上面這三個東西,如線程A想發送訊息給線程B,那麼線上程A中調用線程B的handler來發送訊息,訊息會自動發送線程B的訊息佇列中,同時線程B的looper在不斷遍曆這個隊列,如果發現有新訊息就會自動去處理。
常見問題:
如何避免發生ANR(Application Not Response)現象呢?
盡量在子線程中執行耗時操作(如網路請求或資料庫讀取等)或者資源開銷較大的操作,當子線程執行完畢後,利用handler通知主線程做出更新。
6、記憶體管理機制
前面提到了,每個應用程式運行在獨立的進程中(實際是一個獨立的Dalvik進程),而且系統會為它分配固定的記憶體。在記憶體使用量中常常會發生記憶體流失(memory leak)和記憶體溢出(OOM),其中,記憶體溢出非常嚴重,往往會導致程式崩潰。所以,合理有效管理記憶體非常的重要。
詳細的管理(包括面試重點:圖片緩衝機制)均可以參考本人另一篇文章:Android之記憶體管理及最佳化
此處不再贅述。
只說一下三種圖片緩衝思路:
- LRU演算法:設定緩衝圖片最大數量,當圖片數量超過最大值久刪除使用較少的圖片,從而最佳化記憶體。
- FTU演算法:設定圖片的緩衝時限,從最後一次使用算起,當達到時限即刪除。
- FMU演算法:設定固定大小的緩衝空間,當達到空間限制後刪除最大尺寸的圖片。
7、下一步:
文章還在繼續更新中,讀者有感興趣的話題可以在評論裡回複
文/iam_wingjay(簡書作者)
原文連結:http://www.jianshu.com/p/5f6d79323923
著作權歸作者所有,轉載請聯絡作者獲得授權,並標註“簡書作者”。
快速瞭解Android重要機制