面試某度Android崗,覺得沒過但意外收到Offer,我卻毅然決然的拒絕了

如夢朦朧

九月份的時候有了換工做的躁動,而後投了某度的Android崗位,本覺得像我這種非2十一、985沒工做經驗的渣渣只能被直接pass,結果卻意外的收到了電話,真是受寵若驚,通過電面,技術三面,而後就是等通知到最後拿到了OFFER,如夢通常,真是挺激動的。html

面試的準備

當收到HR的面試的通知仍是很懵逼的,由於感受本身忽然啥都不會了,迅速鎮定下來,去網上找了一下某度的面試題,可是發現都只有提問了什麼並無對所提問題的解答,那隻能自力更生,像作試卷同樣,一遍總結一遍溫顧,其實大多都是平時開發中用到的,只是咱們沒有總結過,被問起來的時候回答的不免會有點捉襟見肘,不能回答的很全面。下面爲我我的總(bai)結(du)的,但願對你能有所幫助,但畢竟能力有限,有寫的不對的地方,還望輕噴。雖然噴我我也不會改的…java

由於本文篇幅較長建議收藏,在用到時候找出來看一眼。有一些知識點可能沒涉及到,之後會加以補足。由於面試無非是考察你對技術的理解和總結,因此本篇的每一個點總結的比較精簡,只是讓你大概的說出來,有的部分是須要可以畫出原理圖並進行解釋說明,這個要在工做中多積累。linux

JAVA

一. 類的加載過程,Person person = new Person();爲例進行說明。android

1).由於new用到了Person.class,因此會先找到Person.class文件,並加載到內存中;
2).執行該類中的static代碼塊,若是有的話,給Person.class類進行初始化;
3).在堆內存中開闢空間分配內存地址;
4).在堆內存中創建對象的特有屬性,並進行默認初始化;
5).對屬性進行顯示初始化;
6).對對象進行構造代碼塊初始化;
7).對對象進行與之對應的構造函數進行初始化;
8).將內存地址付給棧內存中的p變量面試

二. JVM相關知識,GC機制。算法

JVM基本構成數據庫

從上圖可知,JVM主要包括四個部分:編程

    1.類加載器(ClassLoader):在JVM啓動時或者在類運行時將須要的class加載到JVM中。(下圖表示了從java源文件到JVM的整個過程,可配合理解。設計模式

    2.執行引擎:負責執行class文件中包含的字節碼指令;api

    3.內存區(也叫運行時數據區):是在JVM運行的時候操做所分配的內存區。運行時內存區主要能夠劃分爲5個區域,如圖:

  • 方法區(Method Area):用於存儲類結構信息的地方,包括常量池、靜態變量、構造函數等。雖然JVM規範把方法區描述爲堆的一個邏輯部分, 但它卻有個別名non-heap(非堆),因此你們不要搞混淆了。方法區還包含一個運行時常量池。
  • java堆(Heap):存儲java實例或者對象的地方。這塊是GC的主要區域。從存儲的內容咱們能夠很容易知道,方法區和堆是被全部java線程共享的。
  • java棧(Stack):java棧老是和線程關聯在一塊兒,每當建立一個線程時,JVM就會爲這個線程建立一個對應的java棧。在這個java棧中又會包含多個棧幀,每運行一個方法就建立一個棧幀,用於存儲局部變量表、操做棧、方法返回值等。每個方法從調用直至執行完成的過程,就對應一個棧幀在java棧中入棧到出棧的過程。因此java棧是現成私有的。
  • 程序計數器(PC Register):用於保存當前線程執行的內存地址。因爲JVM程序是多線程執行的(線程輪流切換),因此爲了保證線程切換回來後,還能恢復到原先狀態,就須要一個獨立的計數器,記錄以前中斷的地方,可見程序計數器也是線程私有的。
  • 本地方法棧(Native Method Stack):和java棧的做用差很少,只不過是爲JVM使用到的native方法服務的。
    4.本地方法接口:主要是調用C或C++實現的本地方法及返回結果。

GC機制

垃圾收集器通常必須完成兩件事:檢測出垃圾;回收垃圾。怎麼檢測出垃圾?通常有如下幾種方法:

引用計數法:

給一個對象添加引用計數器,每當有個地方引用它,計數器就加1;引用失效就減1。好了,問題來了,若是我有兩個對象A和B,互相引用,除此以外,沒有其餘任何對象引用它們,實際上這兩個對象已經沒法訪問,便是咱們說的垃圾對象。可是互相引用,計數不爲0,致使沒法回收,因此還有另外一種方法:

可達性分析算法:

以根集對象爲起始點進行搜索,若是有對象不可達的話,便是垃圾對象。這裏的根集通常包括java棧中引用的對象、方法區常良池中引用的對象、本地方法中引用的對象等。

總之,JVM在作垃圾回收的時候,會檢查堆中的全部對象是否會被這些根集對象引用,不可以被引用的對象就會被垃圾收集器回收。通常回收算法也有以下幾種:

1).標記-清除(Mark-sweep)
2).複製(Copying
3).標記-整理(Mark-Compact)
4).分代收集算法

具體的解釋能夠參考本篇文章:

https://blog.csdn.net/tonytfj...

三. 類的加載器,雙親機制,Android的類加載器。

類的加載器

你們都知道,當咱們寫好一個Java程序以後,不是管是CS仍是BS應用,都是由若干個.class文件組織而成的一個完整的Java應用程序,當程序在運行時,即會調用該程序的一個入口函數來調用系統的相關功能,而這些功能都被封裝在不一樣的class文件當中,因此常常要從這個class文件中要調用另一個class文件中的方法,若是另一個文件不存在的,則會引起系統異常。而程序在啓動的時候,並不會一次性加載程序所要用的全部class文件,而是根據程序的須要,經過Java的類加載機制(ClassLoader)來動態加載某個class文件到內存當中的,從而只有class文件被載入到了內存以後,才能被其它class所引用。因此ClassLoader就是用來動態加載class文件到內存當中用的。

雙親機制

一、原理介紹

ClassLoader使用的是雙親委託模型來搜索類的,每一個ClassLoader實例都有一個父類加載器的引用(不是繼承的關係,是一個包含的關係),虛擬機內置的類加載器(Bootstrap ClassLoader)自己沒有父類加載器,但能夠用做其它ClassLoader實例的的父類加載器。當一個ClassLoader實例須要加載某個類時,它會試圖親自搜索某個類以前,先把這個任務委託給它的父類加載器,這個過程是由上至下依次檢查的,首先由最頂層的類加載器Bootstrap ClassLoader試圖加載,若是沒加載到,則把任務轉交給Extension ClassLoader試圖加載,若是也沒加載到,則轉交給App ClassLoader 進行加載,若是它也沒有加載獲得的話,則返回給委託的發起者,由它到指定的文件系統或網絡等URL中加載該類。若是它們都沒有加載到這個類時,則拋出ClassNotFoundException異常。不然將這個找到的類生成一個類的定義,並將它加載到內存當中,最後返回這個類在內存中的Class實例對象。

二、爲何要使用雙親委託這種模型呢?

由於這樣能夠避免重複加載,當父親已經加載了該類的時候,就沒有必要子ClassLoader再加載一次。考慮到安全因素,咱們試想一下,若是不使用這種委託模式,那咱們就能夠隨時使用自定義的String來動態替代java核心api中定義的類型,這樣會存在很是大的安全隱患,而雙親委託的方式,就能夠避免這種狀況,由於String已經在啓動時就被引導類加載器(Bootstrcp ClassLoader)加載,因此用戶自定義的ClassLoader永遠也沒法加載一個本身寫的String,除非你改變JDK中ClassLoader搜索類的默認算法。

三、可是JVM在搜索類的時候,又是如何斷定兩個class是相同的呢?

JVM在斷定兩個class是否相同時,不只要判斷兩個類名是否相同,並且要判斷是否由同一個類加載器實例加載的。只有二者同時知足的狀況下,JVM才認爲這兩個class是相同的。就算兩個class是同一份class字節碼,若是被兩個不一樣的ClassLoader實例所加載,JVM也會認爲它們是兩個不一樣class。好比網絡上的一個Java類org.classloader.simple.NetClassLoaderSimple,javac編譯以後生成字節碼文件NetClassLoaderSimple.class,ClassLoaderA和ClassLoaderB這兩個類加載器並讀取了NetClassLoaderSimple.class文件,並分別定義出了java.lang.Class實例來表示這個類,對於JVM來講,它們是兩個不一樣的實例對象,但它們確實是同一份字節碼文件,若是試圖將這個Class實例生成具體的對象進行轉換時,就會拋運行時異常java.lang.ClassCaseException,提示這是兩個不一樣的類型。

Android類加載器

對於Android而言,最終的apk文件包含的是dex類型的文件,dex文件是將class文件從新打包,打包的規則又不是簡單地壓縮,而是徹底對class文件內部的各類函數表,變量表進行優化,產生一個新的文件,即dex文件。所以加載這種特殊的Class文件就須要特殊的類加載器DexClassLoader。

四. 集合框架,list,map,set都有哪些具體的實現類,區別都是什麼?

1.List,Set都是繼承自Collection接口,Map則不是;

2.List特色:元素有放入順序,元素可重複;

Set特色:元素無放入順序,元素不可重複,重複元素會覆蓋掉,(注意:元素雖然無放入順序,可是元素在set中的位置是有該元素的HashCode決定的,其位置實際上是固定的,加入Set 的Object必須定義equals()方法;

另外list支持for循環,也就是經過下標來遍歷,也能夠用迭代器,可是set只能用迭代,由於他無序,沒法用下標來取得想要的值)。

3.Set和List對比:

Set:檢索元素效率低下,刪除和插入效率高,插入和刪除不會引發元素位置改變。

List:和數組相似,List能夠動態增加,查找元素效率高,插入刪除元素效率低,由於會引發其餘元素位置改變。

4.Map適合儲存鍵值對的數據。

5.線程安全集合類與非線程安全集合類

LinkedList、ArrayList、HashSet是非線程安全的,Vector是線程安全的;

HashMap是非線程安全的,HashTable是線程安全的;

StringBuilder是非線程安全的,StringBuffer是線程安全的。

下面是這些類具體的使用介紹:

ArrayList與LinkedList的區別和適用場景

Arraylist:

優勢:ArrayList是實現了基於動態數組的數據結構,由於地址連續,一旦數據存儲好了,查詢操做效率會比較高(在內存裏是連着放的)。

缺點:由於地址連續, ArrayList要移動數據,因此插入和刪除操做效率比較低。

LinkedList:

優勢:LinkedList基於鏈表的數據結構,地址是任意的,因此在開闢內存空間的時候不須要等一個連續的地址,對於新增和刪除操做add和remove,LinedList比較佔優點。LinkedList 適用於要頭尾操做或插入指定位置的場景。

缺點:由於LinkedList要移動指針,因此查詢操做性能比較低。
適用場景分析:
當須要對數據進行對此訪問的狀況下選用ArrayList,當須要對數據進行屢次增長刪除修改時採用LinkedList。

ArrayList與Vector的區別和適用場景

ArrayList有三個構造方法:

public ArrayList(int initialCapacity)//構造一個具備指定初始容量的空列表。    
public ArrayList()//構造一個初始容量爲10的空列表。    
public ArrayList(Collection<? extends E> c)//構造一個包含指定 collection 的元素的列表

Vector有四個構造方法:

public Vector()//使用指定的初始容量和等於零的容量增量構造一個空向量。    
public Vector(int initialCapacity)//構造一個空向量,使其內部數據數組的大小,其標準容量增量爲零。    
public Vector(Collection<? extends E> c)//構造一個包含指定 collection 中的元素的向量    
public Vector(int initialCapacity,int capacityIncrement)//使用指定的初始容量和容量增量構造一個空的向量

ArrayList和Vector都是用數組實現的,主要有這麼幾個區別:

1).Vector是多線程安全的,線程安全就是說多線程訪問同一代碼,不會產生不肯定的結果。而ArrayList不是,這個能夠從源碼中看出,Vector類中的方法不少有synchronized進行修飾,這樣就致使了Vector在效率上沒法與ArrayList相比;

