快速了解Android重要機制


轉自 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算法:設置固定大小的緩存空間,當達到空間限制后刪除最大尺寸的圖片。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM