如夢朦朧
????九月份的時候有了換工作的躁動,然后投了某度的Android崗位,本以為像我這種非211、985沒工作經(jīng)驗的渣渣只能被直接pass,結(jié)果卻意外的收到了電話,真是受寵若驚.經(jīng)過電面,技術(shù)三面,然后就是等通知到最后拿到了OFFER,如夢一般,真是挺激動的.
面試的準備
????當收到HR的面試的通知還是很懵逼的,因為感覺自己突然啥都不會了,迅速鎮(zhèn)定下來,去網(wǎng)上找了一下某度的面試題,但是發(fā)現(xiàn)都只有提問了什么并沒有對所提問題的解答,那只能自力更生,像做試卷一樣,一遍總結(jié)一遍溫顧.其實大多都是平時開發(fā)中用到的,只是我們沒有總結(jié)過,被問起來的時候回答的難免會有點捉襟見肘,不能回答的很全面.下面為我個人總(bai)結(jié)(du)的,希望對你能有所幫助,但畢竟能力有限,有寫的不對的地方,還望輕噴.雖然噴我我也不會改的.
????因為本文篇幅較長建議收藏,在用到時候找出來看一眼.有一些知識點可能沒涉及到,以后會加以補足.因為面試無非是考察你對技術(shù)的理解和總結(jié),所以本篇的每個點總結(jié)的比較精簡,只是讓你大概的說出來,有的部分是需要能夠畫出原理圖并進行解釋說明,這個要在工作中多積累.
JAVA
一. 類的加載過程,Person person = new Person();為例進行說明。
1).因為new用到了Person.class,所以會先找到Person.class文件谓苟,并加載到內(nèi)存中;
2).執(zhí)行該類中的static代碼塊,如果有的話,給Person.class類進行初始化;
3).在堆內(nèi)存中開辟空間分配內(nèi)存地址;
4).在堆內(nèi)存中建立對象的特有屬性耿焊,并進行默認初始化;
5).對屬性進行顯示初始化;
6).對對象進行構(gòu)造代碼塊初始化;
7).對對象進行與之對應(yīng)的構(gòu)造函數(shù)進行初始化;
8).將內(nèi)存地址付給棧內(nèi)存中的p變量
二. JVM相關(guān)知識,GC機制遍搞。
JVM基本構(gòu)成
從上圖可知罗侯,JVM主要包括四個部分:
????2.執(zhí)行引擎:負責執(zhí)行class文件中包含的字節(jié)碼指令纫塌;
????3.內(nèi)存區(qū)(也叫運行時數(shù)據(jù)區(qū)):是在JVM運行的時候操作所分配的內(nèi)存區(qū)坎拐。運行時內(nèi)存區(qū)主要可以劃分為5個區(qū)域焊虏,如圖:
- 方法區(qū)(Method Area):用于存儲類結(jié)構(gòu)信息的地方,包括常量池蛙酪、靜態(tài)變量避除、構(gòu)造函數(shù)等怎披。雖然JVM規(guī)范把方法區(qū)描述為堆的一個邏輯部分, 但它卻有個別名non-heap(非堆)瓶摆,所以大家不要搞混淆了凉逛。方法區(qū)還包含一個運行時常量池。
- java堆(Heap):存儲java實例或者對象的地方群井。這塊是GC的主要區(qū)域状飞。從存儲的內(nèi)容我們可以很容易知道,方法區(qū)和堆是被所有java線程共享的书斜。
- java棧(Stack):java椢鼙玻總是和線程關(guān)聯(lián)在一起,每當創(chuàng)建一個線程時荐吉,JVM就會為這個線程創(chuàng)建一個對應(yīng)的java棧焙糟。在這個java棧中又會包含多個棧幀,每運行一個方法就創(chuàng)建一個棧幀稍坯,用于存儲局部變量表酬荞、操作棧、方法返回值等瞧哟。每一個方法從調(diào)用直至執(zhí)行完成的過程混巧,就對應(yīng)一個棧幀在java棧中入棧到出棧的過程。所以java棧是現(xiàn)成私有的勤揩。
- 程序計數(shù)器(PC Register):用于保存當前線程執(zhí)行的內(nèi)存地址咧党。由于JVM程序是多線程執(zhí)行的(線程輪流切換),所以為了保證線程切換回來后陨亡,還能恢復(fù)到原先狀態(tài)傍衡,就需要一個獨立的計數(shù)器,記錄之前中斷的地方负蠕,可見程序計數(shù)器也是線程私有的蛙埂。
- 本地方法棧(Native Method Stack):和java棧的作用差不多,只不過是為JVM使用到的native方法服務(wù)的遮糖。
????4.本地方法接口:主要是調(diào)用C或C++實現(xiàn)的本地方法及返回結(jié)果绣的。
GC機制
????垃圾收集器一般必須完成兩件事:檢測出垃圾;回收垃圾。怎么檢測出垃圾屡江?一般有以下幾種方法:
引用計數(shù)法:
????給一個對象添加引用計數(shù)器芭概,每當有個地方引用它,計數(shù)器就加1惩嘉;引用失效就減1罢洲。好了,問題來了文黎,如果我有兩個對象A和B惹苗,互相引用,除此之外臊诊,沒有其他任何對象引用它們鸽粉,實際上這兩個對象已經(jīng)無法訪問斜脂,即是我們說的垃圾對象抓艳。但是互相引用,計數(shù)不為0帚戳,導(dǎo)致無法回收玷或,所以還有另一種方法:
可達性分析算法:
????以根集對象為起始點進行搜索,如果有對象不可達的話片任,即是垃圾對象偏友。這里的根集一般包括java棧中引用的對象、方法區(qū)常良池中引用的對象对供、本地方法中引用的對象等位他。
????總之,JVM在做垃圾回收的時候产场,會檢查堆中的所有對象是否會被這些根集對象引用鹅髓,不能夠被引用的對象就會被垃圾收集器回收。一般回收算法也有如下幾種:
1).標記-清除(Mark-sweep)
2).復(fù)制(Copying
3).標記-整理(Mark-Compact)
4).分代收集算法
具體的解釋可以參考本篇文章還不點我?
三. 類的加載器京景,雙親機制窿冯,Android的類加載器。
類的加載器
????大家都知道确徙,當我們寫好一個Java程序之后醒串,不是管是CS還是BS應(yīng)用,都是由若干個.class文件組織而成的一個完整的Java應(yīng)用程序鄙皇,當程序在運行時芜赌,即會調(diào)用該程序的一個入口函數(shù)來調(diào)用系統(tǒng)的相關(guān)功能,而這些功能都被封裝在不同的class文件當中伴逸,所以經(jīng)常要從這個class文件中要調(diào)用另外一個class文件中的方法缠沈,如果另外一個文件不存在的,則會引發(fā)系統(tǒng)異常。而程序在啟動的時候博烂,并不會一次性加載程序所要用的所有class文件香椎,而是根據(jù)程序的需要,通過Java的類加載機制(ClassLoader)來動態(tài)加載某個class文件到內(nèi)存當中的禽篱,從而只有class文件被載入到了內(nèi)存之后畜伐,才能被其它class所引用。所以ClassLoader就是用來動態(tài)加載class文件到內(nèi)存當中用的躺率。
雙親機制
1玛界、原理介紹
????ClassLoader使用的是雙親委托模型來搜索類的,每個ClassLoader實例都有一個父類加載器的引用(不是繼承的關(guān)系悼吱,是一個包含的關(guān)系)慎框,虛擬機內(nèi)置的類加載器(Bootstrap ClassLoader)本身沒有父類加載器,但可以用作其它ClassLoader實例的的父類加載器后添。當一個ClassLoader實例需要加載某個類時笨枯,它會試圖親自搜索某個類之前,先把這個任務(wù)委托給它的父類加載器遇西,這個過程是由上至下依次檢查的馅精,首先由最頂層的類加載器Bootstrap ClassLoader試圖加載,如果沒加載到粱檀,則把任務(wù)轉(zhuǎn)交給Extension ClassLoader試圖加載洲敢,如果也沒加載到,則轉(zhuǎn)交給App ClassLoader 進行加載茄蚯,如果它也沒有加載得到的話压彭,則返回給委托的發(fā)起者,由它到指定的文件系統(tǒng)或網(wǎng)絡(luò)等URL中加載該類渗常。如果它們都沒有加載到這個類時壮不,則拋出ClassNotFoundException異常。否則將這個找到的類生成一個類的定義凳谦,并將它加載到內(nèi)存當中忆畅,最后返回這個類在內(nèi)存中的Class實例對象。
2尸执、為什么要使用雙親委托這種模型呢家凯?
????因為這樣可以避免重復(fù)加載,當父親已經(jīng)加載了該類的時候如失,就沒有必要子ClassLoader再加載一次绊诲。考慮到安全因素褪贵,我們試想一下掂之,如果不使用這種委托模式抗俄,那我們就可以隨時使用自定義的String來動態(tài)替代java核心api中定義的類型,這樣會存在非常大的安全隱患世舰,而雙親委托的方式动雹,就可以避免這種情況,因為String已經(jīng)在啟動時就被引導(dǎo)類加載器(Bootstrcp ClassLoader)加載跟压,所以用戶自定義的ClassLoader永遠也無法加載一個自己寫的String胰蝠,除非你改變JDK中ClassLoader搜索類的默認算法。
3震蒋、但是JVM在搜索類的時候茸塞,又是如何判定兩個class是相同的呢?
????JVM在判定兩個class是否相同時查剖,不僅要判斷兩個類名是否相同钾虐,而且要判斷是否由同一個類加載器實例加載的。只有兩者同時滿足的情況下笋庄,JVM才認為這兩個class是相同的效扫。就算兩個class是同一份class字節(jié)碼,如果被兩個不同的ClassLoader實例所加載无切,JVM也會認為它們是兩個不同class荡短。比如網(wǎng)絡(luò)上的一個Java類org.classloader.simple.NetClassLoaderSimple丐枉,javac編譯之后生成字節(jié)碼文件NetClassLoaderSimple.class哆键,ClassLoaderA和ClassLoaderB這兩個類加載器并讀取了NetClassLoaderSimple.class文件,并分別定義出了java.lang.Class實例來表示這個類瘦锹,對于JVM來說籍嘹,它們是兩個不同的實例對象,但它們確實是同一份字節(jié)碼文件弯院,如果試圖將這個Class實例生成具體的對象進行轉(zhuǎn)換時辱士,就會拋運行時異常java.lang.ClassCaseException,提示這是兩個不同的類型听绳。
Android類加載器
????對于Android而言颂碘,最終的apk文件包含的是dex類型的文件,dex文件是將class文件重新打包椅挣,打包的規(guī)則又不是簡單地壓縮头岔,而是完全對class文件內(nèi)部的各種函數(shù)表,變量表進行優(yōu)化鼠证,產(chǎn)生一個新的文件峡竣,即dex文件。因此加載這種特殊的Class文件就需要特殊的類加載器DexClassLoader量九。
四. 集合框架适掰,list颂碧,map,set都有哪些具體的實現(xiàn)類类浪,區(qū)別都是什么?
1.List,Set都是繼承自Collection接口载城,Map則不是;
2.List特點:元素有放入順序,元素可重復(fù);
Set特點:元素無放入順序费就,元素不可重復(fù)个曙,重復(fù)元素會覆蓋掉,(注意:元素雖然無放入順序受楼,但是元素在set中的位置是有該元素的HashCode決定的垦搬,其位置其實是固定的,加入Set 的Object必須定義equals()方法;
另外list支持for循環(huán)艳汽,也就是通過下標來遍歷猴贰,也可以用迭代器,但是set只能用迭代河狐,因為他無序米绕,無法用下標來取得想要的值)。
3.Set和List對比:
Set:檢索元素效率低下馋艺,刪除和插入效率高栅干,插入和刪除不會引起元素位置改變。
List:和數(shù)組類似捐祠,List可以動態(tài)增長碱鳞,查找元素效率高,插入刪除元素效率低踱蛀,因為會引起其他元素位置改變窿给。
4.Map適合儲存鍵值對的數(shù)據(jù)。
5.線程安全集合類與非線程安全集合類
LinkedList率拒、ArrayList崩泡、HashSet是非線程安全的,Vector是線程安全的;
HashMap是非線程安全的猬膨,HashTable是線程安全的;
StringBuilder是非線程安全的角撞,StringBuffer是線程安全的。
下面是這些類具體的使用介紹:
ArrayList與LinkedList的區(qū)別和適用場景
Arraylist:
優(yōu)點:ArrayList是實現(xiàn)了基于動態(tài)數(shù)組的數(shù)據(jù)結(jié)構(gòu),因為地址連續(xù)勃痴,一旦數(shù)據(jù)存儲好了谒所,查詢操作效率會比較高(在內(nèi)存里是連著放的)。
缺點:因為地址連續(xù)召耘, ArrayList要移動數(shù)據(jù),所以插入和刪除操作效率比較低百炬。
LinkedList:
優(yōu)點:LinkedList基于鏈表的數(shù)據(jù)結(jié)構(gòu),地址是任意的,所以在開辟內(nèi)存空間的時候不需要等一個連續(xù)的地址污它,對于新增和刪除操作add和remove剖踊,LinedList比較占優(yōu)勢庶弃。LinkedList 適用于要頭尾操作或插入指定位置的場景。
缺點:因為LinkedList要移動指針,所以查詢操作性能比較低德澈。
適用場景分析:
當需要對數(shù)據(jù)進行對此訪問的情況下選用ArrayList歇攻,當需要對數(shù)據(jù)進行多次增加刪除修改時采用LinkedList。
ArrayList與Vector的區(qū)別和適用場景
ArrayList有三個構(gòu)造方法:
public ArrayList(int initialCapacity)//構(gòu)造一個具有指定初始容量的空列表梆造。
public ArrayList()//構(gòu)造一個初始容量為10的空列表缴守。
public ArrayList(Collection<? extends E> c)//構(gòu)造一個包含指定 collection 的元素的列表
Vector有四個構(gòu)造方法:
public Vector()//使用指定的初始容量和等于零的容量增量構(gòu)造一個空向量。
public Vector(int initialCapacity)//構(gòu)造一個空向量镇辉,使其內(nèi)部數(shù)據(jù)數(shù)組的大小屡穗,其標準容量增量為零。
public Vector(Collection<? extends E> c)//構(gòu)造一個包含指定 collection 中的元素的向量
public Vector(int initialCapacity,int capacityIncrement)//使用指定的初始容量和容量增量構(gòu)造一個空的向量
ArrayList和Vector都是用數(shù)組實現(xiàn)的忽肛,主要有這么三個區(qū)別:
1).Vector是多線程安全的村砂,線程安全就是說多線程訪問同一代碼,不會產(chǎn)生不確定的結(jié)果屹逛。而ArrayList不是础废,這個可以從源碼中看出,Vector類中的方法很多有synchronized進行修飾罕模,這樣就導(dǎo)致了Vector在效率上無法與ArrayList相比评腺;
2).兩個都是采用的線性連續(xù)空間存儲元素,但是當空間不足的時候淑掌,兩個類的增加方式是不同蒿讥。
3).Vector可以設(shè)置增長因子,而ArrayList不可以锋拖。
4).Vector是一種老的動態(tài)數(shù)組诈悍,是線程同步的,效率很低兽埃,一般不贊成使用。
適用場景:
1.Vector是線程同步的适袜,所以它也是線程安全的柄错,而ArrayList是線程異步的,是不安全的苦酱。如果不考慮到線程的安全因素售貌,一般用ArrayList效率比較高。
2.如果集合中的元素的數(shù)目大于目前集合數(shù)組的長度時疫萤,在集合中使用數(shù)據(jù)量比較大的數(shù)據(jù)颂跨,用Vector有一定的優(yōu)勢。
HashSet與Treeset的適用場景
1.TreeSet 是二叉樹(紅黑樹的樹據(jù)結(jié)構(gòu))實現(xiàn)的,Treeset中的數(shù)據(jù)是自動排好序的扯饶,不允許放入null值恒削。
2.HashSet 是哈希表實現(xiàn)的,HashSet中的數(shù)據(jù)是無序的池颈,可以放入null,但只能放入一個null钓丰,兩者中的值都不能重復(fù)躯砰,就如數(shù)據(jù)庫中唯一約束。
3.HashSet要求放入的對象必須實現(xiàn)HashCode()方法携丁,放入的對象琢歇,是以hashcode碼作為標識的,而具有相同內(nèi)容的String對象梦鉴,hashcode是一樣李茫,所以放入的內(nèi)容不能重復(fù)。但是同一個類的對象可以放入不同的實例肥橙。
適用場景分析:
HashSet是基于Hash算法實現(xiàn)的涌矢,其性能通常都優(yōu)于TreeSet。為快速查找而設(shè)計的Set快骗,我們通常都應(yīng)該使用HashSet娜庇,在我們需要排序的功能時,我們才使用TreeSet方篮。
HashMap與TreeMap名秀、HashTable的區(qū)別及適用場景
HashMap 非線程安全
HashMap:基于哈希表(散列表)實現(xiàn)。使用HashMap要求添加的鍵類明確定義了hashCode()和equals()[可以重寫hashCode()和equals()]藕溅,為了優(yōu)化HashMap空間的使用匕得,您可以調(diào)優(yōu)初始容量和負載因子。其中散列表的沖突處理主要分兩種巾表,一種是開放定址法汁掠,另一種是鏈表法。HashMap的實現(xiàn)中采用的是鏈表法集币。
TreeMap:非線程安全基于紅黑樹實現(xiàn)考阱。TreeMap沒有調(diào)優(yōu)選項,因為該樹總處于平衡狀態(tài)鞠苟。
適用場景分析:
HashMap和HashTable:HashMap去掉了HashTable的contains方法乞榨,但是加上了containsValue()和containsKey()方法。HashTable同步的当娱,而HashMap是非同步的吃既,效率上比HashTable要高。HashMap允許空鍵值跨细,而HashTable不允許鹦倚。
HashMap:適用于Map中插入、刪除和定位元素冀惭。
Treemap:適用于按自然順序或自定義順序遍歷鍵(key)震叙。
(ps:其實我們工作的過程中對集合的使用是很頻繁的,稍加注意并總結(jié)積累一下,在面試的時候應(yīng)該會回答的很輕松)
五. concurrentHashmap原理掀鹅,原子類。
????ConcurrentHashMap作為一種線程安全且高效的哈希表的解決方案捐友,尤其其中的"分段鎖"的方案淫半,相比HashTable的全表鎖在性能上的提升非常之大.
六. volatile原理
????在《Java并發(fā)編程:核心理論》一文中,我們已經(jīng)提到過可見性匣砖、有序性及原子性問題科吭,通常情況下我們可以通過Synchronized關(guān)鍵字來解決這些個問題,不過如果對Synchronized原理有了解的話猴鲫,應(yīng)該知道Synchronized是一個比較重量級的操作对人,對系統(tǒng)的性能有比較大的影響,所以拂共,如果有其他解決方案牺弄,我們通常都避免使用Synchronized來解決問題。而volatile關(guān)鍵字就是Java中提供的另一種解決可見性和有序性問題的方案宜狐。對于原子性势告,需要強調(diào)一點,也是大家容易誤解的一點:對volatile變量的單次讀/寫操作可以保證原子性的抚恒,如long和double類型變量咱台,但是并不能保證i++這種操作的原子性,因為本質(zhì)上i++是讀俭驮、寫兩次操作回溺。
參考文章插好眼了等傳送
七. 多線程的使用場景
使用多線程就一定效率高嗎? 有時候使用多線程并不是為了提高效率混萝,而是使得CPU能夠同時處理多個事件遗遵。
1).為了不阻塞主線程,啟動其他線程來做好事的事情,比如APP中耗時操作都不在UI中做.
2).實現(xiàn)更快的應(yīng)用程序,即主線程專門監(jiān)聽用戶請求,子線程用來處理用戶請求,以獲得大的吞吐量.感覺這種情況下,多線程的效率未必高逸嘀。 這種情況下的多線程是為了不必等待车要, 可以并行處理多條數(shù)據(jù)。
比如JavaWeb的就是主線程專門監(jiān)聽用戶的HTTP請求厘熟,然后啟動子線程去處理用戶的HTTP請求屯蹦。
3).某種雖然優(yōu)先級很低的服務(wù),但是卻要不定時去做绳姨。
比如Jvm的垃圾回收。
4.)某種任務(wù)阔挠,雖然耗時飘庄,但是不耗CPU的操作時,開啟多個線程购撼,效率會有顯著提高跪削。
比如讀取文件谴仙,然后處理。 磁盤IO是個很耗費時間碾盐,但是不耗CPU計算的工作晃跺。 所以可以一個線程讀取數(shù)據(jù),一個線程處理數(shù)據(jù)毫玖∠苹ⅲ肯定比一個線程讀取數(shù)據(jù),然后處理效率高付枫。 因為兩個線程的時候充分利用了CPU等待磁盤IO的空閑時間烹玉。
八. JAVA常量池
Interger中的128(-128~127)
a.當數(shù)值范圍為-128~127時:如果兩個new出來Integer對象,即使值相同阐滩,通過“==”比較結(jié)果為false二打,但兩個對象直接賦值,則通過“==”比較結(jié)果為“true掂榔,這一點與String非常相似继效。
b.當數(shù)值不在-128~127時,無論通過哪種方式装获,即使兩個對象的值相等瑞信,通過“==”比較,其結(jié)果為false饱溢;
c.當一個Integer對象直接與一個int基本數(shù)據(jù)類型通過“==”比較喧伞,其結(jié)果與第一點相同;
d.Integer對象的hash值為數(shù)值本身绩郎;
為什么是-128-127?
????在Integer類中有一個靜態(tài)內(nèi)部類IntegerCache潘鲫,在IntegerCache類中有一個Integer數(shù)組,用以緩存當數(shù)值范圍為-128~127時的Integer對象肋杖。
九. 簡單介紹一下java中的泛型溉仑,泛型擦除以及相關(guān)的概念。
????泛型是Java SE 1.5的新特性状植,泛型的本質(zhì)是參數(shù)化類型浊竟,也就是說所操作的數(shù)據(jù)類型被指定為一個參數(shù)。這種參數(shù)類型可以用在類津畸、接口和方法的創(chuàng)建中咬清,分別稱為泛型類、泛型接口逢享、泛型方法竞穷。 Java語言引入泛型的好處是安全簡單。
????在Java SE 1.5之前,沒有泛型的情況的下卑惜,通過對類型Object的引用來實現(xiàn)參數(shù)的“任意化”膏执,“任意化”帶來的缺點是要做顯式的強制類型轉(zhuǎn)換,而這種轉(zhuǎn)換是要求開發(fā)者對實際參數(shù)類型可以預(yù)知的情況下進行的露久。對于強制類型轉(zhuǎn)換錯誤的情況更米,編譯器可能不提示錯誤,在運行的時候才出現(xiàn)異常毫痕,這是一個安全隱患征峦。
????泛型的好處是在編譯的時候檢查類型安全,并且所有的強制轉(zhuǎn)換都是自動和隱式的镇草,提高代碼的重用率眶痰。
1、泛型的類型參數(shù)只能是類類型(包括自定義類)梯啤,不能是簡單類型竖伯。
2、同一種泛型可以對應(yīng)多個版本(因為參數(shù)類型是不確定的)因宇,不同版本的泛型類實例是不兼容的七婴。
3、泛型的類型參數(shù)可以有多個察滑。
4打厘、泛型的參數(shù)類型可以使用extends語句,例如<T extends superclass>贺辰。習慣上稱為“有界類型”户盯。
5、泛型的參數(shù)類型還可以是通配符類型饲化。例如Class<?> classType = Class.forName("java.lang.String");
泛型擦除以及相關(guān)的概念
????Java中的泛型基本上都是在編譯器這個層次來實現(xiàn)的莽鸭。在生成的Java字節(jié)碼中是不包含泛型中的類型信息的。使用泛型的時候加上的類型參數(shù)吃靠,會在編譯器在編譯的時候去掉硫眨。這個過程就稱為類型擦除。
類型擦除引起的問題及解決方法
1巢块、先檢查礁阁,在編譯,以及檢查編譯的對象和引用傳遞的問題
2族奢、自動類型轉(zhuǎn)換
3姥闭、類型擦除與多態(tài)的沖突和解決方法
4、泛型類型變量不能是基本數(shù)據(jù)類型
5越走、運行時類型查詢
6泣栈、異常中使用泛型的問題
7、數(shù)組(這個不屬于類型擦除引起的問題)
9弥姻、類型擦除后的沖突
10南片、泛型在靜態(tài)方法和靜態(tài)類中的問題
Android
一. Handler機制
????Android 的消息機制也就是 handler 機制,創(chuàng)建 handler 的時候會創(chuàng)建一個 looper ( 通過 looper.prepare() 來創(chuàng)建 ),looper 一般為主線程 looper.
????handler 通過 send 發(fā)送消息 (sendMessage) ,當然 post 一系列方法最終也是通過 send 來實現(xiàn)的,在 send 方法中handler 會通過 enqueueMessage() 方法中的 enqueueMessage(msg,millis )向消息隊列 MessageQueue 插入一條消息,同時會把本身的 handler 通過 msg.target = this 傳入.
????Looper 是一個死循環(huán),不斷的讀取MessageQueue中的消息,loop 方法會調(diào)用 MessageQueue 的 next 方法來獲取新的消息,next 操作是一個阻塞操作,當沒有消息的時候 next 方法會一直阻塞,進而導(dǎo)致 loop 一直阻塞,當有消息的時候,Looper 就會處理消息 Looper 收到消息之后就開始處理消息: msg.target.dispatchMessage(msg),當然這里的 msg.target 就是上面?zhèn)鬟^來的發(fā)送這條消息的 handler 對象,這樣 handler 發(fā)送的消息最終又交給他的dispatchMessage方法來處理了,這里不同的是,handler 的 dispatchMessage 方法是在創(chuàng)建 Handler時所使用的 Looper 中執(zhí)行的,這樣就成功的將代碼邏輯切換到了主線程了.
????Handler 處理消息的過程是:首先,檢查Message 的 callback 是否為 null,不為 null 就通過 handleCallBack 來處理消息,Message 的 callback 是一個 Runnable 對象,實際上是 handler 的 post 方法所傳遞的 Runnable 參數(shù).其次是檢查 mCallback 是 否為 null,不為 null 就調(diào)用 mCallback 的handleMessage 方法來處理消息.
二. View的繪制流程
????View的繪制流程:OnMeasure()——>OnLayout()——>OnDraw()
各步驟的主要工作:
OnMeasure():
????測量視圖大小。從頂層父View到子View遞歸調(diào)用measure方法庭敦,measure方法又回調(diào)OnMeasure疼进。
OnLayout():
????確定View位置,進行頁面布局秧廉。從頂層父View向子View的遞歸調(diào)用view.layout方法的過程伞广,即父View根據(jù)上一步measure子View所得到的布局大小和布局參數(shù),將子View放在合適的位置上疼电。
OnDraw():
????繪制視圖:ViewRoot創(chuàng)建一個Canvas對象嚼锄,然后調(diào)用OnDraw()。六個步驟:①蔽豺、繪制視圖的背景区丑;②、保存畫布的圖層(Layer)修陡;③沧侥、繪制View的內(nèi)容;④魄鸦、繪制View子視圖宴杀,如果沒有就不用;⑤拾因、還原圖層(Layer)旺罢;⑥、繪制滾動條绢记。
三. 事件傳遞機制
1).Android事件分發(fā)機制的本質(zhì)是要解決:點擊事件由哪個對象發(fā)出扁达,經(jīng)過哪些對象,最終達到哪個對象并最終得到處理庭惜。這里的對象是指Activity罩驻、ViewGroup、View.
2).Android中事件分發(fā)順序:Activity(Window) -> ViewGroup -> View.
3).事件分發(fā)過程由dispatchTouchEvent() 护赊、onInterceptTouchEvent()和onTouchEvent()三個方法協(xié)助完成
設(shè)置Button按鈕來響應(yīng)點擊事件事件傳遞情況:(如下圖)
布局如下:
最外層:Activiy A惠遏,包含兩個子View:ViewGroup B、View C
中間層:ViewGroup B骏啰,包含一個子View:View C
最內(nèi)層:View C
假設(shè)用戶首先觸摸到屏幕上View C上的某個點(如圖中黃色區(qū)域)节吮,那么Action_DOWN事件就在該點產(chǎn)生,然后用戶移動手指并最后離開屏幕判耕。
按鈕點擊事件:
DOWN事件被傳遞給C的onTouchEvent方法透绩,該方法返回true,表示處理這個事件;
因為C正在處理這個事件,那么DOWN事件將不再往上傳遞給B和A的onTouchEvent()帚豪;
該事件列的其他事件(Move碳竟、Up)也將傳遞給C的onTouchEvent();
(記住這個圖的傳遞順序,面試的時候能夠畫出來,就很詳細了)
四. Binder機制
1.了解Binder
????在Android系統(tǒng)中,每一個應(yīng)用程序都運行在獨立的進程中狸臣,這也保證了當其中一個程序出現(xiàn)異常而不會影響另一個應(yīng)用程序的正常運轉(zhuǎn)莹桅。在許多情況下,我們activity都會與各種系統(tǒng)的service打交道烛亦,很顯然诈泼,我們寫的程序中activity與系統(tǒng)service肯定不是同一個進程,但是它們之間是怎樣實現(xiàn)通信的呢煤禽?所以Binder是android中一種實現(xiàn)進程間通信(IPC)的方式之一铐达。
????1).首先,Binder分為Client和Server兩個進程檬果。
????注意瓮孙,Client和Server是相對的。誰發(fā)消息汁汗,誰就是Client衷畦,誰接收消息,誰就是Server知牌。
????舉個例子祈争,兩個進程A和B之間使用Binder通信,進程A發(fā)消息給進程B角寸,那么這時候A是Binder Client菩混,B是Binder Server;進程B發(fā)消息給進程A扁藕,那么這時候B是Binder Client沮峡,A是Binder Server——其實這么說雖然簡單了,但還是不太嚴謹亿柑,我們先這么理解著邢疙。
????2).其次,我們看下面這個圖(摘自田維術(shù)的博客)望薄,基本說明白了Binder的組成解構(gòu):
????圖中的IPC就是進程間通信的意思疟游。
????圖中的ServiceManager,負責把Binder Server注冊到一個容器中痕支。
????有人把ServiceManager比喻成電話局颁虐,存儲著每個住宅的座機電話,還是很恰當?shù)奈孕搿埲o李四打電話另绩,撥打電話號碼儒陨,會先轉(zhuǎn)接到電話局,電話局的接線員查到這個電話號碼的地址笋籽,因為李四的電話號碼之前在電話局注冊過蹦漠,所以就能撥通;沒注冊干签,就會提示該號碼不存在津辩。
????對照著Android Binder機制,對著上面這圖容劳,張三就是Binder Client,李四就是Binder Server闸度,電話局就是ServiceManager竭贩,電話局的接線員在這個過程中做了很多事情,對應(yīng)著圖中的Binder驅(qū)動.
????3).接下來我們看Binder通信的過程莺禁,還是摘自田維術(shù)博客的一張圖:
注:圖中的SM也就是ServiceManager留量。
????我們看到,Client想要直接調(diào)用Server的add方法哟冬,是不可以的楼熄,因為它們在不同的進程中,這時候就需要Binder來幫忙了浩峡。
????首先是Server在SM這個容器中注冊可岂。
????其次,Client想要調(diào)用Server的add方法翰灾,就需要先獲取Server對象缕粹, 但是SM不會把真正的Server對象返回給Client,而是把Server的一個代理對象返回給Client纸淮,也就是Proxy平斩。
????然后,Client調(diào)用Proxy的add方法咽块,SM會幫他去調(diào)用Server的add方法绘面,并把結(jié)果返回給Client。
????以上這3步侈沪,Binder驅(qū)動出了很多力揭璃,但我們不需要知道Binder驅(qū)動的底層實現(xiàn),涉及到C++的代碼了——把有限的時間去做更有意義的事情峭竣。
(ps:以上節(jié)選自包建強老師的文章點我點我).
2.為什么android選用Binder來實現(xiàn)進程間通信塘辅?
1).可靠性。在移動設(shè)備上皆撩,通常采用基于Client-Server的通信方式來實現(xiàn)互聯(lián)網(wǎng)與設(shè)備間的內(nèi)部通信扣墩。目前l(fā)inux支持IPC包括傳統(tǒng)的管道哲银,System V IPC,即消息隊列/共享內(nèi)存/信號量呻惕,以及socket中只有socket支持Client-Server的通信方式荆责。Android系統(tǒng)為開發(fā)者提供了豐富進程間通信的功能接口,媒體播放亚脆,傳感器做院,無線傳輸。這些功能都由不同的server來管理濒持。開發(fā)都只關(guān)心將自己應(yīng)用程序的client與server的通信建立起來便可以使用這個服務(wù)键耕。毫無疑問,如若在底層架設(shè)一套協(xié)議來實現(xiàn)Client-Server通信柑营,增加了系統(tǒng)的復(fù)雜性屈雄。在資源有限的手機 上來實現(xiàn)這種復(fù)雜的環(huán)境,可靠性難以保證官套。
2).傳輸性能酒奶。socket主要用于跨網(wǎng)絡(luò)的進程間通信和本機上進程間的通信,但傳輸效率低奶赔,開銷大惋嚎。消息隊列和管道采用存儲-轉(zhuǎn)發(fā)方式,即數(shù)據(jù)先從發(fā)送方緩存區(qū)拷貝到內(nèi)核開辟的一塊緩存區(qū)中站刑,然后從內(nèi)核緩存區(qū)拷貝到接收方緩存區(qū)另伍,其過程至少有兩次拷貝。雖然共享內(nèi)存無需拷貝笛钝,但控制復(fù)雜质况。比較各種IPC方式的數(shù)據(jù)拷貝次數(shù)。共享內(nèi)存:0次玻靡。Binder:1次结榄。Socket/管道/消息隊列:2次。
3).安全性囤捻。Android是一個開放式的平臺臼朗,所以確保應(yīng)用程序安全是很重要的。Android對每一個安裝應(yīng)用都分配了UID/PID,其中進程的UID是可用來鑒別進程身份蝎土。傳統(tǒng)的只能由用戶在數(shù)據(jù)包里填寫UID/PID视哑,這樣不可靠,容易被惡意程序利用誊涯。而我們要求由內(nèi)核來添加可靠的UID挡毅。
所以,出于可靠性暴构、傳輸性跪呈、安全性段磨。android建立了一套新的進程間通信方式。
五. Android中進程的級別耗绿,以及各自的區(qū)別苹支。
1、前臺進程
????用戶當前正在做的事情需要這個進程误阻。如果滿足下面的條件之一债蜜,一個進程就被認為是前臺進程:
1).這個進程擁有一個正在與用戶交互的Activity(這個Activity的onResume()方法被調(diào)用)。
2).這個進程擁有一個綁定到正在與用戶交互的activity上的Service究反。
3).這個進程擁有一個前臺運行的Service(service調(diào)用了方法startForeground()).
4).這個進程擁有一個正在執(zhí)行其任何一個生命周期回調(diào)方法(onCreate(),onStart(),或onDestroy())的Service寻定。
5).這個進程擁有正在執(zhí)行其onReceive()方法的BroadcastReceiver。
????通常奴紧,在任何時間點特姐,只有很少的前臺進程存在。它們只有在達到無法調(diào)合的矛盾時才會被殺--如內(nèi)存太小而不能繼續(xù)運行時黍氮。通常,到了這時浅浮,設(shè)備就達到了一個內(nèi)存分頁調(diào)度狀態(tài)沫浆,所以需要殺一些前臺進程來保證用戶界面的反應(yīng).
2、可見進程
????一個進程不擁有運行于前臺的組件滚秩,但是依然能影響用戶所見专执。滿足下列條件時,進程即為可見:
????這個進程擁有一個不在前臺但仍可見的Activity(它的onPause()方法被調(diào)用)郁油。當一個前臺activity啟動一個對話框時本股,就出了這種情況。
3桐腌、服務(wù)進程
????一個可見進程被認為是極其重要的拄显。并且,除非只有殺掉它才可以保證所有前臺進程的運行案站,否則是不能動它的躬审。
????這個進程擁有一個綁定到可見activity的Service。
????一個進程不在上述兩種之內(nèi)蟆盐,但它運行著一個被startService()所啟動的service承边。
盡管一個服務(wù)進程不直接影響用戶所見,但是它們通常做一些用戶關(guān)心的事情(比如播放音樂或下載數(shù)據(jù))石挂,所以系統(tǒng)不到前臺進程和可見進程活不下去時不會殺它博助。
4、后臺進程
????一個進程擁有一個當前不可見的activity(activity的onStop()方法被調(diào)用)痹愚。
????這樣的進程們不會直接影響到用戶體驗富岳,所以系統(tǒng)可以在任意時刻殺了它們從而為前臺蛔糯、可見、以及服務(wù)進程們提供存儲空間城瞎。通常有很多后臺進程在運行渤闷。它們被保存在一個LRU(最近最少使用)列表中來確保擁有最近剛被看到的activity的進程最后被殺。如果一個activity正確的實現(xiàn)了它的生命周期方法脖镀,并保存了它的當前狀態(tài)飒箭,那么殺死它的進程將不會對用戶的可視化體驗造成影響。因為當用戶返回到這個activity時蜒灰,這個activity會恢復(fù)它所有的可見狀態(tài)弦蹂。
5、空進程
????一個進程不擁有入何active組件强窖。
????保留這類進程的唯一理由是高速緩存凸椿,這樣可以提高下一次一個組件要運行它時的啟動速度。系統(tǒng)經(jīng)常為了平衡在進程高速緩存和底層的內(nèi)核高速緩存之間的整體系統(tǒng)資源而殺死它們翅溺。
六. 線程池的相關(guān)知識脑漫。
????Android中的線程池都是之間或間接通過配置ThreadPoolExecutor來實現(xiàn)不同特性的線程池.Android中最常見的四類具有不同特性的線程池分別為FixThreadPool、CachedThreadPool咙崎、SingleThreadPool优幸、ScheduleThreadExecutor.
1).FixThreadPool
????只有核心線程,并且數(shù)量固定的,也不會被回收,所有線程都活動時,因為隊列沒有限制大小,新任務(wù)會等待執(zhí)行.
????優(yōu)點:更快的響應(yīng)外界請求.
2).SingleThreadPool
????只有一個核心線程,確保所有的任務(wù)都在同一線程中按順序完成.因此不需要處理線程同步的問題.
3).CachedThreadPool
????只有非核心線程,最大線程數(shù)非常大,所有線程都活動時,會為新任務(wù)創(chuàng)建新線程,否則會利用空閑線程(60s空閑時間,過了就會被回收,所以線程池中有0個線程的可能)處理任務(wù).
????優(yōu)點:任何任務(wù)都會被立即執(zhí)行(任務(wù)隊列SynchronousQueue相當于一個空集合);比較適合執(zhí)行大量的耗時較少的任務(wù).
4).ScheduledThreadPool
????核心線程數(shù)固定,非核心線程(閑著沒活干會被立即回收)數(shù)沒有限制.
????優(yōu)點:執(zhí)行定時任務(wù)以及有固定周期的重復(fù)任務(wù)
????參考Android開發(fā)——Android中常見的4種線程池(保證你能看懂并理解)
七. 內(nèi)存泄露,怎樣查找褪猛,怎么產(chǎn)生的內(nèi)存泄露网杆。
產(chǎn)生的內(nèi)存泄露
1).資源對象沒關(guān)閉造成的內(nèi)存泄漏
2).構(gòu)造Adapter時,沒有使用緩存的convertView
3).Bitmap對象不在使用時調(diào)用recycle()釋放內(nèi)存
4).試著使用關(guān)于application的context來替代和activity相關(guān)的context
5).注冊沒取消造成的內(nèi)存泄漏
6).集合中對象沒清理造成的內(nèi)存泄漏
查找內(nèi)存泄漏
????查找內(nèi)存泄漏可以使用Android Stdio 自帶的Android Profiler工具,也可以使用Square產(chǎn)品的LeadCanary.
八. Android優(yōu)化
性能優(yōu)化
????1).節(jié)制的使用Service 如果應(yīng)用程序需要使用Service來執(zhí)行后臺任務(wù)的話伊滋,只有當任務(wù)正在執(zhí)行的時候才應(yīng)該讓Service運行起來碳却。當啟動一個Service時,系統(tǒng)會傾向于將這個Service所依賴的進程進行保留笑旺,系統(tǒng)可以在LRUcache當中緩存的進程數(shù)量也會減少昼浦,導(dǎo)致切換程序的時候耗費更多性能。我們可以使用IntentService燥撞,當后臺任務(wù)執(zhí)行結(jié)束后會自動停止座柱,避免了Service的內(nèi)存泄漏。
????2).當界面不可見時釋放內(nèi)存 當用戶打開了另外一個程序物舒,我們的程序界面已經(jīng)不可見的時候色洞,我們應(yīng)當將所有和界面相關(guān)的資源進行釋放。重寫Activity的onTrimMemory()方法冠胯,然后在這個方法中監(jiān)聽TRIM_MEMORY_UI_HIDDEN這個級別火诸,一旦觸發(fā)說明用戶離開了程序,此時就可以進行資源釋放操作了荠察。
????3).當內(nèi)存緊張時釋放內(nèi)存 onTrimMemory()方法還有很多種其他類型的回調(diào)置蜀,可以在手機內(nèi)存降低的時候及時通知我們奈搜,我們應(yīng)該根據(jù)回調(diào)中傳入的級別來去決定如何釋放應(yīng)用程序的資源。
????4).避免在Bitmap上浪費內(nèi)存 讀取一個Bitmap圖片的時候盯荤,千萬不要去加載不需要的分辨率馋吗。可以壓縮圖片等操作秋秤。
????5).使用優(yōu)化過的數(shù)據(jù)集合 Android提供了一系列優(yōu)化過后的數(shù)據(jù)集合工具類宏粤,如SparseArray、SparseBooleanArray灼卢、LongSparseArray绍哎,使用這些API可以讓我們的程序更加高效。HashMap工具類會相對比較低效鞋真,因為它需要為每一個鍵值對都提供一個對象入口崇堰,而SparseArray就避免掉了基本數(shù)據(jù)類型轉(zhuǎn)換成對象數(shù)據(jù)類型的時間。
布局優(yōu)化
1).重用布局文件
????標簽可以允許在一個布局當中引入另一個布局涩咖,那么比如說我們程序的所有界面都有一個公共的部分海诲,這個時候最好的做法就是將這個公共的部分提取到一個獨立的布局中,然后每個界面的布局文件當中來引用這個公共的布局檩互。
????Tips:如果我們要在標簽中覆寫layout屬性饿肺,必須要將layout_width和layout_height這兩個屬性也進行覆寫,否則覆寫效果將不會生效盾似。
????標簽是作為標簽的一種輔助擴展來使用的,它的主要作用是為了防止在引用布局文件時引用文件時產(chǎn)生多余的布局嵌套雪标。布局嵌套越多零院,解析起來就越耗時,性能就越差村刨。因此編寫布局文件時應(yīng)該讓嵌套的層數(shù)越少越好告抄。
????舉例:比如在LinearLayout里邊使用一個布局。里邊又有一個LinearLayout嵌牺,那么其實就存在了多余的布局嵌套打洼,使用merge可以解決這個問題。
2).僅在需要時才加載布局
????某個布局當中的元素不是一起顯示出來的逆粹,普通情況下只顯示部分常用的元素募疮,而那些不常用的元素只有在用戶進行特定操作時才會顯示出來。
????舉例:填信息時不是需要全部填的僻弹,有一個添加更多字段的選項阿浓,當用戶需要添加其他信息的時候,才將另外的元素顯示到界面上蹋绽。用VISIBLE性能表現(xiàn)一般芭毙,可以用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所加載的布局是不可以使用標簽的,因此這有可能導(dǎo)致加載出來出來的布局存在著多余的嵌套結(jié)構(gòu)设哗。
高性能編碼優(yōu)化
????都是一些微優(yōu)化唱捣,在性能方面看不出有什么顯著的提升的。使用合適的算法和數(shù)據(jù)結(jié)構(gòu)是優(yōu)化程序性能的最主要手段网梢。
1).避免創(chuàng)建不必要的對象 不必要的對象我們應(yīng)該避免創(chuàng)建:
????如果有需要拼接的字符串震缭,那么可以優(yōu)先考慮使用StringBuffer或者StringBuilder來進行拼接,而不是加號連接符战虏,因為使用加號連接符會創(chuàng)建多余的對象拣宰,拼接的字符串越長,加號連接符的性能越低烦感。
????當一個方法的返回值是String的時候巡社,通常需要去判斷一下這個String的作用是什么,如果明確知道調(diào)用方會將返回的String再進行拼接操作的話手趣,可以考慮返回一個StringBuffer對象來代替晌该,因為這樣可以將一個對象的引用進行返回,而返回String的話就是創(chuàng)建了一個短生命周期的臨時對象绿渣。
????盡可能地少創(chuàng)建臨時對象朝群,越少的對象意味著越少的GC操作。
2).在沒有特殊原因的情況下中符,盡量使用基本數(shù)據(jù)類型來代替封裝數(shù)據(jù)類型姜胖,int比Integer要更加有效,其它數(shù)據(jù)類型也是一樣淀散。
????基本數(shù)據(jù)類型的數(shù)組也要優(yōu)于對象數(shù)據(jù)類型的數(shù)組右莱。另外兩個平行的數(shù)組要比一個封裝好的對象數(shù)組更加高效,舉個例子档插,F(xiàn)oo[]和Bar[]這樣的數(shù)組慢蜓,使用起來要比Custom(Foo,Bar)[]這樣的一個數(shù)組高效的多。
3).靜態(tài)優(yōu)于抽象
????如果你并不需要訪問一個對系那個中的某些字段阀捅,只是想調(diào)用它的某些方法來去完成一項通用的功能胀瞪,那么可以將這個方法設(shè)置成靜態(tài)方法,調(diào)用速度提升15%-20%,同時也不用為了調(diào)用這個方法去專門創(chuàng)建對象了凄诞,也不用擔心調(diào)用這個方法后是否會改變對象的狀態(tài)(靜態(tài)方法無法訪問非靜態(tài)字段)圆雁。
4).對常量使用static final修飾符
static int intVal = 42;
static String strVal = "Hello, world!";
????編譯器會為上面的代碼生成一個初始方法,稱為方法帆谍,該方法會在定義類第一次被使用的時候調(diào)用伪朽。這個方法會將42的值賦值到intVal當中,從字符串常量表中提取一個引用賦值到strVal上汛蝙。當賦值完成后烈涮,我們就可以通過字段搜尋的方式去訪問具體的值了。
final進行優(yōu)化:
static final int intVal = 42;
static final String strVal = "Hello, world!";
????這樣窖剑,定義類就不需要方法了坚洽,因為所有的常量都會在dex文件的初始化器當中進行初始化。當我們調(diào)用intVal時可以直接指向42的值西土,而調(diào)用strVal會用一種相對輕量級的字符串常量方式讶舰,而不是字段搜尋的方式。
????這種優(yōu)化方式只對基本數(shù)據(jù)類型以及String類型的常量有效需了,對于其他數(shù)據(jù)類型的常量是無效的跳昼。
5).使用增強型for循環(huán)語法
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)的設(shè)備上是運行最快的,而在有JIT的設(shè)備上運行效率和one()方法不相上下墓造,需要注意這種寫法需要JDK1.5之后才支持堪伍。
????Tips:ArrayList手寫的循環(huán)比增強型for循環(huán)更快,其他的集合沒有這種情況觅闽。因此默認情況下使用增強型for循環(huán)杠娱,而遍歷ArrayList使用傳統(tǒng)的循環(huán)方式。
6).多使用系統(tǒng)封裝好的API
????系統(tǒng)提供不了的Api完成不了我們需要的功能才應(yīng)該自己去寫谱煤,因為使用系統(tǒng)的Api很多時候比我們自己寫的代碼要快得多,它們的很多功能都是通過底層的匯編模式執(zhí)行的禽拔。 舉個例子刘离,實現(xiàn)數(shù)組拷貝的功能,使用循環(huán)的方式來對數(shù)組中的每一個元素一一進行賦值當然可行睹栖,但是直接使用系統(tǒng)中提供的System.arraycopy()方法會讓執(zhí)行效率快9倍以上硫惕。
7).避免在內(nèi)部調(diào)用Getters/Setters方法
????面向?qū)ο笾蟹庋b的思想是不要把類內(nèi)部的字段暴露給外部丁稀,而是提供特定的方法來允許外部操作相應(yīng)類的內(nèi)部字段匀泊。但在Android中否灾,字段搜尋比方法調(diào)用效率高得多性置,我們直接訪問某個字段可能要比通過getters方法來去訪問這個字段快3到7倍逞盆。但是編寫代碼還是要按照面向?qū)ο笏季S的,我們應(yīng)該在能優(yōu)化的地方進行優(yōu)化把将,比如避免在內(nèi)部調(diào)用getters/setters方法焰枢。
九. 插件化相關(guān)技術(shù),熱修補技術(shù)是怎樣實現(xiàn)的徽级,和插件化有什么區(qū)別
相同點:
????都使用ClassLoader來實現(xiàn)的加載的新的功能類气破,都可以使用PathClassLoader與DexClassLoader
不同點:
????熱修復(fù)因為是為了修復(fù)Bug的,所以要將新的同名類替代同名的Bug類餐抢,要搶先加載新的類而不是Bug類现使,所以多做兩件事:在原先的app打包的時候,阻止相關(guān)類去打上CLASS_ISPREVERIFIED標志旷痕,還有在熱修復(fù)時動態(tài)改變BaseDexClassLoader對象間接引用的dexElements碳锈,這樣才能搶先代替Bug類,完成系統(tǒng)不加載舊的Bug類.
????而插件化只是增肌新的功能類或者是資源文件欺抗,所以不涉及搶先加載舊的類這樣的使命售碳,就避過了阻止相關(guān)類去打上CLASS_ISPREVERIFIED標志和還有在熱修復(fù)時動態(tài)改變BaseDexClassLoader對象間接引用的dexElements.
????所以插件化比熱修復(fù)簡單,熱修復(fù)是在插件化的基礎(chǔ)上在進行替舊的Bug類
十. 怎樣計算一張圖片的大小佩迟,加載bitmap過程(怎樣保證不產(chǎn)生內(nèi)存溢出)团滥,二級緩存,LRUCache算法报强。
計算一張圖片的大小
????圖片占用內(nèi)存的計算公式:圖片高度 * 圖片寬度 * 一個像素占用的內(nèi)存大小.所以灸姊,計算圖片占用內(nèi)存大小的時候,要考慮圖片所在的目錄跟設(shè)備密度秉溉,這兩個因素其實影響的是圖片的高寬力惯,android會對圖片進行拉升跟壓縮。
加載bitmap過程(怎樣保證不產(chǎn)生內(nèi)存溢出)
????由于Android對圖片使用內(nèi)存有限制召嘶,若是加載幾兆的大圖片便內(nèi)存溢出父晶。Bitmap會將圖片的所有像素(即長x寬)加載到內(nèi)存中,如果圖片分辨率過大弄跌,會直接導(dǎo)致內(nèi)存OOM甲喝,只有在BitmapFactory加載圖片時使用BitmapFactory.Options對相關(guān)參數(shù)進行配置來減少加載的像素。
BitmapFactory.Options相關(guān)參數(shù)詳解
(1).Options.inPreferredConfig值來降低內(nèi)存消耗铛只。
比如:默認值A(chǔ)RGB_8888改為RGB_565,節(jié)約一半內(nèi)存埠胖。
(2).設(shè)置Options.inSampleSize 縮放比例,對大圖片進行壓縮 淳玩。
(3).設(shè)置Options.inPurgeable和inInputShareable:讓系統(tǒng)能及時回 收內(nèi)存直撤。
A:inPurgeable:設(shè)置為True時,表示系統(tǒng)內(nèi)存不足時可以被回 收蜕着,設(shè)置為False時谋竖,表示不能被回收。
B:inInputShareable:設(shè)置是否深拷貝,與inPurgeable結(jié)合使用蓖乘,inPurgeable為false時锤悄,該參數(shù)無意義。
(4).使用decodeStream代替其他方法驱敲。
decodeResource,setImageResource,setImageBitmap等方法
十一. LRUCache算法是怎樣實現(xiàn)的铁蹈。
????內(nèi)部存在一個LinkedHashMap和maxSize,把最近使用的對象用強引用存儲在 LinkedHashMap中众眨,給出來put和get方法握牧,每次put圖片時計算緩存中所有圖片總大小,跟maxSize進行比較娩梨,大于maxSize沿腰,就將最久添加的圖片移除;反之小于maxSize就添加進來狈定。
????之前颂龙,我們會使用內(nèi)存緩存技術(shù)實現(xiàn),也就是軟引用或弱引用纽什,在Android 2.3(APILevel 9)開始措嵌,垃圾回收器會更傾向于回收持有軟引用或弱引用的對象,這讓軟引用和弱引用變得不再可靠芦缰。
算法
一. 算法題
m * n的矩陣企巢,能形成幾個正方形(2 * 2能形成1個正方形,2 * 3 2個让蕾,3 * 3 6個)
計數(shù)的關(guān)鍵是要觀察到任意一個傾斜的正方形必然唯一內(nèi)接于一個非傾斜的正方形浪规,而一個非傾斜的邊長為k的非傾斜正方形,一條邊上有k-1個內(nèi)點探孝,每個內(nèi)點恰好確定一個內(nèi)接于其中的傾斜正方形笋婿,加上非傾斜正方形本身,可知顿颅,將邊長為k的非傾斜正方形數(shù)目乘以k缸濒,再按k求和即可得到所有正方形的數(shù)目。
設(shè)2≤n≤m粱腻,k≤n-1绍填,則邊長為k的非傾斜有
(n-k)(m-k)個,故所有正方形有
∑(m-k)(n-k)k個
例如m=n=4
正方形有331+222+113=20個
下面是面試過程中遇到的題目
大多數(shù)題目都可以在上面找到答案.
電話面試題
1.ArrayList 和 Hashmap 簡單說一些,區(qū)別,底層的數(shù)據(jù)結(jié)構(gòu).
2.Handler 消息機制
3.引起內(nèi)存泄漏的場景
4.多線程的使用場景?
5.常用的線程池有哪幾種?
6.在公司做了什么?團隊規(guī)模?為什么離職?
面試中實際涉及到的問題
第一輪
1.知道哪些單例模式,寫一個線程安全的單例,并分析為什么是線程安全的?
2.Java中的集合有哪些?解釋一下HashMap?底部的數(shù)據(jù)結(jié)構(gòu)?散列表沖突的處理方法,散列表是一個什么樣的數(shù)據(jù)結(jié)構(gòu)?HashMap是采用什么方法處理沖突的?
3.解釋一下什么是MVP架構(gòu),畫出圖解,一句話解釋MVP和MVC的區(qū)別?
4.Handle消息機制?在使用Handler的時候要注意哪些東西,是否會引起內(nèi)存泄漏?畫一下Handler機制的圖解?
5.是否做過性能優(yōu)化?已經(jīng)采取了哪些措施進行優(yōu)化?
6.引起內(nèi)存泄漏的原因是什么?以及你是怎么解決的?
這些問題應(yīng)該都是比較基礎(chǔ)的問題,每個開發(fā)者都應(yīng)該是非常熟悉并能詳細敘述的.這一輪的面試官問的技術(shù)都是平時用到的.
第二輪
1.關(guān)于并發(fā)理解多少?說幾個并發(fā)的集合?
2.Handler 消息機制圖解?
3.在項目中做了哪些東西?
4.畫圖說明View 事件傳遞機制?并舉一個例子闡述
5.類加載機制,如何換膚,換膚插件中存在的問題?hotfix是否用過,原理是否了解?
6.說說項目中用到了哪些設(shè)計模式,說了一下策略模式和觀察者模式?
7.會JS么?有Hybid開發(fā)經(jīng)驗么?
8.說一下快排的思想?手寫代碼
9.堆有哪些數(shù)據(jù)結(jié)構(gòu)?
對于這輪米那是明顯感覺到壓力,知識的縱向了解也比較深,應(yīng)該是個leader.
第三輪
1.介紹一下在項目中的角色?
2.遇到困難是怎么解決的?
3.如何與人相處,與別人意見相左的時候是怎么解決的,并舉生活中的一個例子.
4.有沒有壓力特別大的時候?
這個應(yīng)該是項目經(jīng)理了,問的問題偏向于生活性格方面.
以上面試中問到的題目基本上都可以在上面找到答案,所以做準備是很重要的,但技術(shù)是一點點積累的,就算你全會背了,面試過了,真正等到工作的時候還是會捉襟見肘的,所以踏實點吧騷年.
如夢飄落
種種原因我卻拒絕了某度的OFFER,或許很可惜,但更激勵我學好技術(shù).