2).兩個都是採用的線性連續空間存儲元素,可是當空間不足的時候,兩個類的增長方式是不一樣。

3).Vector能夠設置增加因子,而ArrayList不能夠。

4).Vector是一種老的動態數組,是線程同步的,效率很低,通常不同意使用。

適用場景:

1.Vector是線程同步的,因此它也是線程安全的,而ArrayList是線程異步的,是不安全的。若是不考慮到線程的安全因素,通常用ArrayList效率比較高。

2.若是集合中的元素的數目大於目前集合數組的長度時,在集合中使用數據量比較大的數據,用Vector有必定的優點。

HashSet與Treeset的適用場景

1.TreeSet 是二叉樹(紅黑樹的樹據結構)實現的,Treeset中的數據是自動排好序的,不容許放入null值。

2.HashSet 是哈希表實現的,HashSet中的數據是無序的,能夠放入null,但只能放入一個null,二者中的值都不能重複,就如數據庫中惟一約束。

3.HashSet要求放入的對象必須實現HashCode()方法,放入的對象,是以hashcode碼做爲標識的,而具備相同內容的String對象,hashcode是同樣,因此放入的內容不能重複。可是同一個類的對象能夠放入不一樣的實例。

適用場景分析:

HashSet是基於Hash算法實現的,其性能一般都優於TreeSet。爲快速查找而設計的Set,咱們一般都應該使用HashSet,在咱們須要排序的功能時,咱們才使用TreeSet。

HashMap與TreeMap、HashTable的區別及適用場景

HashMap 非線程安全

HashMap:基於哈希表(散列表)實現。使用HashMap要求添加的鍵類明肯定義了hashCode()和equals()[能夠重寫hashCode()和equals()],爲了優化HashMap空間的使用,您能夠調優初始容量和負載因子。其中散列表的衝突處理主要分兩種,一種是開放定址法,另外一種是鏈表法。HashMap的實現中採用的是鏈表法。

TreeMap:非線程安全基於紅黑樹實現。TreeMap沒有調優選項,由於該樹總處於平衡狀態。

適用場景分析:

HashMap和HashTable:HashMap去掉了HashTable的contains方法,可是加上了containsValue()和containsKey()方法。HashTable同步的,而HashMap是非同步的,效率上比HashTable要高。

HashMap容許空鍵值,而HashTable不容許。

HashMap:適用於Map中插入、刪除和定位元素。

Treemap:適用於按天然順序或自定義順序遍歷鍵(key)。

(ps:其實咱們工做的過程當中對集合的使用是很頻繁的,稍加註意並總結積累一下,在面試的時候應該會回答的很輕鬆)

五. concurrentHashmap原理,原子類。

ConcurrentHashMap做爲一種線程安全且高效的哈希表的解決方案,尤爲其中的"分段鎖"的方案,相比HashTable的全表鎖在性能上的提高很是之大.

六. volatile原理

在《Java併發編程:核心理論》一文中,咱們已經提到過可見性、有序性及原子性問題,一般狀況下咱們能夠經過Synchronized關鍵字來解決這些個問題,不過若是對Synchronized原理有了解的話,應該知道Synchronized是一個比較重量級的操做,對系統的性能有比較大的影響,因此,若是有其餘解決方案,咱們一般都避免使用Synchronized來解決問題。而volatile關鍵字就是Java中提供的另外一種解決可見性和有序性問題的方案。對於原子性,須要強調一點,也是你們容易誤解的一點:對volatile變量的單次讀/寫操做能夠保證原子性的,如long和double類型變量,可是並不能保證i++這種操做的原子性,由於本質上i++是讀、寫兩次操做。

參考文章:

https://www.cnblogs.com/paddi...

七. 多線程的使用場景

使用多線程就必定效率高嗎? 有時候使用多線程並非爲了提升效率,而是使得CPU可以同時處理多個事件。

1).爲了避免阻塞主線程,啓動其餘線程來作好事的事情,好比APP中耗時操做都不在UI中作.

2).實現更快的應用程序,即主線程專門監聽用戶請求,子線程用來處理用戶請求,以得到大的吞吐量.感受這種狀況下,多線程的效率未必高。 這種狀況下的多線程是爲了避免必等待, 能夠並行處理多條數據。
好比JavaWeb的就是主線程專門監聽用戶的HTTP請求,而後啓動子線程去處理用戶的HTTP請求。

3).某種雖然優先級很低的服務,可是卻要不定時去作。
好比Jvm的垃圾回收。

4.)某種任務,雖然耗時,可是不耗CPU的操做時,開啓多個線程,效率會有顯著提升。
好比讀取文件,而後處理。 磁盤IO是個很耗費時間,可是不耗CPU計算的工做。 因此能夠一個線程讀取數據,一個線程處理數據。確定比一個線程讀取數據,而後處理效率高。 由於兩個線程的時候充分利用了CPU等待磁盤IO的空閒時間。

八. JAVA常量池

Interger中的128(-128~127)

a.當數值範圍爲-128~127時:若是兩個new出來Integer對象,即便值相同,經過「==」比較結果爲false,但兩個對象直接賦值,則經過「==」比較結果爲「true,這一點與String很是類似。

b.當數值不在-128~127時,不管經過哪一種方式,即便兩個對象的值相等,經過「==」比較,其結果爲false;

c.當一個Integer對象直接與一個int基本數據類型經過「==」比較,其結果與第一點相同;

d.Integer對象的hash值爲數值自己;

爲何是-128-127?

在Integer類中有一個靜態內部類IntegerCache,在IntegerCache類中有一個Integer數組,用以緩存當數值範圍爲-128~127時的Integer對象。

九. 簡單介紹一下java中的泛型,泛型擦除以及相關的概念。

泛型是Java SE 1.5的新特性,泛型的本質是參數化類型,也就是說所操做的數據類型被指定爲一個參數。這種參數類型能夠用在類、接口和方法的建立中,分別稱爲泛型類、泛型接口、泛型方法。 Java語言引入泛型的好處是安全簡單。

在Java SE 1.5以前,沒有泛型的狀況的下,經過對類型Object的引用來實現參數的「任意化」,「任意化」帶來的缺點是要作顯式的強制類型轉換,而這種轉換是要求開發者對實際參數類型能夠預知的狀況下進行的。對於強制類型轉換錯誤的狀況,編譯器可能不提示錯誤,在運行的時候纔出現異常,這是一個安全隱患。

泛型的好處是在編譯的時候檢查類型安全,而且全部的強制轉換都是自動和隱式的,提升代碼的重用率。

一、泛型的類型參數只能是類類型(包括自定義類),不能是簡單類型。

二、同一種泛型能夠對應多個版本(由於參數類型是不肯定的),不一樣版本的泛型類實例是不兼容的。

三、泛型的類型參數能夠有多個。

四、泛型的參數類型能夠使用extends語句,例如<T extends superclass>。習慣上稱爲「有界類型」。

五、泛型的參數類型還能夠是通配符類型。例如Class<?> classType = Class.forName("java.lang.String");

泛型擦除以及相關的概念

Java中的泛型基本上都是在編譯器這個層次來實現的。在生成的Java字節碼中是不包含泛型中的類型信息的。使用泛型的時候加上的類型參數,會在編譯器在編譯的時候去掉。這個過程就稱爲類型擦除。

類型擦除引發的問題及解決方法

一、先檢查,再編譯,以及檢查編譯的對象和引用傳遞的問題

二、自動類型轉換

三、類型擦除與多態的衝突和解決方法

四、泛型類型變量不能是基本數據類型

五、運行時類型查詢

六、異常中使用泛型的問題

七、數組(這個不屬於類型擦除引發的問題)

九、類型擦除後的衝突

十、泛型在靜態方法和靜態類中的問題

Android

一. Handler機制

Android 的消息機制也就是 handler 機制,建立 handler 的時候會建立一個 looper ( 經過 looper.prepare() 來建立 ),looper 通常爲主線程 looper.

handler 經過 send 發送消息 (sendMessage) ,固然 post 一系列方法最終也是經過 send 來實現的,在 send 方法中handler 會經過 enqueueMessage() 方法中的 enqueueMessage(msg,millis )向消息隊列 MessageQueue 插入一條消息,同時會把自己的 handler 經過 msg.target = this 傳入.

Looper 是一個死循環,不斷的讀取MessageQueue中的消息,loop 方法會調用 MessageQueue 的 next 方法來獲取新的消息,next 操做是一個阻塞操做,當沒有消息的時候 next 方法會一直阻塞,進而致使 loop 一直阻塞,當有消息的時候,Looper 就會處理消息 Looper 收到消息以後就開始處理消息: msg.target.dispatchMessage(msg),固然這裏的 msg.target 就是上面傳過來的發送這條消息的 handler 對象,這樣 handler 發送的消息最終又交給他的dispatchMessage方法來處理了,這裏不一樣的是,handler 的 dispatchMessage 方法是在建立 Handler時所使用的 Looper 中執行的,這樣就成功的將代碼邏輯切換到了主線程了.

Handler 處理消息的過程是:首先,檢查Message 的 callback 是否爲 null,不爲 null 就經過 handleCallBack 來處理消息,Message 的 callback 是一個 Runnable 對象,其實是 handler 的 post 方法所傳遞的 Runnable 參數.其次是檢查 mCallback 是 否爲 null,不爲 null 就調用 mCallback 的handleMessage 方法來處理消息.

二. View的繪製流程

View的繪製流程:OnMeasure()——>OnLayout()——>OnDraw()

各步驟的主要工做:

OnMeasure():

測量視圖大小。從頂層父View到子View遞歸調用measure方法,measure方法又回調OnMeasure。

OnLayout():

肯定View位置,進行頁面佈局。從頂層父View向子View的遞歸調用view.layout方法的過程,即父View根據上一步measure子View所獲得的佈局大小和佈局參數,將子View放在合適的位置上。

OnDraw():

繪製視圖:ViewRoot建立一個Canvas對象,而後調用OnDraw()。六個步驟:①、繪製視圖的背景;②、保存畫布的圖層(Layer);③、繪製View的內容;④、繪製View子視圖,若是沒有就不用;⑤、還原圖層(Layer);⑥、繪製滾動條。

三. 事件傳遞機制

1).Android事件分發機制的本質是要解決:點擊事件由哪一個對象發出,通過哪些對象,最終達到哪一個對象並最終獲得處理。這裏的對象是指Activity、ViewGroup、View.

2).Android中事件分發順序:Activity(Window) -> ViewGroup -> View.

3).事件分發過程由dispatchTouchEvent() 、onInterceptTouchEvent()和onTouchEvent()三個方法協助完成

設置Button按鈕來響應點擊事件事件傳遞狀況:(以下圖)

佈局以下:

最外層:Activiy A,包含兩個子View:ViewGroup B、View C

中間層:ViewGroup B,包含一個子View:View C

最內層:View C

假設用戶首先觸摸到屏幕上View C上的某個點(如圖中黃色區域),那麼Action_DOWN事件就在該點產生,而後用戶移動手指並最後離開屏幕。

按鈕點擊事件:

DOWN事件被傳遞給C的onTouchEvent方法,該方法返回true,表示處理這個事件;

由於C正在處理這個事件,那麼DOWN事件將再也不往上傳遞給B和A的onTouchEvent();

該事件列的其餘事件(Move、Up)也將傳遞給C的onTouchEvent();

(記住這個圖的傳遞順序,面試的時候可以畫出來,就很詳細了)

四. Binder機制

1.瞭解Binder

在Android系統中,每個應用程序都運行在獨立的進程中,這也保證了當其中一個程序出現異常而不會影響另外一個應用程序的正常運轉。在許多狀況下,咱們activity都會與各類系統的service打交道,很顯然,咱們寫的程序中activity與系統service確定不是同一個進程,可是它們之間是怎樣實現通訊的呢?因此Binder是android中一種實現進程間通訊(IPC)的方式之一。

    1).首先,Binder分爲Client和Server兩個進程。
    注意,Client和Server是相對的。誰發消息,誰就是Client,誰接收消息,誰就是Server。
    舉個例子,兩個進程A和B之間使用Binder通訊,進程A發消息給進程B,那麼這時候A是Binder Client,B是Binder Server;進程B發消息給進程A,那麼這時候B是Binder Client,A是Binder Server——其實這麼說雖然簡單了,但仍是不太嚴謹,咱們先這麼理解着。

    2).其次,咱們看下面這個圖(摘自田維術的博客),基本說明白了Binder的組成解構:

    圖中的IPC就是進程間通訊的意思。

    圖中的ServiceManager,負責把Binder Server註冊到一個容器中。

    有人把ServiceManager比喻成電話局,存儲着每一個住宅的座機電話,仍是很恰當的。張三給李四打電話,撥打電話號碼,會先轉接到電話局,電話局的接線員查到這個電話號碼的地址,由於李四的電話號碼以前在電話局註冊過,因此就能撥通;沒註冊,就會提示該號碼不存在。

    對照着Android Binder機制,對着上面這圖,張三就是Binder Client,李四就是Binder Server,電話局就是ServiceManager,電話局的接線員在這個過程當中作了不少事情,對應着圖中的Binder驅動.

    3).接下來咱們看Binder通訊的過程,仍是摘自田維術博客的一張圖:

注:圖中的SM也就是ServiceManager。

    咱們看到,Client想要直接調用Server的add方法,是不能夠的,由於它們在不一樣的進程中,這時候就須要Binder來幫忙了。

    首先是Server在SM這個容器中註冊。

    其次,Client想要調用Server的add方法,就須要先獲取Server對象, 可是SM不會把真正的Server對象返回給Client,而是把Server的一個代理對象返回給Client,也就是Proxy。

    而後,Client調用Proxy的add方法,SM會幫他去調用Server的add方法,並把結果返回給Client。

    以上這3步,Binder驅動出了不少力,但咱們不須要知道Binder驅動的底層實現,涉及到C++的代碼了——把有限的時間去作更有意義的事情。

參考文章:https://www.cnblogs.com/Jax/p...

2.爲何android選用Binder來實現進程間通訊?

1).可靠性。在移動設備上,一般採用基於Client-Server的通訊方式來實現互聯網與設備間的內部通訊。目前linux支持IPC包括傳統的管道,System V IPC,即消息隊列/共享內存/信號量,以及socket中只有socket支持Client-Server的通訊方式。Android系統爲開發者提供了豐富進程間通訊的功能接口,媒體播放,傳感器,無線傳輸。這些功能都由不一樣的server來管理。開發都只關心將本身應用程序的client與server的通訊創建起來即可以使用這個服務。毫無疑問,如若在底層架設一套協議來實現Client-Server通訊,增長了系統的複雜性。在資源有限的手機 上來實現這種複雜的環境,可靠性難以保證。

2).傳輸性能。socket主要用於跨網絡的進程間通訊和本機上進程間的通訊,但傳輸效率低,開銷大。消息隊列和管道採用存儲-轉發方式,即數據先從發送方緩存區拷貝到內核開闢的一塊緩存區中,而後從內核緩存區拷貝到接收方緩存區,其過程至少有兩次拷貝。雖然共享內存無需拷貝,但控制複雜。比較各類IPC方式的數據拷貝次數。共享內存:0次。Binder:1次。Socket/管道/消息隊列:2次。

3).安全性。Android是一個開放式的平臺,因此確保應用程序安全是很重要的。Android對每個安裝應用都分配了UID/PID,其中進程的UID是可用來鑑別進程身份。傳統的只能由用戶在數據包裏填寫UID/PID,這樣不可靠,容易被惡意程序利用。而咱們要求由內核來添加可靠的UID。
因此,出於可靠性、傳輸性、安全性。android創建了一套新的進程間通訊方式。

五. Android中進程的級別,以及各自的區別。

一、前臺進程

    用戶當前正在作的事情須要這個進程。若是知足下面的條件之一,一個進程就被認爲是前臺進程:

1).這個進程擁有一個正在與用戶交互的Activity(這個Activity的onResume()方法被調用)。

2).這個進程擁有一個綁定到正在與用戶交互的activity上的Service。

3).這個進程擁有一個前臺運行的Service(service調用了方法startForeground()).

4).這個進程擁有一個正在執行其任何一個生命週期回調方法(onCreate(),onStart(),或onDestroy())的Service。

5).這個進程擁有正在執行其onReceive()方法的BroadcastReceiver。

    一般,在任什麼時候間點,只有不多的前臺進程存在。它們只有在達到沒法調合的矛盾時纔會被殺--如內存過小而不能繼續運行時。一般,到了這時,設備就達到了一個內存分頁調度狀態,因此須要殺一些前臺進程來保證用戶界面的反應.

二、可見進程

    一個進程不擁有運行於前臺的組件,可是依然能影響用戶所見。知足下列條件時,進程即爲可見:

    這個進程擁有一個不在前臺但仍可見的Activity(它的onPause()方法被調用)。當一個前臺activity啓動一個對話框時,就出了這種狀況。

三、服務進程

    一個可見進程被認爲是極其重要的。而且,除非只有殺掉它才能夠保證全部前臺進程的運行,不然是不能動它的。

    這個進程擁有一個綁定到可見activity的Service。

    一個進程不在上述兩種以內,但它運行着一個被startService()所啓動的service。

儘管一個服務進程不直接影響用戶所見,可是它們一般作一些用戶關心的事情(好比播放音樂或下載數據),因此係統不到前臺進程和可見進程活不下去時不會殺它。

四、後臺進程
    一個進程擁有一個當前不可見的activity(activity的onStop()方法被調用)。

    這樣的進程們不會直接影響到用戶體驗,因此係統能夠在任意時刻殺了它們從而爲前臺、可見、以及服務進程們提供存儲空間。一般有不少後臺進程在運行。它們被保存在一個LRU(最近最少使用)列表中來確保擁有最近剛被看到的activity的進程最後被殺。若是一個activity正確的實現了它的生命週期方法,並保存了它的當前狀態,那麼殺死它的進程將不會對用戶的可視化體驗形成影響。由於當用戶返回到這個activity時,這個activity會恢復它全部的可見狀態。

五、空進程

    一個進程不擁有入何active組件。

    保留這類進程的惟一理由是高速緩存,這樣能夠提升下一次一個組件要運行它時的啓動速度。系統常常爲了平衡在進程高速緩存和底層的內核高速緩存之間的總體系統資源而殺死它們。

六. 線程池的相關知識。

    Android中的線程池都是之間或間接經過配置ThreadPoolExecutor來實現不一樣特性的線程池.Android中最多見的四類具備不一樣特性的線程池分別爲FixThreadPool、CachedThreadPool、SingleThreadPool、ScheduleThreadExecutor.

1).FixThreadPool

    只有核心線程,而且數量固定的,也不會被回收,全部線程都活動時,由於隊列沒有限制大小,新任務會等待執行.

    優勢:更快的響應外界請求.

2).SingleThreadPool

    只有一個核心線程,確保全部的任務都在同一線程中按順序完成.所以不須要處理線程同步的問題.

3).CachedThreadPool

    只有非核心線程,最大線程數很是大,全部線程都活動時,會爲新任務建立新線程,不然會利用空閒線程(60s空閒時間,過了就會被回收,因此線程池中有0個線程的可能)處理任務.

    優勢:任何任務都會被當即執行(任務隊列SynchronousQueue至關於一個空集合);比較適合執行大量的耗時較少的任務.

4).ScheduledThreadPool

    核心線程數固定,非核心線程(閒着沒活幹會被當即回收)數沒有限制.

    優勢:執行定時任務以及有固定週期的重複任務

    參考文章:https://blog.csdn.net/seu_cal...

七. 內存泄露,怎樣查找,怎麼產生的內存泄露。

產生的內存泄露

1).資源對象沒關閉形成的內存泄漏

2).構造Adapter時,沒有使用緩存的convertView

3).Bitmap對象不在使用時調用recycle()釋放內存

4).試着使用關於application的context來替代和activity相關的context

5).註冊沒取消形成的內存泄漏

6).集合中對象沒清理形成的內存泄漏

查找內存泄漏

    查找內存泄漏能夠使用Android Stdio 自帶的Android Profiler工具,也能夠使用Square產品的LeadCanary.

八. Android優化

性能優化

    1).節制的使用Service 若是應用程序須要使用Service來執行後臺任務的話,只有當任務正在執行的時候才應該讓Service運行起來。當啓動一個Service時,系統會傾向於將這個Service所依賴的進程進行保留,系統能夠在LRUcache當中緩存的進程數量也會減小,致使切換程序的時候耗費更多性能。咱們能夠使用IntentService,當後臺任務執行結束後會自動中止,避免了Service的內存泄漏。

    2).當界面不可見時釋放內存 當用戶打開了另一個程序,咱們的程序界面已經不可見的時候,咱們應當將全部和界面相關的資源進行釋放。重寫Activity的onTrimMemory()方法,而後在這個方法中監聽TRIM_MEMORY_UI_HIDDEN這個級別,一旦觸發說明用戶離開了程序,此時就能夠進行資源釋放操做了。

    3).當內存緊張時釋放內存 onTrimMemory()方法還有不少種其餘類型的回調,能夠在手機內存下降的時候及時通知咱們,咱們應該根據回調中傳入的級別來去決定如何釋放應用程序的資源。

    4).避免在Bitmap上浪費內存 讀取一個Bitmap圖片的時候,千萬不要去加載不須要的分辨率。能夠壓縮圖片等操做。

    5).使用優化過的數據集合 Android提供了一系列優化事後的數據集合工具類,如SparseArray、SparseBooleanArray、LongSparseArray,使用這些API可讓咱們的程序更加高效。HashMap工具類會相對比較低效,由於它須要爲每個鍵值對都提供一個對象入口,而SparseArray就避免掉了基本數據類型轉換成對象數據類型的時間。

佈局優化

1).重用佈局文件

    標籤能夠容許在一個佈局當中引入另外一個佈局,那麼好比說咱們程序的全部界面都有一個公共的部分,這個時候最好的作法就是將這個公共的部分提取到一個獨立的佈局中,而後每一個界面的佈局文件當中來引用這個公共的佈局。

    Tips:若是咱們要在標籤中覆寫layout屬性,必需要將layout_width和layout_height這兩個屬性也進行覆寫,不然覆寫效果將不會生效。

    標籤是做爲標籤的一種輔助擴展來使用的,它的主要做用是爲了防止在引用佈局文件時引用文件時產生多餘的佈局嵌套。佈局嵌套越多,解析起來就越耗時,性能就越差。所以編寫佈局文件時應該讓嵌套的層數越少越好。

    舉例:好比在LinearLayout裏邊使用一個佈局。裏邊又有一個LinearLayout,那麼其實就存在了多餘的佈局嵌套,使用merge能夠解決這個問題。

2).僅在須要時才加載佈局

    某個佈局當中的元素不是一塊兒顯示出來的,普通狀況下只顯示部分經常使用的元素,而那些不經常使用的元素只有在用戶進行特定操做時纔會顯示出來。

    舉例:填信息時不是須要所有填的,有一個添加更多字段的選項,當用戶須要添加其餘信息的時候,纔將另外的元素顯示到界面上。用VISIBLE性能表現通常,能夠用ViewStub。ViewStub也是View的一種,可是沒有大小,沒有繪製功能,也不參與佈局,資源消耗很是低,能夠認爲徹底不影響性能。

<ViewStub   
    android:id="@+id/view_stub"  
    android:layout="@layout/profile_extra"  
    android:layout_width="match_parent"  
    android:layout_height="wrap_content"  
    />  

public void onMoreClick() {  
    ViewStub viewStub = (ViewStub) findViewById(R.id.view_stub);  
    if (viewStub != null) {  
        View inflatedView = viewStub.inflate();  
        editExtra1 = (EditText) inflatedView.findViewById(R.id.edit_extra1);  
        editExtra2 = (EditText) inflatedView.findViewById(R.id.edit_extra2);  
        editExtra3 = (EditText) inflatedView.findViewById(R.id.edit_extra3);  
    }  
}

    tips:ViewStub所加載的佈局是不能夠使用標籤的,所以這有可能致使加載出來出來的佈局存在着多餘的嵌套結構。

高性能編碼優化

    都是一些微優化,在性能方面看不出有什麼顯著的提高的。使用合適的算法和數據結構是優化程序性能的最主要手段。

1).避免建立沒必要要的對象 沒必要要的對象咱們應該避免建立:

    若是有須要拼接的字符串,那麼能夠優先考慮使用StringBuffer或者StringBuilder來進行拼接,而不是加號鏈接符,由於使用加號鏈接符會建立多餘的對象,拼接的字符串越長,加號鏈接符的性能越低。

    當一個方法的返回值是String的時候,一般須要去判斷一下這個String的做用是什麼,若是明確知道調用方會將返回的String再進行拼接操做的話,能夠考慮返回一個StringBuffer對象來代替,由於這樣能夠將一個對象的引用進行返回,而返回String的話就是建立了一個短生命週期的臨時對象。

    儘量地少建立臨時對象,越少的對象意味着越少的GC操做。

2).在沒有特殊緣由的狀況下,儘可能使用基本數據類型來代替封裝數據類型,int比Integer要更加有效,其它數據類型也是同樣。

    基本數據類型的數組也要優於對象數據類型的數組。另外兩個平行的數組要比一個封裝好的對象數組更加高效,舉個例子,Foo[]和Bar[]這樣的數組,使用起來要比Custom(Foo,Bar)[]這樣的一個數組高效的多。

3).靜態優於抽象

    若是你並不須要訪問一個對系那個中的某些字段,只是想調用它的某些方法來去完成一項通用的功能,那麼能夠將這個方法設置成靜態方法,調用速度提高15%-20%,同時也不用爲了調用這個方法去專門建立對象了,也不用擔憂調用這個方法後是否會改變對象的狀態(靜態方法沒法訪問非靜態字段)。

4).對常量使用static final修飾符

static int intVal = 42;  
static String strVal = "Hello, world!";

    編譯器會爲上面的代碼生成一個初始方法,稱爲方法,該方法會在定義類第一次被使用的時候調用。這個方法會將42的值賦值到intVal當中,從字符串常量表中提取一個引用賦值到strVal上。當賦值完成後,咱們就能夠經過字段搜尋的方式去訪問具體的值了。

final進行優化:

static final int intVal = 42;  
static final String strVal = "Hello, world!";

    這樣,定義類就不須要方法了,由於全部的常量都會在dex文件的初始化器當中進行初始化。當咱們調用intVal時能夠直接指向42的值,而調用strVal會用一種相對輕量級的字符串常量方式,而不是字段搜尋的方式。

    這種優化方式只對基本數據類型以及String類型的常量有效,對於其餘數據類型的常量是無效的。

5).使用加強型for循環語法

static class Counter {  
int mCount;  
}  

Counter[] mArray = ...  

public void zero() {  
int sum = 0;  
for (int i = 0; i < mArray.length; ++i) {  
    sum += mArray[i].mCount;  
}  }  

public void one() {  
int sum = 0;  
Counter[] localArray = mArray;  
int len = localArray.length;  
for (int i = 0; i < len; ++i) {  
    sum += localArray[i].mCount;  
}  
}  

public void two() {  
int sum = 0;  
for (Counter a : mArray) {  
    sum += a.mCount;  
}  
}

    zero()最慢,每次都要計算mArray的長度,one()相對快得多,two()fangfa在沒有JIT(Just In Time Compiler)的設備上是運行最快的,而在有JIT的設備上運行效率和one()方法不相上下,須要注意這種寫法須要JDK1.5以後才支持。

    Tips:ArrayList手寫的循環比加強型for循環更快,其餘的集合沒有這種狀況。所以默認狀況下使用加強型for循環,而遍歷ArrayList使用傳統的循環方式。

6).多使用系統封裝好的API

    系統提供不了的Api完成不了咱們須要的功能才應該本身去寫,由於使用系統的Api不少時候比咱們本身寫的代碼要快得多,它們的不少功能都是經過底層的彙編模式執行的。 舉個例子,實現數組拷貝的功能,使用循環的方式來對數組中的每個元素一一進行賦值固然可行,可是直接使用系統中提供的System.arraycopy()方法會讓執行效率快9倍以上。

7).避免在內部調用Getters/Setters方法

    面向對象中封裝的思想是不要把類內部的字段暴露給外部,而是提供特定的方法來容許外部操做相應類的內部字段。但在Android中,字段搜尋比方法調用效率高得多,咱們直接訪問某個字段可能要比經過getters方法來去訪問這個字段快3到7倍。可是編寫代碼仍是要按照面向對象思惟的,咱們應該在能優化的地方進行優化,好比避免在內部調用getters/setters方法。

九. 插件化相關技術,熱修補技術是怎樣實現的,和插件化有什麼區別

相同點:

都使用ClassLoader來實現的加載的新的功能類,均可以使用PathClassLoader與DexClassLoader

不一樣點:

熱修復由於是爲了修復Bug的,因此要將新的同名類替代同名的Bug類,要搶先加載新的類而不是Bug類,因此多作兩件事:在原先的app打包的時候,阻止相關類去打上CLASS_ISPREVERIFIED標誌,還有在熱修復時動態改變BaseDexClassLoader對象間接引用的dexElements,這樣才能搶先代替Bug類,完成系統不加載舊的Bug類.

而插件化只是增肌新的功能類或者是資源文件,因此不涉及搶先加載舊的類這樣的使命,就避過了阻止相關類去打上CLASS_ISPREVERIFIED標誌和還有在熱修復時動態改變BaseDexClassLoader對象間接引用的dexElements.

因此插件化比熱修復簡單,熱修復是在插件化的基礎上在進行替舊的Bug類

十. 怎樣計算一張圖片的大小,加載bitmap過程(怎樣保證不產生內存溢出),二級緩存,LRUCache算法。

計算一張圖片的大小

圖片佔用內存的計算公式:圖片高度 圖片寬度 一個像素佔用的內存大小.因此,計算圖片佔用內存大小的時候,要考慮圖片所在的目錄跟設備密度,這兩個因素其實影響的是圖片的高寬,android會對圖片進行拉昇跟壓縮。

加載bitmap過程(怎樣保證不產生內存溢出)

因爲Android對圖片使用內存有限制,如果加載幾兆的大圖片便內存溢出。Bitmap會將圖片的全部像素(即長x寬)加載到內存中,若是圖片分辨率過大,會直接致使內存OOM,只有在BitmapFactory加載圖片時使用BitmapFactory.Options對相關參數進行配置來減小加載的像素。

BitmapFactory.Options相關參數詳解

(1).Options.inPreferredConfig值來下降內存消耗。

好比:默認值ARGB_8888改成RGB_565,節約一半內存。

(2).設置Options.inSampleSize 縮放比例,對大圖片進行壓縮 。

(3).設置Options.inPurgeable和inInputShareable:讓系統能及時回 收內存。

A:inPurgeable:設置爲True時,表示系統內存不足時能夠被回 收,設置爲False時,表示不能被回收。

B:inInputShareable:設置是否深拷貝,與inPurgeable結合使用,inPurgeable爲false時,該參數無心義。

(4).使用decodeStream代替其餘方法。

decodeResource,setImageResource,setImageBitmap等方法

十一. LRUCache算法是怎樣實現的。

    內部存在一個LinkedHashMap和maxSize,把最近使用的對象用強引用存儲在 LinkedHashMap中,給出來put和get方法,每次put圖片時計算緩存中全部圖片總大小,跟maxSize進行比較,大於maxSize,就將最久添加的圖片移除;反之小於maxSize就添加進來。

    以前,咱們會使用內存緩存技術實現,也就是軟引用或弱引用,在Android 2.3(APILevel 9)開始,垃圾回收器會更傾向於回收持有軟引用或弱引用的對象,這讓軟引用和弱引用變得再也不可靠。

算法

一. 算法題

m n的矩陣,能造成幾個正方形(2 2能造成1個正方形,2 3 2個,3 3 6個)

計數的關鍵是要觀察到任意一個傾斜的正方形必然惟一內接於一個非傾斜的正方形,而一個非傾斜的邊長爲k的非傾斜正方形,一條邊上有k-1個內點,每一個內點剛好肯定一個內接於其中的傾斜正方形,加上非傾斜正方形自己,可知,將邊長爲k的非傾斜正方形數目乘以k,再按k求和便可獲得全部正方形的數目。

設2≤n≤m,k≤n-1,則邊長爲k的非傾斜有(n-k)(m-k)個,故全部正方形有∑(m-k)(n-k)k個

例如m=n=4

正方形有331+222+113=20個

下面是面試過程當中遇到的題目

大多數題目均可以在上面找到答案.

電話面試題

1.ArrayList 和 Hashmap 簡單說一些,區別,底層的數據結構.

2.Handler 消息機制

3.引發內存泄漏的場景

4.多線程的使用場景?

5.經常使用的線程池有哪幾種?

6.在公司作了什麼?團隊規模?爲何離職?

面試中實際涉及到的問題

第一輪

1.知道哪些單例模式,寫一個線程安全的單例,並分析爲何是線程安全的?

2.Java中的集合有哪些?解釋一下HashMap?底部的數據結構?散列表衝突的處理方法,散列表是一個什麼樣的數據結構?HashMap是採用什麼方法處理衝突的?

3.解釋一下什麼是MVP架構,畫出圖解,一句話解釋MVP和MVC的區別?

4.Handle消息機制?在使用Handler的時候要注意哪些東西,是否會引發內存泄漏?畫一下Handler機制的圖解?

5.是否作過性能優化?已經採起了哪些措施進行優化?

6.引發內存泄漏的緣由是什麼?以及你是怎麼解決的?

這些問題應該都是比較基礎的問題,每一個開發者都應該是很是熟悉並能詳細敘述的.這一輪的面試官問的技術都是平時用到的.

第二輪

1.關於併發理解多少?說幾個併發的集合?

2.Handler 消息機制圖解?

3.在項目中作了哪些東西?

4.畫圖說明View 事件傳遞機制?並舉一個例子闡述

5.類加載機制,如何換膚,換膚插件中存在的問題?hotfix是否用過,原理是否瞭解?

6.說說項目中用到了哪些設計模式,說了一下策略模式和觀察者模式?

7.會JS麼?有Hybid開發經驗麼?

8.說一下快排的思想?手寫代碼

9.堆有哪些數據結構?

對於這輪米那是明顯感受到壓力,知識的縱向瞭解也比較深,應該是個leader.

第三輪

1.介紹一下在項目中的角色?

2.遇到困難是怎麼解決的?

3.如何與人相處,與別人意見相左的時候是怎麼解決的,並舉生活中的一個例子.

4.有沒有壓力特別大的時候?

這個應該是項目經理了,問的問題偏向於生活性格方面.

以上面試中問到的題目基本上均可以在上面找到答案,因此作準備是很重要的,但技術是一點點積累的,就算你全會背了,面試過了,真正等到工做的時候仍是會捉襟見肘的,因此踏實點吧騷年.

如夢飄落

種種緣由我卻拒絕了某度的OFFER,或許很惋惜,但更激勵我學好技術.


感謝你們能耐着性子看完囉裏囉嗦的面經

在這裏我也分享一份私貨,本身收錄整理的Android學習PDF+架構視頻+面試文檔+源碼筆記,還有高級架構技術進階腦圖、Android開發面試專題資料,高級進階架構資料幫助你們學習提高進階,也節省你們在網上搜索資料的時間來學習,也能夠分享給身邊好友一塊兒學習

若是你有須要的話,能夠點贊+評論關注我,點擊這裏領取 Android學習PDF+架構視頻+面試文檔+源碼筆記

相關文章
相關標籤/搜索