2019年必備的Android面試題及參考答案收集匯總

前言
找工作、招人必備之良品。后期不斷完善中……
如何招聘人,搜集了一些知識(shí)點(diǎn)。如何做好應(yīng)聘準(zhǔn)備,也收集了一些主要知識(shí)點(diǎn),供你參考。
- Android基礎(chǔ)知識(shí):基本涵蓋Android所有知識(shí)體系,四大組件,F(xiàn)ragment,WebView,事件分發(fā),View繪制…
- Java基礎(chǔ)知識(shí)&高階知識(shí)點(diǎn):基礎(chǔ)部分不談了,高階部分:泛型,反射,Java虛擬機(jī)…
- 算法與數(shù)據(jù)結(jié)構(gòu):鏈表,堆,棧,樹…
- Android常用框架:異步,網(wǎng)絡(luò),圖片加載,內(nèi)存優(yōu)化,依賴注入,數(shù)據(jù)庫等框架
- Android前沿技術(shù):Android組件化,熱更新,插件化,消息推送,AOP面向切面編程,Flutter(谷歌的移動(dòng)UI框架)…
- 源碼分析:Android源碼分析,啟動(dòng)一個(gè)app的源碼分析,常用框架源碼分析,Java源碼分析,集合源碼分析…
- 網(wǎng)絡(luò)基礎(chǔ):五層網(wǎng)絡(luò)模型,三次握手&四次揮手,請(qǐng)求頭&響應(yīng)頭,Socket&WebSocket…
文章目錄
- 前言
- 1.Java中的==、equals和hashCode的區(qū)別
- 2.int和integer的區(qū)別
- 3.String、StringBuffer和StringBuilder的區(qū)別
- 4.什么是內(nèi)部類?內(nèi)部類的作用是什么?
- 5.進(jìn)程與線程的區(qū)別
- 6.final、finally、finalize的區(qū)別
- 7.Serializable和Parcelable的區(qū)別
- 8.靜態(tài)屬性和靜態(tài)方法是否可以被繼承?是否可以被重寫?
- 9.成員內(nèi)部類、靜態(tài)內(nèi)部類、局部內(nèi)部類、和匿名內(nèi)部類的理解
- 10.Java的垃圾回收機(jī)制及其在何時(shí)會(huì)被觸發(fā)
- 11.Java中的代理是什么?靜態(tài)代理和動(dòng)態(tài)代理的區(qū)別是什么?
- 12.Java中實(shí)現(xiàn)多態(tài)的機(jī)制是什么?
- 13.Java中反射的相關(guān)理解
- 14.Java中注解的相關(guān)理解
- 15.對(duì)Java中String類的理解
- 16.對(duì)Java中字符串常量池的理解
- 17.Java中為什么String類要設(shè)計(jì)成不可變的
- 18.Java中Hash碼(哈希碼)的理解
- 19.Object類的equal方法和hashcode方法的重寫
- 20.Java常用集合List與Set,以及Map的區(qū)別
- 21.ArrayMap和HashMap的區(qū)別
- 22.HashMap和HashTable的區(qū)別
- 23.HashMap和HashSet的區(qū)別
- 24.ArrayList和LinkedList的區(qū)別
- 25.?dāng)?shù)組和鏈表的區(qū)別
- 26.Java中多線程實(shí)現(xiàn)的三種方式
- 27.Java中創(chuàng)建線程的三種方式
- 28.線程和進(jìn)程的區(qū)別
- 29.Java中的線程的run( )方法和start()方法的區(qū)別
- 31.Java中wait和sleep方法的不同
- 32.對(duì)Java中wait/notify關(guān)鍵字的理解
- 33.什么是線程阻塞?線程該如何關(guān)閉?
- 34.如何保證線程的安全
- 35.實(shí)現(xiàn)線程同步的方式
- 36.Java中Synchronized關(guān)鍵字的用法,以及對(duì)象鎖、方法鎖、類鎖的理解
- 37.Java中鎖與同步的相關(guān)知識(shí)
- 38.Synchronized和volatile關(guān)鍵字的區(qū)別
- 39.Java的原子性、可見性、有序性的理解
- 40.ReentrantLock、Synchronized、Volatile關(guān)鍵字
- 41.Java中死鎖的概念,其產(chǎn)生的四個(gè)必要條件
- 42.Java中堆和棧的理解
- 43.理解線程間通信
- 44.線程中的join()方法的理解,及如何讓多個(gè)線程按順序執(zhí)行
- 45.工作者線程(workerThread)與主線程(UI線程)的理解
- 46.AsyncTask(異步任務(wù))的工作原理
- 47.并發(fā)和并行的區(qū)別及理解
- 48.同步和異步的區(qū)別、阻塞和非阻塞的區(qū)別的理解
- 49.Java中任務(wù)調(diào)度的理解
- 50.Java中進(jìn)程的詳細(xì)概念
- 51.線程的詳細(xì)概念
- 52.Android中的性能優(yōu)化相關(guān)問題
- 53.內(nèi)存泄漏的相關(guān)原因
- 54.通過Handler在線程間通信的原理
- 55.Android中動(dòng)畫的類型:
- 55.理解Activity、View、Window三者之間的關(guān)系
- 56.Android中Context詳解:
- 57.Java中double和float類型的區(qū)別
- 58.Android常用的數(shù)據(jù)存儲(chǔ)方式(4種)
- 59.ANR的了解及優(yōu)化
- 60.Android垃圾回收機(jī)制和程序優(yōu)化System.gc( )
- 61.Android平臺(tái)的優(yōu)勢和不足
- 62.其他講解:
- Activity組件
- Service組件
- BoradcastReceiver組件
- ContentProvider(內(nèi)容提供者)組件
- Fragment
- ViewPager
- Android的事件傳遞(分發(fā))機(jī)制
- Bitmap的使用及內(nèi)存優(yōu)化
- 使用View繪制視圖
- Android內(nèi)存泄漏及管理
- Android設(shè)計(jì)模式之MVC
- JVM運(yùn)行原理詳解
- Android平臺(tái)的虛擬機(jī)Dalvik
- Java的內(nèi)存分配
- Android中的Binder機(jī)制
- Android中的緩存機(jī)制
- Android 中圖片的三級(jí)緩存策略
1.Java中的==、equals和hashCode的區(qū)別
(1)“==”運(yùn)算符用來比較兩個(gè)變量的值是否相等,即該運(yùn)算符用于比較變量之間對(duì)應(yīng)的內(nèi)存中的地址是否相同,
要比較兩個(gè)基本類型的數(shù)據(jù)或兩個(gè)引用變量是否相等,只能使用“=.=“(注:編譯器格式顯示問題,是雙等號(hào))運(yùn)算符
(2)equals是Object類提供的方法之一,每個(gè)java類都集成自O(shè)bject類,即每個(gè)對(duì)象都有equals方法,equals與“==”一樣,比較的都是引用,相比運(yùn) 算符,equals(Object)方法的特殊之處在于其可以被覆蓋,所以可以通過覆蓋的方法讓他比較的不是引用而是數(shù)據(jù)內(nèi)容,即堆中的內(nèi)容是否相等。
(3)hashCode()方法是從Object類繼承過來的,他也是用來比較兩個(gè)對(duì)象是否相等,Object類中的hashCode方法,返回對(duì)象在內(nèi)存中地址轉(zhuǎn)換成的一個(gè)Int值,所以如果未重寫hashCode方法,任何對(duì)象的hashCode方法返回的值都是不相等的。
綜上而言,==和equals判斷的是(基本類型數(shù)據(jù))引用是否相等,但equals可以通過覆蓋方法使其比較的是數(shù)據(jù)內(nèi)容,事實(shí)上,Java中很多類庫中的類已經(jīng)覆蓋了此方法,而hashCode判斷的是對(duì)象在內(nèi)存中地址是否相等。
2.int和integer的區(qū)別
Integer是int提供的封裝類,而int是java的基本數(shù)據(jù)類型,Integer的默認(rèn)值是null,而int的默認(rèn)值是0,聲明Integer的變量需要實(shí)例化,而int不需要,Integer是對(duì)象,是一個(gè)引用指向這個(gè)對(duì)象,而int是基本數(shù)據(jù)類型,直接存儲(chǔ)數(shù)據(jù)。
3.String、StringBuffer和StringBuilder的區(qū)別
他們的主要區(qū)別在于運(yùn)行速度和線程安全兩個(gè)方面,運(yùn)行速度:StringBuilder>StringBuffer>String,String最慢的原因在于String是字符串常量,一旦創(chuàng)建是不可以再更改的,但后兩者的對(duì)象是變量,是可以更改的,Java中對(duì)String對(duì)象的操作實(shí)際上是一個(gè)不斷創(chuàng)建新的對(duì)象而將舊的對(duì)象回收的過程,而后兩者因?yàn)槭亲兞?,所以可以直接進(jìn)行更改,在線程安全上,StringBuilder是線程不安全的,而StringBuffer是線程安全的,因?yàn)樵赟tringBuffer對(duì)象在字符串緩沖區(qū)被多個(gè)線程使用時(shí),StringBuffer中很多方法可以帶有synchronized關(guān)鍵字,所以可以保證線程是安全的,而StringBuilder不存在該關(guān)鍵字,所以在線程中并不安全。
4.什么是內(nèi)部類?內(nèi)部類的作用是什么?
內(nèi)部類是定義在另一個(gè)類里面的類,與之相對(duì)應(yīng),包含內(nèi)部類的類被稱為外部類,
內(nèi)部類的作用有:(1)內(nèi)部類提供了更好的封裝,可以把內(nèi)部類隱藏在外部類之內(nèi),不允許同一包中的其他類訪問,(2)內(nèi)部類的方法可以直接訪問外部類的所有數(shù)據(jù),包括私有的數(shù)據(jù),(3)內(nèi)部類的種類:成員內(nèi)部類、靜態(tài)內(nèi)部類、方法內(nèi)部類、匿名內(nèi)部類
5.進(jìn)程與線程的區(qū)別
進(jìn)程是CPU資源分配的最小單位,而線程是CPU調(diào)度的最小單位,進(jìn)程之間不能共享資源,而線程共享所在進(jìn)程的地址空間和其他資源,一個(gè)進(jìn)程內(nèi)可以擁有多個(gè)線程,進(jìn)程可以開啟進(jìn)程、也可以開啟線程,一個(gè)線程只能屬于一個(gè)進(jìn)程,線程可直接使用同進(jìn)程的資源,線程依賴于進(jìn)程而存在。
6.final、finally、finalize的區(qū)別
final是用于修飾類、成員變量和成員方法,類不可被繼承,成員變量不可變,成員方法不可被重寫;finally與try…catch…共同使用,確保無論是否出現(xiàn)異常都能被調(diào)用到;finalize是類的方法,垃圾回收前會(huì)調(diào)用此方法,子類可以重寫finalize方法實(shí)現(xiàn)對(duì)資源的回收。
7.Serializable和Parcelable的區(qū)別
Serlizable是java序列化接口,在硬盤上讀寫,讀寫的過程中有大量臨時(shí)變量產(chǎn)生,內(nèi)部執(zhí)行大量的I/O操作,效率很低;Parcelable是Android序列化接口,效率高,在內(nèi)存中讀寫,但使用麻煩,對(duì)象不能保存到磁盤中。
8.靜態(tài)屬性和靜態(tài)方法是否可以被繼承?是否可以被重寫?
可以繼承,但不可以被重寫,而是被隱藏,如果子類里面定義了靜態(tài)方法或?qū)傩裕敲催@時(shí)候父類的靜態(tài)方法或?qū)傩苑Q之為隱藏。
9.成員內(nèi)部類、靜態(tài)內(nèi)部類、局部內(nèi)部類、和匿名內(nèi)部類的理解
Java中內(nèi)部類主要分為成員內(nèi)部類、局部內(nèi)部類(嵌套在方法和作用域中)、匿名內(nèi)部類(無構(gòu)造方法)、靜態(tài)內(nèi)部類(由static修飾的類、不能使用任何外圍類的非static成員變量和方法、不依賴外圍類),每個(gè)內(nèi)部類都能獨(dú)立的繼承一個(gè)(接口的)實(shí)現(xiàn),所以無論外圍類是否已經(jīng)繼承了某個(gè)(接口的)實(shí)現(xiàn),對(duì)于內(nèi)部類均無影響,因?yàn)镴ava支持實(shí)現(xiàn)多個(gè)接口,而不支持多繼承,我們可以使用內(nèi)部類提供的、可以繼承多個(gè)具體的或抽象的類的能力來解決使用接口難以解決的問題,接口只是解決了部分問題,而內(nèi)部類使得多繼承的解決方案變得更加完整。
10.Java的垃圾回收機(jī)制及其在何時(shí)會(huì)被觸發(fā)
內(nèi)存回收機(jī)制:就是釋放掉在內(nèi)存中已經(jīng)沒有用的對(duì)象,要判斷怎樣的對(duì)象是沒用的,有兩種方法:(1)采用標(biāo)記數(shù)的方法,在給內(nèi)存中的對(duì)象打上標(biāo)記,對(duì)象被引用一次,計(jì)數(shù)加一,引用被釋放,計(jì)數(shù)就減一,當(dāng)這個(gè)計(jì)數(shù)為零時(shí),這個(gè)對(duì)象就可以被回收,但是,此種方法,對(duì)于循環(huán)引用的對(duì)象是無法識(shí)別出來并加以回收的,(2)采用根搜索的方法,從一個(gè)根出發(fā),搜索所有的可達(dá)對(duì)象,則剩下的對(duì)象就是可被回收的,垃圾回收是在虛擬機(jī)空閑的時(shí)候或者內(nèi)存緊張的時(shí)候執(zhí)行的,什么時(shí)候回收并不是由程序員控制的,可達(dá)與不可達(dá)的概念:分配對(duì)象使用new關(guān)鍵字,釋放對(duì)象時(shí),只需將對(duì)象的引用賦值為null,讓程序不能夠在訪問到這個(gè)對(duì)象,則稱該對(duì)象不可達(dá)。
在以下情況中垃圾回收機(jī)制會(huì)被觸發(fā):
(1)所有實(shí)例都沒有活動(dòng)線程訪問 ;(2)沒有其他任何實(shí)例訪問的循環(huán)引用實(shí)例;(3)Java中有不同的引用類型。判斷實(shí)例是否符合垃圾收集的條件都依賴于它的引用類型。
11.Java中的代理是什么?靜態(tài)代理和動(dòng)態(tài)代理的區(qū)別是什么?
代理模式:在某些情況下,一個(gè)用戶不想或不能直接引用一個(gè)對(duì)象,此時(shí)可以通過一個(gè)稱之為“代理”的第三者來實(shí)現(xiàn)間接引用,代理對(duì)象可以在客戶端和目標(biāo)對(duì)象之間起中介的作用,并且可以通過中介對(duì)象去掉用戶不能看到的內(nèi)容和服務(wù),或者添加用戶需要的額外服務(wù)。
靜態(tài)代理:即在程序運(yùn)行前代理類就已經(jīng)存在,也就是編寫代碼的時(shí)候已經(jīng)將代理類的代碼寫好。
動(dòng)態(tài)代理:在程序運(yùn)行時(shí),通過反射機(jī)制動(dòng)態(tài)創(chuàng)建代理類。
12.Java中實(shí)現(xiàn)多態(tài)的機(jī)制是什么?
方法的重寫Overriding和重載Overloading是Java多態(tài)性的不同表現(xiàn)
重寫Overriding 是父類與子類之間多態(tài)的一種表現(xiàn);
重載Overloading是同一個(gè)類中多態(tài)性的一種表現(xiàn);
13.Java中反射的相關(guān)理解
Java的反射機(jī)制是在運(yùn)行狀態(tài)中,對(duì)于任意一個(gè)類,都能夠獲取到這個(gè)類的所有屬性和方法,對(duì)于任意一個(gè)對(duì)象,能夠調(diào)用它的任意一個(gè)方法和屬性,包括私有的方法和屬性,這種動(dòng)態(tài)地獲取信息以及動(dòng)態(tài)的調(diào)用對(duì)象的方法的功能就稱之為Java的反射機(jī)制。
從對(duì)象出發(fā),通過反射(.class類)可以獲取到類的完整信息,(類名、class類型、所在包、具有的所有方法Method[]類型、某個(gè)方法的完整信息,包括修飾符、返回值類型、異常、參數(shù)類型、所有屬性Field[ ]、某個(gè)屬性的完整信息,構(gòu)造器Constructors,調(diào)用類的屬性或方法。
14.Java中注解的相關(guān)理解
Java注解,英文名為Annotation,一種代碼級(jí)別的說明,是JDK1.5及以后版本引入的一個(gè)特性,與類、接口、枚舉在同一個(gè)層次,他可以聲明在包、類、字段、方法、局部變量、方法參數(shù)等的前面,用來對(duì)這些元素進(jìn)行說明、注釋、作用分類;注解是Java提供的一種元程序中元素關(guān)聯(lián)任何信息和任何元數(shù)據(jù)(metadata)的途徑和方法,Annotion(注解)是一個(gè)接口,程序可以通過反射來獲取指定程序元素的Annotion對(duì)象,然后通過Annotion對(duì)象來獲取注解里面的元數(shù)據(jù)
注解的一般格式為:[修飾符]@interface[名稱]{元素},元素是無方法體的方法聲明,可以有默認(rèn)值。
注解的作用:
編寫文檔:通過代碼里標(biāo)識(shí)的元數(shù)據(jù)生成文檔[生成文檔doc]
代碼分析:通過代碼里的元數(shù)據(jù)對(duì)代碼進(jìn)行分析[使用反射]
編譯檢查:通過代碼里標(biāo)識(shí)的元數(shù)據(jù)讓編譯器能夠?qū)崿F(xiàn)基本的編譯檢查[Override]
15.對(duì)Java中String類的理解
通過對(duì)String類的源代碼分析可知,(1)String類是final類,即意味著String類不能被繼承,并且他的成員方法都默認(rèn)為final方法;(2)String類在源代碼中實(shí)際上是通過char[ ]數(shù)組來保存字符串的;(3)String對(duì)象一旦創(chuàng)建就是固定不便的,對(duì)String對(duì)象的任何change操作都會(huì)生成新的對(duì)象。
16.對(duì)Java中字符串常量池的理解
字符串的分配和其他對(duì)象分配一樣,是需要消耗高昂的時(shí)間和空間成本的,而且字符串在程序中使用得非常多,JVM為了提高性能和減少內(nèi)存的開銷,在實(shí)例化字符串的時(shí)候會(huì)進(jìn)行一些優(yōu)化;每當(dāng)我們創(chuàng)建字符串常量時(shí),JVM首先會(huì)檢查字符串常量池,如果該字符床已經(jīng)存在于常量池中,那么就直接返回常量池中的實(shí)例引用,如果該字符串不存在,就會(huì)實(shí)例化該字符串,并將其放在常量池中,由于字符串的不可變性,我們可以十分肯定常量池中一定不存在兩個(gè)相同的字符串,Java中的常量池實(shí)際上分為兩種形態(tài):靜態(tài)常量池和運(yùn)行時(shí)常量池
靜態(tài)常量池:即*.class文件的常量池,.class文件的常量池不僅僅包括字符串(數(shù)字)字面量,還包含類、方法的信息,占用class文件的絕大部分空間。
運(yùn)行時(shí)常量池:常量存入到內(nèi)存中,并保存在方法區(qū)中,我們常說的常量池,就是指方法區(qū)中的運(yùn)行時(shí)常量池。
17.Java中為什么String類要設(shè)計(jì)成不可變的
在java中將String類設(shè)計(jì)成不可變的是綜合考慮到各種因素的結(jié)果,需要綜合內(nèi)存、同步、數(shù)據(jù)結(jié)構(gòu)、以及安全等方面的考慮。
字符串常量池的需要:字符串常量池是Java堆內(nèi)存中一個(gè)特殊的存儲(chǔ)區(qū)域,當(dāng)創(chuàng)建一個(gè)String對(duì)象時(shí),假如此字符串值已經(jīng)存在于常量池中,則不會(huì)創(chuàng)建一個(gè)新的對(duì)象,而是引用已經(jīng)存在的對(duì)象,假若字符串對(duì)象允許改變,那么將會(huì)導(dǎo)致各種邏輯的錯(cuò)誤,比如改變一個(gè)引用的字符串將會(huì)導(dǎo)致另一個(gè)引用出現(xiàn)臟數(shù)據(jù)。
允許String對(duì)象緩存HashCode:Java中String對(duì)象的哈希碼被頻繁的使用,比如在HashMap等容器中,字符串不變性保證了哈希碼的唯一性,因此可以放心地進(jìn)行緩存,這也是一種性能優(yōu)化的手段,意味著不必每次都去計(jì)算新的哈希碼。
安全性:String被許多Java類庫用來當(dāng)作參數(shù),如:網(wǎng)絡(luò)連接(network connection)、打開文件(opening files)等等,如果String不是不可變的,網(wǎng)絡(luò)連接、打開文件將會(huì)被改變——這將導(dǎo)致一系列的安全威脅,操作的方法本以為連接上一臺(tái)機(jī)器,其實(shí)不是,優(yōu)于反射的參數(shù)都是字符串,同樣也會(huì)引起一系列的安全問題。
18.Java中Hash碼(哈希碼)的理解
在Java中,哈希碼代表了對(duì)象的一種特征,例如我們判斷某兩個(gè)字符串是否==,如果其哈希碼相等,則這兩個(gè)字符串是相等的,其次,哈希碼是一種數(shù)據(jù)結(jié)構(gòu)的算法,常見的哈希碼的算法有:
Object類的HashCode,返回對(duì)象的內(nèi)存地址經(jīng)過處理后的結(jié)構(gòu),由于每個(gè)對(duì)象的內(nèi)存地址都不一樣,所以哈希碼也不一樣。
String類的HashCode,根據(jù)String類包含的字符串的內(nèi)容,根據(jù)一種特殊的算法返回哈希碼,只要字符串的內(nèi)容相同,返回的哈希碼也相同。
Integer類:返回的哈希碼就是integer對(duì)象里所包含的那個(gè)整數(shù)的數(shù)值。例如
Integer i1=new Integer(100) i1.hashCode的值就是100,由此可見兩個(gè)一樣大小的Integer對(duì)象返回的哈希碼也一樣。
19.Object類的equal方法和hashcode方法的重寫
equal和hashCode的關(guān)系是這樣的:(1)如果兩個(gè)對(duì)象相同(即用equal比較返回true),那么他們的hashcode值一定要相同;(2)如果兩個(gè)對(duì)象的hashcode相同,他們并不一定相同(即用equal比較返回false),因?yàn)閔ashcode的方法是可以重載的,如果不重載,會(huì)用Java.long.Object的hashcode方法,只要是不同的對(duì)象,hashcode必然不同。
由于為了提高程序的效率才實(shí)現(xiàn)了hashcode方法,先進(jìn)行hashcode的比較,如果不同,就沒有必要再進(jìn)行equal的比較了,這樣就大大減少了equals比較的次數(shù),這對(duì)需要比較數(shù)量很大的運(yùn)算效率特稿是很多的。
附加:一旦new對(duì)象就會(huì)在內(nèi)存中開辟空間,==比較的是對(duì)象的地址值,返回的是boolean型,equal方法默認(rèn)比較的對(duì)象的地址值,但是Integer等基本類型包裝類以及String類中已經(jīng)重寫了equal()方法,比較的是對(duì)象內(nèi)存中的內(nèi)容,返回值是boolean型。
20.Java常用集合List與Set,以及Map的區(qū)別
Java中的集合主要分為三種類型:Set(集)、List(列表)、Map(映射);
數(shù)組是大小固定的,并且同一個(gè)數(shù)組只能存放類型一樣的數(shù)據(jù)(基本類型/引用類型),而Java集合是可以存儲(chǔ)和操作數(shù)目不固定的一組數(shù)據(jù),所有的Java集合都位于java.util包中,Java集合只能存放引用類型的數(shù)據(jù),不能存放基本數(shù)據(jù)類型。
Collection是最基本的集合接口,聲明了適用于Java集合(只包括Set和List)的通用方法,Set和List都繼承了Collection接口。
Set是最簡單的一種集合,集合中的對(duì)象不按特定的方式排序,并且沒有重復(fù)對(duì)象,Set接口主要實(shí)現(xiàn)了兩種實(shí)現(xiàn)類
TreeSet:TreeSet類實(shí)現(xiàn)了SortedSet接口,能夠?qū)现械膶?duì)象進(jìn)行排序;
HashSet:HashSet類按照哈希算法來存取集合中的對(duì)象,存取速度比較快;
Set具有與Collection完全一樣的接口,因此沒有任何額外的功能,實(shí)際上Set就是Collection,只是行為不同(這是繼承與多態(tài)思想的典型應(yīng)用,表現(xiàn)不同的行為),Set不保存重復(fù)的元素,Set接口不保證維護(hù)元素的次序。
(2) List列表的特征是其他元素以線性表的方式存儲(chǔ),集合中可以存放重復(fù)的對(duì)象,其接口主要實(shí)現(xiàn)類:
ArrayList( ):代表長度可以改變的數(shù)組,可以對(duì)元素進(jìn)行隨機(jī)的訪問,向ArrayList( )中插入與刪除元素的速度慢。
LinkedList( ):在實(shí)現(xiàn)類中采用鏈表數(shù)據(jù)結(jié)構(gòu),插入和刪除的速度快,但訪問的速度慢。
對(duì)于List的隨機(jī)訪問來說,就是只是隨機(jī)來檢索位于特定位置的元素,List的get(int index)方法返回集合中由參數(shù)index指定的索引位置的對(duì)象,索引下標(biāo)從0開始。
Map映射是一種把關(guān)鍵字對(duì)象映射的集合,他的每一個(gè)元素都包括一堆鍵對(duì)象和值對(duì)象,Map沒有繼承Collection接口,從Map集合中檢索元素時(shí)只要給出鍵對(duì)象,就會(huì)返回對(duì)應(yīng)的值對(duì)象。
HashMap:Map基于散列表的實(shí)現(xiàn),插入和查詢“鍵值對(duì)”的開銷是固定的,可以通過構(gòu)造器設(shè)置容量capacity和負(fù)載因子load factor ,以調(diào)整容器的性能;
LinkedHashMap:類似于HashMap,但在迭代遍歷時(shí),取得“鍵值對(duì)”的順序是其插入次序,只比HashMap慢一點(diǎn),而在迭代訪問時(shí)反而更快,因?yàn)樗褂面湵砭S護(hù)內(nèi)部次序。
TreeMap:基于紅黑樹數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn),查看“鍵”或“鍵值對(duì)”時(shí),他們會(huì)對(duì)其排序(次序由Comparabel和Comparator決定)
21.ArrayMap和HashMap的區(qū)別
ArrayMap相比傳統(tǒng)的HashMap速度更慢,因?yàn)槠洳檎曳椒ㄊ嵌址?,并且?dāng)刪除或添加數(shù)據(jù)時(shí),會(huì)對(duì)空間重新調(diào)整,可以說ArrayMap是犧牲了時(shí)間來換空間,ArrayMap與HashMap的區(qū)別主要在:
存儲(chǔ)方式不同:HashMap內(nèi)部有一個(gè)HashMapEntry<K,V>[ ]對(duì)象,而ArrayMap是一個(gè)<key,value>映射的數(shù)據(jù)結(jié)構(gòu),內(nèi)部使用兩個(gè)數(shù)組進(jìn)行數(shù)據(jù)存儲(chǔ),一個(gè)數(shù)組記錄key的hash值,另一個(gè)數(shù)組記錄value值。
添加數(shù)據(jù)時(shí)擴(kuò)容的處理不一樣:HashMap進(jìn)行了new操作,重新創(chuàng)建對(duì)象,開銷很大,而ArrayMap用的是copy數(shù)據(jù),效率相對(duì)高很多。
ArrayMap提供了數(shù)組收縮的功能,在clear或remove之后,會(huì)重新收縮數(shù)組,釋放空間。
ArrayMap采用的是二分法查找。
22.HashMap和HashTable的區(qū)別
HashMap是基于哈希表實(shí)現(xiàn)的,每一個(gè)元素是一個(gè)key—value對(duì),其內(nèi)部通過單鏈表解決沖突的問題HashMap是非線程安全的,只適用于單線程的環(huán)境下。多線程的環(huán)境下可以采用concurrent并發(fā)包下的concurrentHashMap,HsahMap實(shí)現(xiàn)了serializable接口,支持序列化,實(shí)現(xiàn)了cloneable接口,能被克隆。HashMap內(nèi)部維持了一個(gè)存儲(chǔ)數(shù)據(jù)的Entry數(shù)組,HashMap采用鏈表解決沖突,HashMap中的key和value都允許為null,key為null的鍵值對(duì)永遠(yuǎn)都放在以table[0]為節(jié)點(diǎn)的鏈表中。
HashTable同樣是基于哈希表實(shí)現(xiàn)的,同樣每個(gè)元素是一個(gè)key-value對(duì),其內(nèi)部也是通過單鏈表解決沖突問題,容量不足時(shí),同樣會(huì)自動(dòng)增大,HashTble是線程安全的,能用在多線程的環(huán)境下,HashTable實(shí)現(xiàn)了serializable接口,它支持序列化,實(shí)現(xiàn)了cloneable接口,能被克隆。
HashMap和HashTable之間的區(qū)別有以下幾點(diǎn)
繼承的父類不同,hashTable繼承自Dictionary類,而HashMap繼承自AbstractMap類,但二者都實(shí)現(xiàn)了Map接口。
線程安全性不同,HashTable中的方法是synchronized的,而HashMap中的方法在缺省的情況下是非ynchronized的,在多線程的環(huán)境下,可以直接使用HsahTable,不需要為他的方法實(shí)現(xiàn)同步,但使用HashMap時(shí)就必須自己增加同步處理。
key和value是否允許為null值:關(guān)于其中的key和value都是對(duì)象,并且不能包含重復(fù)的key,但可以包含重復(fù)的value,hashtable中,key和value都不允許出現(xiàn)null值,但在hashmap中,null可以作為鍵,這樣的鍵只有一個(gè),可以有多個(gè)鍵對(duì)應(yīng)的值為null.
23.HashMap和HashSet的區(qū)別
HashMap:其實(shí)現(xiàn)了Map接口,HashMap存儲(chǔ)鍵值對(duì),使用put( )方法將元素放入到Map中,HashMap使用鍵對(duì)象來計(jì)算hashcode值,HashMap比較快,因?yàn)槭鞘褂梦ㄒ坏逆I來獲取對(duì)象。
HashSet:實(shí)現(xiàn)了Set接口,hashSet僅僅存儲(chǔ)對(duì)象,使用add()方法將元素放入到set中,hashset使用成員對(duì)象來計(jì)算hashcode值,對(duì)于兩個(gè)對(duì)象來說,hashcode可能相同,所以equal方法用來判斷對(duì)象的相等性,如果兩個(gè)對(duì)象不同的話,那么返回false,hashSet較hashMap來說較慢。
24.ArrayList和LinkedList的區(qū)別
ArrayList和LinkedList,前者是Array(動(dòng)態(tài)數(shù)組)的數(shù)據(jù)結(jié)構(gòu),后者是Link(鏈表)的數(shù)據(jù)結(jié)構(gòu),此外他們兩個(gè)都是對(duì)List接口的實(shí)現(xiàn)
當(dāng)隨機(jī)訪問List時(shí)(get和set操作),ArrayList和LinkedList的效率更高,因?yàn)長inkedList是線性的數(shù)據(jù)存儲(chǔ)方式,所以需要移動(dòng)指針從前往后查找。
當(dāng)對(duì)數(shù)據(jù)進(jìn)行增刪的操作時(shí)(add和remove),LinkedList比ArrayList的效率更高,因?yàn)锳rrayList是數(shù)組,所以在其中進(jìn)行增刪操作時(shí),會(huì)對(duì)操作點(diǎn)之后的所有數(shù)據(jù)的下標(biāo)索引造成影響,需要進(jìn)行數(shù)據(jù)的移動(dòng)
從利用效率來看,ArrayList自由性較低,因?yàn)樾枰謩?dòng)的設(shè)置固定大小的容量,但是他的使用比較方便,只需要?jiǎng)?chuàng)建,然后添加數(shù)據(jù),通過調(diào)用下標(biāo)進(jìn)行使用,而LinkedList自由性交給,能夠動(dòng)態(tài)的隨數(shù)據(jù)量的變化而變化,但是它不便于使用。
25.?dāng)?shù)組和鏈表的區(qū)別
數(shù)組:是將元素在內(nèi)存中連續(xù)的存儲(chǔ)的,因?yàn)閿?shù)據(jù)是連續(xù)存儲(chǔ)的,內(nèi)存地址連續(xù),所以在查找數(shù)據(jù)的時(shí)候效率比較高,但在存儲(chǔ)之前,需要申請(qǐng)一塊連續(xù)的內(nèi)存空間,并且在編譯的時(shí)候就必須確定好他的空間大小。在運(yùn)行的時(shí)候空間的大小是無法隨著需要進(jìn)行增加和減少的,當(dāng)數(shù)據(jù)比較大時(shí),有可能出現(xiàn)越界的情況,當(dāng)數(shù)據(jù)比較小時(shí),有可能浪費(fèi)內(nèi)存空間,在改變數(shù)據(jù)個(gè)數(shù)時(shí),增加、插入、刪除數(shù)據(jù)效率比較低。
鏈表:是動(dòng)態(tài)申請(qǐng)內(nèi)存空間的,不需要像數(shù)組需要提前申請(qǐng)好內(nèi)存的大小,鏈表只需在使用的時(shí)候申請(qǐng)就可以了,根據(jù)需要?jiǎng)討B(tài)的申請(qǐng)或刪除內(nèi)存空間,對(duì)于數(shù)據(jù)增加和刪除以及插入比數(shù)組靈活,鏈表中數(shù)據(jù)在內(nèi)存中可以在任意的位置,通過應(yīng)用來關(guān)聯(lián)數(shù)據(jù)。
26.Java中多線程實(shí)現(xiàn)的三種方式
Java中多線程實(shí)現(xiàn)的方式主要有三種:繼承Thread類、實(shí)現(xiàn)Runnable接口、使用ExecutorService、Callable、Future實(shí)現(xiàn)有返回結(jié)果的多線程,其中前兩種方式線程執(zhí)行完沒有返回值,只有最后一種是帶返回值的。
繼承Thread類實(shí)現(xiàn)多線程:繼承Thread類本質(zhì)上也是實(shí)現(xiàn)Tunnable接口的一個(gè)實(shí)例,他代表一個(gè)線程的實(shí)例,并且啟動(dòng)線程的唯一方法是通過Thread類的start()方法,start()方法是一個(gè)native方法,他將啟動(dòng)一個(gè)新線程,并執(zhí)行run( )方法。
實(shí)現(xiàn)Runnable接口方式實(shí)現(xiàn)多線程:實(shí)例化一個(gè)Thread對(duì)象,并傳入實(shí)現(xiàn)的Runnable接口,當(dāng)傳入一個(gè)Runnable target參數(shù)給Thread后,Thraed的run()方法就會(huì)調(diào)用target.run( );
使用ExecutorService、Callable、Future實(shí)現(xiàn)有返回結(jié)果的多線程:可返回值的任務(wù)必須實(shí)現(xiàn)Callable接口,類似的無返回值的任務(wù)必須實(shí)現(xiàn)Runnable接口,執(zhí)行Callable任務(wù)后,可以獲取一個(gè)Future的對(duì)象,在該對(duì)象上調(diào)用get就可以獲取到Callable任務(wù)返回的Object了,在結(jié)合線程池接口ExecutorService就可以實(shí)現(xiàn)有返回結(jié)果的多線程。
27.Java中創(chuàng)建線程的三種方式
Java中使用Thread類代表線程,所有的線程對(duì)象都必須時(shí)Thread類或其子類的實(shí)例,Java中可以用三種方式來創(chuàng)建線程
繼承Java中的Thread類創(chuàng)建線程:定義Thread類的子類,并重寫其run( )方法,run( )方法也稱為線程執(zhí)行體,創(chuàng)建該子類的實(shí)例,調(diào)用線程的start()方法啟動(dòng)線程。
實(shí)現(xiàn)Runnable接口創(chuàng)建線程:定義Runnable接口的實(shí)現(xiàn)類,重寫run()方法,run方法是線程的執(zhí)行體,創(chuàng)建Runnable實(shí)現(xiàn)類的實(shí)例,并用這個(gè)實(shí)例作為Thread的target來創(chuàng)建Thread對(duì)象,這個(gè)Thread對(duì)象才是真正的線程對(duì)象,調(diào)用線程對(duì)象的Start方法啟動(dòng)線程。
使用Callable和Future創(chuàng)建線程:Callable接口提供了一個(gè)call( )方法,作為線程的執(zhí)行體,call( )方法可以有返回值,call( )方法可以聲明拋出異常,其創(chuàng)建線程并啟動(dòng)的步驟,創(chuàng)建Callable接口的實(shí)現(xiàn)類,并實(shí)現(xiàn)call( )方法,創(chuàng)建該實(shí)現(xiàn)類的實(shí)例,使用FutureTask類來包裝Callable對(duì)象,該FuutureTask對(duì)象封裝了callable對(duì)象的call( )方法的返回值,使用FutureTask對(duì)象作為Thread對(duì)象的target創(chuàng)建并啟動(dòng)線程,調(diào)用FutureTask對(duì)象的get( )方法來獲得子線程執(zhí)行結(jié)束后的返回值。
28.線程和進(jìn)程的區(qū)別
線程是進(jìn)程的子集,一個(gè)進(jìn)程可以有很多線程,每條線程并行執(zhí)行不同的任務(wù),不同的進(jìn)程使用不同的內(nèi)存空間,而所有的線程共享一片相同的內(nèi)存空間,注意勿與棧內(nèi)存混淆,每個(gè)線程都擁有單獨(dú)的棧內(nèi)存用來存儲(chǔ)本地?cái)?shù)據(jù)。
29.Java中的線程的run( )方法和start()方法的區(qū)別
start()方法被用來啟動(dòng)新創(chuàng)建的線程,而且start( )內(nèi)部調(diào)用了run ( )方法,這和直接調(diào)用run( )方法的效果不同,當(dāng)調(diào)用run( )方法時(shí),只會(huì)是在原來的線程中調(diào)用,沒有新的線程啟動(dòng),只有start( )方法才會(huì)啟動(dòng)新線程。
30.如何控制某個(gè)方法允許并發(fā)訪問線程的個(gè)數(shù)
在Java中常使用Semaphore(信號(hào)量)來進(jìn)行并發(fā)編程,Semaphore控制的是線程并發(fā)的數(shù)量,實(shí)例化一個(gè)Semaphore對(duì)象,如Semaphore semaphore = newSemaphore(5,true) ,其創(chuàng)建了對(duì)象semaphore,并初始化了5個(gè)信號(hào)量,即最多允許5個(gè)線程并發(fā)訪問,在執(zhí)行的任務(wù)中,調(diào)用semaphore的acquire()方法請(qǐng)求一個(gè)信號(hào)量,這時(shí)信號(hào)量個(gè)數(shù)就減1,(一旦沒有可使用的信號(hào)量,再次請(qǐng)求就會(huì)阻塞),來執(zhí)行任務(wù),執(zhí)行完任務(wù),調(diào)用semaphore的release()方法釋放一個(gè)信號(hào)量此時(shí)信號(hào)量的個(gè)數(shù)就會(huì)加1 。
31.Java中wait和sleep方法的不同
Java程序中wait和sleep都會(huì)造成某種形式的暫停,sleep()方法屬于Thread類中,而wait( )方法屬于Object類中,sleep( )方法是讓程序暫停執(zhí)行指定的時(shí)間,釋放CPU資源,但不會(huì)釋放鎖,線程的監(jiān)控狀態(tài)依然保持著,當(dāng)指定的時(shí)間到了又會(huì)自動(dòng)恢復(fù)運(yùn)行狀態(tài),而當(dāng)調(diào)用wait( )方法的時(shí)候,線程會(huì)放棄對(duì)象鎖,進(jìn)入等待此對(duì)象的等待鎖定池,只有針對(duì)此對(duì)象調(diào)用notify()方法后線程才進(jìn)入對(duì)象鎖定池準(zhǔn)備,獲取對(duì)象鎖進(jìn)入運(yùn)行狀態(tài)。
32.對(duì)Java中wait/notify關(guān)鍵字的理解
wait()、notify()、notifyAll( )都不屬于Thread類,而是屬于Object基礎(chǔ)類,也就是每個(gè)對(duì)象都有wait( )、notify()、notifyAll( )的功能,因?yàn)槊總€(gè)對(duì)象都有鎖,鎖是每個(gè)對(duì)象的基礎(chǔ)。
wait():會(huì)把持有該對(duì)象線程的對(duì)象控制權(quán)交出去,然后處于等待狀態(tài)。
notify():會(huì)通知某個(gè)正在等待這個(gè)對(duì)象的控制權(quán)的線程可以運(yùn)行。
notifyAll( ):會(huì)通知所有等待這個(gè)對(duì)象的控制權(quán)的線程繼續(xù)運(yùn)行,如果有多個(gè)正在等待該對(duì)象控制權(quán)時(shí),具體喚醒哪個(gè)線程,就由操作系統(tǒng)進(jìn)行調(diào)度。
33.什么是線程阻塞?線程該如何關(guān)閉?
阻塞式方法是指程序會(huì)一直等待該方法完成執(zhí)行,而在此期間不做其他的事情,例如ServerSocket的accept( )方法就是一直等待客戶端連接,這里的阻塞是指調(diào)用結(jié)果返回之前,當(dāng)前線程會(huì)被掛起,直到得到結(jié)果之后才返回你,此外還有異步和非阻塞式方法在任務(wù)完成前就返回。
線程關(guān)閉的方法有如下兩種:
一種是調(diào)用線程的stop( )方法;
另一種是自己自行設(shè)計(jì)一個(gè)停止線程的標(biāo)記;
34.如何保證線程的安全
使用Synchronized關(guān)鍵字:
調(diào)用Object類的wait很notify;
通過ThreadLocal機(jī)制實(shí)現(xiàn);
35.實(shí)現(xiàn)線程同步的方式
Java允許線程并發(fā)控制,當(dāng)多個(gè)線程同時(shí)操作一個(gè)可共享的資源變量時(shí)(如數(shù)據(jù)的增刪改查),將會(huì)導(dǎo)致數(shù)據(jù)的不準(zhǔn)確,相互之間產(chǎn)生沖突,因此在加入同步鎖以避免在該線程沒有完成操作之前,被其他線程調(diào)用,從而保證該變量的唯一性和準(zhǔn)確性,同步的方法有以下幾種:
Synchronized關(guān)鍵字修飾的方法:由于Java的每個(gè)對(duì)象都有一個(gè)內(nèi)置鎖,當(dāng)用此關(guān)鍵字修飾方法時(shí),內(nèi)置鎖會(huì)保護(hù)整個(gè)方法;
Synchronized關(guān)鍵字修飾語句塊:被Synchronized關(guān)鍵字修飾的語句塊會(huì)自動(dòng)加上內(nèi)置鎖,從而實(shí)現(xiàn)同步;
使用特殊域變量(volatile)實(shí)現(xiàn)線程同步:當(dāng)一個(gè)共享變量被volatile修飾時(shí),他會(huì)保證修改的值立即被更新到主存中,volatile的特殊性在于,內(nèi)存可見性,就是一個(gè)線程對(duì)于volatile變量的修改,對(duì)于其他線程來說是可見的,即線程每次獲取volatile變量的值都是最新的。
36.Java中Synchronized關(guān)鍵字的用法,以及對(duì)象鎖、方法鎖、類鎖的理解
Java的內(nèi)置鎖:每個(gè)Java對(duì)象都可以用作一個(gè)實(shí)現(xiàn)同步的鎖,這些鎖稱為內(nèi)置鎖,線程進(jìn)入同步代碼塊或方法時(shí),會(huì)自動(dòng)獲得該鎖,在退出同步代碼塊或方法時(shí)會(huì)釋放該鎖,獲得內(nèi)置鎖的唯一途徑就是進(jìn)入這個(gè)鎖的保護(hù)的同步代碼塊或方法。
Java的內(nèi)置鎖是一個(gè)互斥鎖,這即意味著最多只有一個(gè)線程獲得該鎖,當(dāng)線程A嘗試去獲得線程B持有的內(nèi)置鎖時(shí),線程A必須等待或阻塞,直到線程B釋放該鎖,如果B線程不釋放該鎖,那么A線程將一直等待下去。
Java的對(duì)象鎖與類鎖,對(duì)象鎖是用于實(shí)例方法的,或者一個(gè)對(duì)象實(shí)例上的,類鎖是用于類的靜態(tài)方法或者一個(gè)類的class對(duì)象上的,類的對(duì)象實(shí)例可以有很多個(gè),但每個(gè)類只有一個(gè)class對(duì)象,所以不同對(duì)象的實(shí)例的對(duì)象鎖是互不干擾,但是每個(gè)類只有一個(gè)類鎖。
Synchronized的用法:Synchronized修飾方法和修飾代碼塊。
37.Java中鎖與同步的相關(guān)知識(shí)
鎖提供了兩種主要的特性:互斥和可見性
互斥即一次只允許一個(gè)線程持有某個(gè)特定的鎖,因此可使用該特性實(shí)現(xiàn)對(duì)共享數(shù)據(jù)的協(xié)調(diào)訪問協(xié)議,這樣,一次就只有一個(gè)線程能夠使用該共享的數(shù)據(jù);可見性在必須確保鎖釋放之前對(duì)共享對(duì)象做出的更改對(duì)于隨后獲得該鎖的另一個(gè)線程是可見的。
在Java中,為了確保多線程讀寫數(shù)據(jù)時(shí)的一致性,可以采用兩種方式
同步:如使用synchronized關(guān)鍵字,或者使用鎖對(duì)象;
使用volatile關(guān)鍵字:使變量的值發(fā)生改變時(shí)盡快通知其他線程;
Volatile關(guān)鍵字詳解
編譯器為了加快程序的運(yùn)行的速度,對(duì)一些變量的寫操作會(huì)先在寄存器或者CPU緩存上進(jìn)行,最后寫入內(nèi)存中,而在這個(gè)過程中,變量的新值對(duì)于其他線程是不可見的,當(dāng)對(duì)使用volatile標(biāo)記的變量進(jìn)行修改時(shí),會(huì)將其它緩存中存儲(chǔ)的修改前的變量清除,然后重新讀取。
38.Synchronized和volatile關(guān)鍵字的區(qū)別
Volatile在本質(zhì)上是告訴JVM當(dāng)前變量在寄存器(工作內(nèi)存)中的值是不確定的,需要從主存中讀??;Synchronized則是鎖定當(dāng)前變量,只有當(dāng)前線程可以訪問該變量,其他線程會(huì)被阻塞。
Volatile僅能使用在變量級(jí)別,synchronized則可以使用在變量、方法和類級(jí)別。
Volatile僅能修改變量的可見性,不能保證原子性,而synchronized則可以保證變量的修改可見性和原子性。
Volatile不會(huì)造成線程的阻塞,synchronized可能會(huì)造成線程的阻塞。
Volatile標(biāo)記的變量不會(huì)被編譯器優(yōu)化,synchronized標(biāo)記的變量可以被編譯器優(yōu)化。
39.Java的原子性、可見性、有序性的理解
原子性:原子是世界上最小的物質(zhì)單位,具有不可分割性,比如a=0,這個(gè)操作是不可分割的,那么我們就會(huì)說這個(gè)操作是原子操作,再如a++,這個(gè)操作實(shí)際上是a=a+1,是可以分割的,所以他不是一個(gè)原子操作,非原子操作都會(huì)存在線程安全的問題,需要使用synchronized同步技術(shù)來使其變成一個(gè)原子操作,一個(gè)操作是原子操作,那么我么稱它具有原子性。
可見性:是指線程之間的可見性,一個(gè)線程修改的狀態(tài)對(duì)于另一個(gè)線程是可見的,比如用volatile修飾的變量就具有可見性,volatile修飾的變量不允許線程內(nèi)部緩存和重排序,即會(huì)直接修改內(nèi)存,所以對(duì)其他線程是可見的,但volatile只能讓其被修飾的內(nèi)容具有可見性,并不能保證它具有原子性,因?yàn)関olatile僅能使用在變量級(jí)別,并不能對(duì)方法進(jìn)行修飾,
有序性:即線程執(zhí)行的順序按代碼的先后順序執(zhí)行,在Java內(nèi)存模型中,允許編譯器和處理器對(duì)指令進(jìn)行重排序,但重排序過程不會(huì)影響到單線程程序的執(zhí)行,卻會(huì)影響到多線程并發(fā)執(zhí)行的正確性,在Java里面可以通過volatile關(guān)鍵字來保證一定的“有序性”,另外還可以通過synchronized和Lock來保證有序性。
40.ReentrantLock、Synchronized、Volatile關(guān)鍵字
Synchronized:即互斥鎖,即操作互斥,并發(fā)線程,串行得到鎖,串行執(zhí)行代碼,就像一個(gè)房間一把鑰匙,一個(gè)人進(jìn)去后,下一個(gè)人必須等到第一個(gè)人出來得到鑰匙才能進(jìn)去;
ReetrantLock:可重入鎖,和同步鎖功能類似,不過需要顯性的創(chuàng)建和銷毀,其特點(diǎn)在于①ReentrantLock有try Lock方法,如果鎖被其他線程持有,返回false,可以避免形成死鎖,②創(chuàng)建時(shí)可自定義是可搶占的,③ReentrantReadWriteLock,用于讀多寫少,且不需要互斥的場景大大提高性能;
Volatile:只保證同意變量在多線程中的可見,他會(huì)強(qiáng)制將對(duì)緩存的修改操作立即寫入主存,如果是寫操作,會(huì)導(dǎo)致其他CPU對(duì)應(yīng)的緩存無效;
41.Java中死鎖的概念,其產(chǎn)生的四個(gè)必要條件
死鎖是一種情形,多個(gè)線程被阻塞,他們中的一個(gè)或者全部都在等待某個(gè)資源被釋放,由于線程被無限期的阻塞,因此程序不能正常運(yùn)行,簡單的說就是線程死鎖時(shí),第一個(gè)線程等待第二個(gè)線程釋放資源,而同時(shí)第二個(gè)線程又在等待第一個(gè)線程釋放資源;
死鎖產(chǎn)生的四個(gè)必要條件:
互斥條件:一個(gè)資源每次只能被一個(gè)進(jìn)程使用,即在一段時(shí)間內(nèi)某資源僅為一個(gè)進(jìn)程所占有,此時(shí)若有其他請(qǐng)求該資源,則請(qǐng)求進(jìn)程只能等待;
請(qǐng)求與保持條件:進(jìn)程已經(jīng)持有了至少一個(gè)資源,但又提出了新的資源請(qǐng)求,而該資源已經(jīng)被其他進(jìn)程所占有,此時(shí)請(qǐng)求會(huì)被阻塞,但對(duì)自己獲得的資源又保持不放;
不可剝奪條件:進(jìn)程所獲得的資源在未使用完畢之前,不能被其他進(jìn)程強(qiáng)行奪走,只能由獲得該資源的進(jìn)程自己來釋放(只能時(shí)主動(dòng)釋放);
循環(huán)等待條件:即若干進(jìn)程形成首尾相接的循環(huán)等待資源的關(guān)系,即形成了一個(gè)進(jìn)程等待環(huán)路,環(huán)路中每一個(gè)進(jìn)程所占有的資源同時(shí)被另一個(gè)進(jìn)程所申請(qǐng),也就是前一個(gè)進(jìn)程占有后一個(gè)進(jìn)程所申請(qǐng)的資源
這四個(gè)條件是死鎖產(chǎn)生必要條件,只要系統(tǒng)發(fā)生死鎖,這些條件必然成立,而只要上述條件之一不滿足,就不會(huì)發(fā)生死鎖;
死鎖的避免:避免死鎖的基本思想是系統(tǒng)對(duì)進(jìn)程發(fā)出的每一個(gè)系統(tǒng)能夠滿足的資源申請(qǐng)進(jìn)行動(dòng)態(tài)檢查,并根據(jù)檢查結(jié)果決定是否分配資源,如果分配資源后系統(tǒng)可能發(fā)生死鎖,則不分配,否則予以分配,這是一種保證系統(tǒng)不進(jìn)入死鎖狀態(tài)的動(dòng)態(tài)策略。
42.Java中堆和棧的理解
在Java中內(nèi)存分為兩種,一種是棧內(nèi)存,另一種是堆內(nèi)存
堆內(nèi)存:用于存儲(chǔ)Java中的對(duì)象和數(shù)組,當(dāng)我們new一個(gè)對(duì)象或創(chuàng)建一個(gè)數(shù)組的時(shí)候,就會(huì)在堆內(nèi)存中開辟一段空間給它,用于存放,堆內(nèi)存的特點(diǎn):先進(jìn)先出,后今后出,②可以動(dòng)態(tài)的分配內(nèi)存的大小,生存期不必告訴編譯器,但存取速度較慢;
棧內(nèi)存:主要用來執(zhí)行程序用,比如基本類型的變量和對(duì)象的引用變量,其特點(diǎn):①先進(jìn)后出,后進(jìn)后出,②存取速度比堆快,僅次于寄存器,棧數(shù)據(jù)可以共享,但其在棧中的數(shù)據(jù)大小和生存期必須是確定的;
棧內(nèi)存和堆內(nèi)存都屬于Java內(nèi)存的一種,系統(tǒng)會(huì)自動(dòng)去回收它,但對(duì)于堆內(nèi)存開發(fā)人員一般會(huì)自動(dòng)回收。
棧是一塊和線程緊密相關(guān)的內(nèi)存區(qū)域,每個(gè)線程都有自己的棧內(nèi)存,用于存儲(chǔ)本地變量、方法參數(shù)和棧調(diào)用一個(gè)線程中存儲(chǔ)的變量,對(duì)于其他線程是不可見的,而堆是所有線程共享的一個(gè)公用內(nèi)存區(qū)域,對(duì)象都在堆里創(chuàng)建,但為了提升效率,線程會(huì)從堆中拷貝一個(gè)緩存到自己的棧中,如果多個(gè)線程使用該變量,就可能引發(fā)問題,這是volatile修飾變量就可以發(fā)揮作用,他要求線程從主存中讀取變量的值。
43.理解線程間通信
線程是CPU調(diào)度的最小單位(進(jìn)程是CPU分配資源的最小單位),在Android中主線程是不能夠做耗時(shí)的操作的,子線程是不能更新UI的,而線程間的通信方式有很多,比如廣播、接口回掉等,在Android中主要使用handler,handler通過調(diào)用sendMessage方法,將保存好的消息發(fā)送到MessageQueue中,而Looper對(duì)象不斷地調(diào)用loop方法,從MessageQueue中取出message,交給handler處理,從而完成線程間通信。
44.線程中的join()方法的理解,及如何讓多個(gè)線程按順序執(zhí)行
Thread類的join()方法主要作用是同步,它可以使線程之間的并行執(zhí)行變?yōu)榇袌?zhí)行,join()方法把指定的線程加入到當(dāng)前線程中,可以將兩個(gè)交替執(zhí)行的線程合并為順序執(zhí)行的線程,比如在線程B中調(diào)用了線程A的join()方法,則會(huì)等到A線程執(zhí)行完,才會(huì)繼續(xù)執(zhí)行線程B,join方法還可以指定時(shí)長,表示等待該線程執(zhí)行指定時(shí)長后再執(zhí)行其他線程,如A.join(1000),表示等待A線程執(zhí)行1000毫秒后,再執(zhí)行其他線程,當(dāng)有多個(gè)線程、要使他們案順序執(zhí)行時(shí),可以使用join()方法在一個(gè)線程中啟動(dòng)另一個(gè)線程,另外一個(gè)線程在該線程執(zhí)行完之后再繼續(xù)執(zhí)行。
45.工作者線程(workerThread)與主線程(UI線程)的理解
Android應(yīng)用的主線程(UI線程)肩負(fù)著繪制用戶界面,和及時(shí)響應(yīng)用戶操作的重任,為避免“用戶點(diǎn)擊按鈕后沒有反應(yīng)”的狀況,就要確保主線程時(shí)刻保持著較高的響應(yīng)性,把耗時(shí)的任務(wù)移除主線程,交予工作者線程(即子線程)完成,常見的工作者線程有AsyncTask(異步任務(wù))、IntentService、HandlerThread,他們本質(zhì)上都是對(duì)線程或線程池的封裝。
46.AsyncTask(異步任務(wù))的工作原理
AsyncTask是對(duì)Handler和線程池的封裝,使用它可以更新用戶界面,當(dāng)然,這里的更新操作還是在主線程中完成的,但由于AsyncTask內(nèi)部包含了一個(gè)Handler,所以可以發(fā)送消息給主線程,讓他更新UI,另外AsyncTask內(nèi)還包含了一個(gè)線程池,避免了不必要的創(chuàng)建和銷毀線程的開銷。
47.并發(fā)和并行的區(qū)別及理解
在單核的機(jī)器上,“多進(jìn)程”并不是真正多個(gè)進(jìn)程同時(shí)執(zhí)行,而是通過CPU時(shí)間分片,操作系統(tǒng)快速在進(jìn)程間切換而模擬出來的多進(jìn)程,我們通常將這種情況稱為并發(fā),也就是多個(gè)進(jìn)程的運(yùn)行行為是“一并發(fā)生”的,但不是同時(shí)執(zhí)行的,因?yàn)镃PU核數(shù)的限制(CPU和通用寄存器只有有一套),嚴(yán)格來說,同一時(shí)刻只能存在一個(gè)進(jìn)程的上下文。
但在多核CPU上,能真正實(shí)現(xiàn)多個(gè)進(jìn)程并行執(zhí)行,這種情況叫做并行,因?yàn)槎鄠€(gè)進(jìn)程是真正“一并執(zhí)行的”(具體多少個(gè)進(jìn)程可以并行執(zhí)行取決于CPU的核數(shù)),所以可知,并發(fā)是一個(gè)比并行更加寬泛的概念,在單核情況下,并發(fā)只是并發(fā),而在多核情況下,并發(fā)就變?yōu)椴⑿辛恕?/p>
48.同步和異步的區(qū)別、阻塞和非阻塞的區(qū)別的理解
同步:所謂同步是一個(gè)服務(wù)的完成需要依賴其他服務(wù),只有等待被依賴的服務(wù)完成后,依賴的服務(wù)才能算完成,這是一種可靠的服務(wù)序列,要么都成功,要么都失敗服務(wù)的狀態(tài)可以保持一致;
異步:所謂異步是一個(gè)服務(wù)的完成需要依賴其他的服務(wù)時(shí),只要通知其依賴的服務(wù)開始執(zhí)行,而不需要等待被依賴的服務(wù)完成,此時(shí)該服務(wù)就算完成了,至于被依賴的服務(wù)是否真正完成并不關(guān)心,所以這是不可靠的服務(wù)序列;
阻塞:阻塞調(diào)用是指調(diào)用結(jié)果返回之前,當(dāng)前線程會(huì)被掛起,一直處于等待消息通知,不能夠執(zhí)行其他業(yè)務(wù),函數(shù)只有在得到結(jié)果之后才會(huì)返回;
非阻塞:和阻塞的概念相對(duì)應(yīng),指在不能立刻得到結(jié)果之前,該函數(shù)不會(huì)阻塞當(dāng)前線程,而會(huì)立即返回;
阻塞調(diào)用和同步調(diào)用是不同的,對(duì)于同步調(diào)用來說,很多時(shí)候當(dāng)前線程可能還是激活的,只是從邏輯上當(dāng)前函數(shù)沒有返回值而已,此時(shí)這個(gè)線程可能也會(huì)處理其他消息,所以如下總結(jié):
如果這個(gè)線程在等待當(dāng)前函數(shù)返回時(shí),仍在執(zhí)行其他消息處理,這種情況叫做同步非阻塞;
如果這個(gè)線程在等待當(dāng)前函數(shù)返回時(shí),沒有執(zhí)行其他的消息處理,而是處于掛起等待的狀態(tài),這種情況叫做同步阻塞;
如果這個(gè)線程當(dāng)前的函數(shù)已經(jīng)返回,并且仍在執(zhí)行其他的消息處理,這種情況叫做異步非阻塞;
如果這個(gè)線程當(dāng)前的函數(shù)已經(jīng)返回,但沒有執(zhí)行其他的消息處理,而是處于被掛起的等待狀態(tài),這種情況叫做異步阻塞;
同步與異步的重點(diǎn)在于的等待依賴的服務(wù)是否返回結(jié)果(即使沒有執(zhí)行完),也就是結(jié)果通知的方式,而不管其依賴的服務(wù)是否完成,阻塞與非阻塞的重點(diǎn)在于當(dāng)前線程等待消息返回時(shí)的行為,是否執(zhí)行其他的消息處理,當(dāng)前線程是否被掛起;
49.Java中任務(wù)調(diào)度的理解
大部分操作系統(tǒng)(如windows、Linux)的任務(wù)調(diào)度采用時(shí)間片輪轉(zhuǎn)的搶占式調(diào)度方式,也就是說一個(gè)任務(wù)執(zhí)行一小段時(shí)間后強(qiáng)制暫停去執(zhí)行下一個(gè)任務(wù),每個(gè)任務(wù)輪流執(zhí)行,任務(wù)執(zhí)行的一段時(shí)間叫做時(shí)間片,任務(wù)正在執(zhí)行的狀態(tài)叫做運(yùn)行狀態(tài),任務(wù)執(zhí)行一段時(shí)間后強(qiáng)制暫停去執(zhí)行下一個(gè)任務(wù),被暫停的任務(wù)就處于就緒狀態(tài),等待下一個(gè)屬于他的時(shí)間片的到來,這樣每個(gè)任務(wù)都可以得到執(zhí)行,由于CPU的執(zhí)行效率非常高,時(shí)間片非常短,在各個(gè)任務(wù)之間快速的切換,給人的感覺是多個(gè)任務(wù)在“同時(shí)執(zhí)行”,這也即我們所說的并發(fā);
50.Java中進(jìn)程的詳細(xì)概念
計(jì)算機(jī)的額核心是CPU,它承擔(dān)了所有的計(jì)算任務(wù),操作系統(tǒng)是計(jì)算機(jī)的管理者,它負(fù)責(zé)任務(wù)的調(diào)度、資源的分配和管理,統(tǒng)領(lǐng)整個(gè)計(jì)算機(jī)的硬件,應(yīng)用程序是具有某種功能的程序,其運(yùn)行在操作系統(tǒng)上,而進(jìn)程則是一個(gè)具有一定獨(dú)立功能的程序在一個(gè)數(shù)據(jù)集上一次動(dòng)態(tài)執(zhí)行的過程,是操作系統(tǒng)進(jìn)行資源分配和調(diào)度的一個(gè)獨(dú)立單位,是應(yīng)用程序運(yùn)行的載體,進(jìn)程是CPU資源分配的最小單位(線程是CPU執(zhí)行的最小的單元),各個(gè)進(jìn)程之間內(nèi)存地址相互隔離。
51.線程的詳細(xì)概念
線程是程序執(zhí)行中一個(gè)單一的順序控制流程,是程序執(zhí)行流的最小單元,是處理器調(diào)度和分派的基本單位,一個(gè)進(jìn)程可以有多個(gè)線程,各個(gè)線程之間共享程序的內(nèi)存空間(也就是所在進(jìn)程的內(nèi)存空間),線程是一個(gè)進(jìn)程中代碼的不同執(zhí)行路線,線程上下文切換比進(jìn)程上下文切換要快得多。
52.Android中的性能優(yōu)化相關(guān)問題
由于手機(jī)硬件的限制,在Android手機(jī)中過多的使用內(nèi)存,會(huì)容易導(dǎo)致oom(out of memory 內(nèi)存溢出),過多的使用CPU資源,會(huì)導(dǎo)致手機(jī)卡頓,甚至導(dǎo)致ANR(Application Not Responding 應(yīng)用程序無響應(yīng)),Android主要從以下幾部分對(duì)此進(jìn)行優(yōu)化:
布局優(yōu)化,使用herarchyviewer(視圖層次窗口)工具刪除無用的空間和層級(jí);
選擇性能較低的viewgroup,比如在可以選擇RelativeLayout也可以使用LinearLayout的情況下,優(yōu)先使用LinearLayout,因?yàn)橄鄬?duì)來說RelativeLayout功能較為復(fù)雜,會(huì)占用更多的CPU資源;
使用標(biāo)簽重用布局、減少層級(jí)、進(jìn)行預(yù)加載(用的時(shí)候才加載);
繪制優(yōu)化:指view在ondraw方法中避免大量的耗時(shí)的操作,由于onDraw方法可能會(huì)被頻繁的調(diào)用;
ondraw方法中不要?jiǎng)?chuàng)建新的局部變量,ondraw方法被頻繁的調(diào)用,很容易引起GC;
ondraw方法不要做耗時(shí)的操作;
線程優(yōu)化:使用線程池來管理和復(fù)用線程,避免程序中出現(xiàn)大量的Thread,同時(shí)可以控制線 的并發(fā)數(shù),避免相互搶占資源,而導(dǎo)致線程阻塞;
53.內(nèi)存泄漏的相關(guān)原因
如果一個(gè)無用的對(duì)象(不需要再使用的對(duì)象)仍然被其他對(duì)象持有引用,造成該對(duì)象無法被系統(tǒng)回收,以致該對(duì)象在堆中所占有的內(nèi)存單元無法被釋放而造成內(nèi)存空間浪費(fèi),這種情況就是內(nèi)存泄漏,常見的內(nèi)存泄露的場景有:
單例模式:因?yàn)閱卫撵o態(tài)特性使得他的生命周期同應(yīng)用的生命周期一樣長,如果一個(gè)對(duì)象已經(jīng)沒有用處了,但是單例還持有他的引用,那么在某個(gè)應(yīng)用程序的生命周期他都不能正?;厥?,從而導(dǎo)致內(nèi)存泄漏;
靜態(tài)變量導(dǎo)致內(nèi)存泄漏:靜態(tài)變量存儲(chǔ)在方法區(qū),他的生命周期從類加載開始,到整個(gè)進(jìn)程結(jié)束,一旦靜態(tài)變量初始化,他所持有的引用只有等到進(jìn)程結(jié)束才會(huì)釋放;
非靜態(tài)內(nèi)部類導(dǎo)致內(nèi)存泄漏:非靜態(tài)內(nèi)部類(包括匿名內(nèi)部類)默認(rèn)就會(huì)持有外部類的引用,當(dāng)非靜態(tài)內(nèi)部類對(duì)象的生命周期比外部對(duì)象的生命周期長時(shí),就會(huì)導(dǎo)致內(nèi)存泄漏,通常在Android開發(fā)中,如果要使用內(nèi)部類,但又要規(guī)避內(nèi)存泄漏,一般會(huì)采用靜態(tài)內(nèi)部類+弱引用的方式;
未取消注冊(cè)或回掉導(dǎo)致內(nèi)存泄漏:比如在Activity中注冊(cè)廣播,如果在Activity銷毀后不取消注冊(cè),那么這個(gè)廣播會(huì)一直存在于系統(tǒng)中,同非靜態(tài)內(nèi)部類一樣持有Activity引用,導(dǎo)致內(nèi)存泄漏,因此在注冊(cè)廣播后一定要取消注冊(cè);
54.通過Handler在線程間通信的原理
Android中主線程是不能進(jìn)行耗時(shí)操作的,子線程不能進(jìn)行更新UI,所以就有了Handler,其作用就是實(shí)現(xiàn)線程之間的通信。
Handler在整個(gè)線程通信的過程中,主要有四個(gè)對(duì)象,Handler、Message、MessageQueue、Looper等,當(dāng)應(yīng)用創(chuàng)建時(shí),就會(huì)在主線程中創(chuàng)建Handler對(duì)象,將要傳遞的信息保存到Message中,Handler通過調(diào)用sendMessage()方法將Message發(fā)送到MessageQueue中,Looper對(duì)象不斷調(diào)用Loop( )方法從MessageQueue中取出Message交給handler進(jìn)行處理,從而實(shí)現(xiàn)線程之間的通信。
55.Android中動(dòng)畫的類型:
幀動(dòng)畫:通過指定每一幀圖片和播放的時(shí)間,有序地進(jìn)行播放而形成動(dòng)畫效果;
補(bǔ)間動(dòng)畫:通過指定的view的初始狀態(tài)、變化時(shí)間、方式等,通過一系列的算法去進(jìn)行圖形變換從而形成動(dòng)畫效果,主要有Alpha、Scale、Translate、Rotate四種效果;
屬性動(dòng)畫:在Android3.0開始支持,通過不斷改變view的屬性,不斷的重繪而形成動(dòng)畫效果;
55.理解Activity、View、Window三者之間的關(guān)系
使用一個(gè)比喻形容他們的關(guān)系,Activity像一個(gè)工匠(控制單元),Window像窗戶(承載模型)、View像窗花(顯示視圖)、LayoutInflater像剪刀、XML配置像窗花圖紙:
Activity構(gòu)造的時(shí)候會(huì)初始化一個(gè)Window,準(zhǔn)確的說應(yīng)該是PhoneWindow;
這個(gè)PhoneWindow有一個(gè)”ViewRoot“,這個(gè)”ViewRoot”是一個(gè)View或者說是ViewGroup,是最初的根視圖;
“ViewRoot”是通過addView方法來添加一個(gè)個(gè)View的,比如TextView、Button等;
這些view的事件的監(jiān)聽,是由WindowManagerService來接收消息,并且回調(diào)Activity函數(shù),比如onClickListener、onKeyDown等;
56.Android中Context詳解:
Context是一個(gè)抽象基類,譯為上下文,也可理解為環(huán)境,是用戶操作操作系統(tǒng)的一個(gè)過程,這個(gè)對(duì)象描述的是一個(gè)應(yīng)用程序環(huán)境的全局信息,通過它可以訪問應(yīng)用程序的資源和相關(guān)的權(quán)限,簡單的說Context負(fù)責(zé)Activity、Service、Intent、資源、Package和權(quán)限等操作。Context層次如下圖:
第一層:Context抽象類;
第二層:一個(gè)ContextImpl的實(shí)現(xiàn)類,里面擁有一個(gè)PackageInfo類,PackageInfo類是關(guān)于整個(gè)包信息的類,一個(gè)ContextWraper是一個(gè)Context的包裝類,里面有一個(gè)ContextImpl類的實(shí)例,通過整個(gè)實(shí)例去調(diào)用ContextImpl里面的方法;
第三層:Service和Application直接繼承ContextWrapper,但是Activity需要引入主題,所以有了ContextThemeImpl類;
總結(jié):在使用Context對(duì)象獲取靜態(tài)資源,創(chuàng)建單例對(duì)象或者靜態(tài)方法時(shí),多考慮Context的生命周期,不要使用Activity的Context,要使用生命周期較長的Application的Context對(duì)象,但并不是所有的情況都使用Application的Context,在創(chuàng)建Dialog、view等控件的時(shí)候,必須使用Activity的Context對(duì)象。
57.Java中double和float類型的區(qū)別
float是單精度類型,精度是8位有效數(shù)字,取值范圍是10的-38次方到10的38次方,float占用4個(gè)字節(jié)的存儲(chǔ)空間;
double是雙精度類型,精度是10的-308次方到10的308次方,double類型占用8個(gè)字節(jié)的存儲(chǔ)空間;
默認(rèn)情況下都用double來表示,所以如果要用float,則應(yīng)在數(shù)字對(duì)象后加字符f。
58.Android常用的數(shù)據(jù)存儲(chǔ)方式(4種)
使用SharedPreference存儲(chǔ):保存基于xml文件存儲(chǔ)的key-value鍵值對(duì)數(shù)據(jù),通常用來存儲(chǔ)一些簡單的配置信息;
文件存儲(chǔ)方式:Context提供了兩個(gè)方法來打開數(shù)據(jù)文件的文件IO流;
SQLite存儲(chǔ)數(shù)據(jù):SQLite是輕量級(jí)的嵌入式數(shù)據(jù)庫引擎,支持SQL語言;
網(wǎng)絡(luò)存儲(chǔ)數(shù)據(jù):通過網(wǎng)絡(luò)存儲(chǔ)數(shù)據(jù);
59.ANR的了解及優(yōu)化
ANR全名Appication NotResponding ,也就是“應(yīng)用程序無反應(yīng)”,當(dāng)操作在一段時(shí)間內(nèi)無法得到系統(tǒng)回應(yīng)時(shí)就會(huì)出現(xiàn)ANR錯(cuò)誤,造成ANR的主要原因是由于線程的任務(wù)在規(guī)定的時(shí)間內(nèi)沒有完成造成的;
60.Android垃圾回收機(jī)制和程序優(yōu)化System.gc( )
垃圾收集算法的核心思想:對(duì)虛擬機(jī)的可用內(nèi)存空間,即堆空間中的對(duì)象進(jìn)行識(shí)別,如果對(duì)象正在被引用,則稱其為存活對(duì)象,反之,如果對(duì)象不再被引用,則為垃圾對(duì)象,可以回收其占據(jù)的空間,用于再分配。
JVM進(jìn)行次GC的頻率很高,但因?yàn)檫@種GC占用的時(shí)間極短,所以對(duì)系統(tǒng)產(chǎn)生的影響不大,但主GC對(duì)系統(tǒng)的影響很明顯,觸發(fā)主GC的條件主要有下:
當(dāng)應(yīng)用程序空閑時(shí),即沒有應(yīng)用線程在運(yùn)行時(shí),GC會(huì)被調(diào)用,因?yàn)镚C在優(yōu)先級(jí)別最低的線程中進(jìn)行,所以當(dāng)應(yīng)用繁忙時(shí),GC就不會(huì)被調(diào)用;
Java堆內(nèi)存不足時(shí),主GC會(huì)被調(diào)用,當(dāng)應(yīng)用線程正在運(yùn)行,并在運(yùn)行過程中創(chuàng)建新對(duì)象時(shí),若這時(shí)內(nèi)存空間不足,JVM會(huì)強(qiáng)制調(diào)用主GC,以便回收內(nèi)存用于新的分配,若GC一次之后仍無法滿足,則會(huì)繼續(xù)進(jìn)行兩次,若仍無法滿足,則會(huì)報(bào)OOM錯(cuò)誤;
System.gc( )函數(shù)的作用只是提醒虛擬機(jī)希望進(jìn)行一次垃圾回收,但并不一定保證會(huì)進(jìn)行,具體什么時(shí)候進(jìn)行取決于虛擬機(jī);
61.Android平臺(tái)的優(yōu)勢和不足
Android平臺(tái)手機(jī) 5大優(yōu)勢:
開放性:Android平臺(tái)首先就是其開放性,開發(fā)的平臺(tái)允許任何移動(dòng)終端廠商加入到Android聯(lián)盟中來。顯著的開放性可以使其擁有更多的開發(fā)者;
掙脫運(yùn)營商的束縛:在過去很長的一段時(shí)間,手機(jī)應(yīng)用往往受到運(yùn)營商制約,使用什么功能接入什么網(wǎng)絡(luò),幾乎都受到運(yùn)營商的控制,而Android用戶可以更加方便地連接網(wǎng)絡(luò),運(yùn)營商的制約減少;
豐富的硬件選擇:由于Android的開放性,眾多的廠商會(huì)推出千奇百怪,功能特色各具的多種產(chǎn)品。功能上的差異和特色,卻不會(huì)影響到數(shù)據(jù)同步、甚至軟件的兼容;
開發(fā)商不受任何限制:Android平臺(tái)提供給第三方開發(fā)商一個(gè)十分寬泛、自由的環(huán)境,不會(huì)受到各種條條框框的阻擾;
無縫結(jié)合的Google應(yīng)用: Android平臺(tái)手機(jī)將無縫結(jié)合這些優(yōu)秀的Google服務(wù)如地圖、郵件、搜索等;
Android平臺(tái)手機(jī)幾大不足:
安全和隱私:由于手機(jī)與互聯(lián)網(wǎng)的緊密聯(lián)系,個(gè)人隱私很難得到保守。除了上網(wǎng)過程中經(jīng)意或不經(jīng)意留下的個(gè)人足跡,Google這個(gè)巨人也時(shí)時(shí)站在你的身后,洞穿一切;
過分依賴開發(fā)商缺少標(biāo)準(zhǔn)配置:在Android平臺(tái)中,由于其開放性,軟件更多依賴第三方廠商,比如Android系統(tǒng)的SDK中就沒有內(nèi)置音樂播放器,全部依賴第三方開發(fā),缺少了產(chǎn)品的統(tǒng)一性;
同類機(jī)型用戶很少:在不少手機(jī)論壇都會(huì)有針對(duì)某一型號(hào)的子論壇,對(duì)一款手機(jī)的使用心得交流,并分享軟件資源。而對(duì)于Android平臺(tái)手機(jī),由于廠商豐富,產(chǎn)品類型多樣,這樣使用同一款機(jī)型的用戶越來越少,缺少統(tǒng)一機(jī)型的程序強(qiáng)化。
62.其他講解:
Activity組件
在Activity的生命周期中,可以將Activity表現(xiàn)為3種狀態(tài):
激活態(tài):當(dāng)Acitivity位于屏幕前端,并可以獲得用戶焦點(diǎn)、收用戶輸入時(shí),這種狀態(tài)稱為激活態(tài),也可以稱為運(yùn)行態(tài);
暫停態(tài):當(dāng)Activity在運(yùn)行時(shí)被另一個(gè)Activity所遮擋并獲取焦點(diǎn),此時(shí)Activity仍然可見,也就是說另一個(gè)Activity是部分遮擋的,或者另一個(gè)Activity是透明的或半透明的,此時(shí)Activity處于暫停狀態(tài);
停止態(tài):當(dāng)Activity被另一個(gè)Activity完全遮擋不可見時(shí)處于停止?fàn)顟B(tài),這個(gè)Activity仍然存在,它保留在內(nèi)存中并保持所有狀態(tài)和成員信息,但是當(dāng)該設(shè)備內(nèi)存不足時(shí),該Activity可能會(huì)被系統(tǒng)殺掉以釋放其占用的內(nèi)存空間,當(dāng)再次打開該Activity時(shí),它會(huì)被重新創(chuàng)建;
Activity生命周期中的7個(gè)方法:
onCreate( ):當(dāng)Activity被創(chuàng)建時(shí)調(diào)用;
onStart( ):當(dāng)Activity被創(chuàng)建后將可見時(shí)調(diào)用;
onResume( ):(繼續(xù)開始)當(dāng)Activity位于設(shè)備最前端,對(duì)用戶可見時(shí)調(diào)用;
onPause( ):(暫停)當(dāng)另一個(gè)Activity遮擋當(dāng)前Activity,當(dāng)前Activity被切換到后臺(tái)時(shí)調(diào)用;
onRestart( ):(重新啟動(dòng))當(dāng)另一個(gè)Activity執(zhí)行完onStop()方法,又被用戶打開時(shí)調(diào)用;
onStop( ):如果另一個(gè)Activity完全遮擋了當(dāng)前Activity時(shí),該方法被調(diào)用;
onDestory( ):當(dāng)Activity被銷毀時(shí)調(diào)用;
Activity的四種啟動(dòng)模式:standard、singleTop、singleTask和singleInstance,他們是在配置文件中通過android:LauchMode屬性配置;
standard:默認(rèn)的啟動(dòng)模式,每次啟動(dòng)會(huì)在任務(wù)棧中新建一個(gè)啟動(dòng)的Activity的實(shí)例;
SingleTop:如果要啟動(dòng)的Activity實(shí)例已位于棧頂,則不會(huì)重新創(chuàng)建該Activity的實(shí)例,否則會(huì)產(chǎn)生一個(gè)新的運(yùn)行實(shí)例;
SingleTask:如果棧中有該Activity實(shí)例,則直接啟動(dòng),中間的Activity實(shí)例將會(huì)被關(guān)閉,關(guān)閉的順序與啟動(dòng)的順序相同;
SingleInstance:該啟動(dòng)模式會(huì)在啟動(dòng)一個(gè)Activity時(shí),啟動(dòng)一個(gè)新的任務(wù)棧,將該Activity實(shí)例放置在這個(gè)任務(wù)棧中,并且該任務(wù)棧中不會(huì)再保存其他的Activity實(shí)例;
Activity任務(wù)棧:即存放Activity任務(wù)的棧,每打開一個(gè)Activity時(shí)就會(huì)往Activity棧中壓入一個(gè)Activity
任務(wù),每當(dāng)銷毀一個(gè)Activity的時(shí)候,就會(huì)從Activity任務(wù)棧中彈出一個(gè)Activity任務(wù),
由于安卓手機(jī)的限制,只能從手機(jī)屏幕獲取當(dāng)前一個(gè)Activity的焦點(diǎn),即棧頂元素(
最上面的Activity),其余的Activity會(huì)暫居后臺(tái)等待系統(tǒng)的調(diào)用;
關(guān)于任務(wù)棧的更多概念:
當(dāng)程序打開時(shí)就創(chuàng)建了一個(gè)任務(wù)棧,用于存儲(chǔ)當(dāng)前程序的Activity,當(dāng)前程序(包括被當(dāng)前程序所調(diào)用的)所有的Activity都屬于一個(gè)任務(wù)棧;
一個(gè)任務(wù)棧包含了一個(gè)Activity的集合,可以有序的選擇哪個(gè)Activity和用戶進(jìn)行交互,只有任務(wù)棧頂?shù)腁ctivity才可以與用戶進(jìn)行交互;
任務(wù)??梢砸苿?dòng)到后臺(tái),并保留每一個(gè)Activity的狀態(tài),并且有序的給用戶列出他們的任務(wù),而且還不會(huì)丟失他們的狀態(tài)信息;
退出應(yīng)用程序時(shí),當(dāng)把所有的任務(wù)棧中所有的Activity清除出棧時(shí),任務(wù)棧會(huì)被銷毀,程序退出;
默認(rèn)Acctivity啟動(dòng)方式的缺點(diǎn):
每開啟一次頁面都會(huì)在任務(wù)棧中添加一個(gè)Activity,而只有任務(wù)棧中的Activity全部清除出棧時(shí),任務(wù)棧被銷毀,程序才會(huì)退出,這樣就造成了用戶體驗(yàn)差,需要點(diǎn)擊多次返回才可以把程序退出了。
每開啟一次頁面都會(huì)在任務(wù)棧中添加一個(gè)Activity還會(huì)造成數(shù)據(jù)冗余重復(fù)數(shù)據(jù)太多,會(huì)導(dǎo)致內(nèi)存溢出的問題(OOM)。
Service組件
Service組件主要用于處理不干擾用戶交互的后臺(tái)操作,如更新應(yīng)用、播放音樂等,Service組件既不會(huì)開啟新的進(jìn)程,也不會(huì)開啟新的線程,它運(yùn)行在應(yīng)用程序的主線程中,Service實(shí)現(xiàn)的邏輯代碼不能阻塞整個(gè)應(yīng)用程序的運(yùn)行,否則會(huì)引起應(yīng)用程序拋出ANR異常。
Service組件常被用于實(shí)現(xiàn)以下兩種功能(分別對(duì)應(yīng)兩種啟動(dòng)模式):
使用startService()方法啟動(dòng)Service組件,運(yùn)行在系統(tǒng)的后臺(tái)在不需要用戶交互的前提下,實(shí)現(xiàn)某些功能;
使用bindService()方法啟動(dòng)Service組件,啟動(dòng)者與服務(wù)者之間建立“綁定關(guān)系”,應(yīng)用程序可以與Service組件交互;
Service中常用的方法:
onBind(Intent intent):抽象方法綁定模式時(shí)執(zhí)行的方法,通過Ibinder對(duì)象訪問Service;
onCreate():Service組件創(chuàng)建時(shí)執(zhí)行;
onDestroy():Service組件銷毀時(shí)執(zhí)行;
onStartCommand( Intent intent ,int flags ,int startId ):開始模式時(shí)執(zhí)行的方法,每次執(zhí)行startService()方法,該方法都會(huì)執(zhí)行;
onUnBind( ):解除綁定時(shí)執(zhí)行;
stop Self():停止Service組件;
Service組件的生命周期:Service有兩種啟動(dòng)模式,startService方法或bindServce方法,啟動(dòng)方法決定了Service組件的生命周期和他所執(zhí)行的生命周期方法:
通過statrtService方法啟動(dòng)Service組件,如果已經(jīng)存在目標(biāo)組件,則會(huì)直接調(diào)用onStartCommand方法,否則回調(diào)哦那Create()方法,創(chuàng)建Service對(duì)象,啟動(dòng)后的Service組件會(huì)一直運(yùn)行在后臺(tái),與“啟動(dòng)者”無關(guān)聯(lián),其狀態(tài)不會(huì)受“啟動(dòng)者”的影響,即使“啟動(dòng)者被銷毀,Service組件還會(huì)繼續(xù)運(yùn)行,直到調(diào)用StopService()方法,或執(zhí)行stopSelf()方法;
通過bindService()方法,啟動(dòng)Service組件,如果組件對(duì)象已經(jīng)存在,則與之綁定,否則創(chuàng)建性的Service對(duì)象后再與之綁定,這種啟動(dòng)方法把Service組件與“啟動(dòng)者“綁定,Service返回Ibinder對(duì)象,啟動(dòng)者借助ServiceConnection對(duì)象實(shí)例實(shí)現(xiàn)與之交互,這種啟動(dòng)方式會(huì)將Service組件與”啟動(dòng)者“相關(guān)聯(lián),Service的狀態(tài)會(huì)受到啟動(dòng)者的影響;
Service的啟動(dòng)模式詳解
啟動(dòng)和停止Service組件的方法都位于context類中,再Activity中可以直接調(diào)用;
startService(Intent service):以Start模式啟動(dòng)Service組件,參數(shù)service是目標(biāo)組件;
stopService(Intent service):停止start模式啟動(dòng)的Service組件,參數(shù)service是目標(biāo)組件;
bindService(Intent service ,serviceConnection conn ,int flags):以Bind模式啟動(dòng)Service組件,參數(shù)service是目標(biāo)組件,conn是與目標(biāo)鏈接的對(duì)象,不可為NULL,flags是綁定模式;
unbindService(ServiceConnection conn):解除綁定模式啟動(dòng)的Service組件,conn是綁定時(shí)的鏈接對(duì)象;
BoradcastReceiver組件
廣播機(jī)制是安卓系統(tǒng)中的通信機(jī)制,可以實(shí)現(xiàn)在應(yīng)用程序內(nèi)部或應(yīng)用程序之間傳遞消息的作用,發(fā)出廣播(或稱廣播)和接收廣播是兩個(gè)不同的動(dòng)作,Android系統(tǒng)主動(dòng)發(fā)出的廣播稱為系統(tǒng)廣播,應(yīng)用程序發(fā)出廣播稱為自定義廣播,廣播實(shí)質(zhì)上是一個(gè)Intent對(duì)象,接收廣播實(shí)質(zhì)上是接收Intent對(duì)象。
接收廣播需要自定義廣播接收器類,繼承自BoradcastReceiver類,BoradcastReceiver的生命周期很短,BoradcastReceiver對(duì)象在執(zhí)行完onReceiver()方法后就會(huì)被銷毀,在onReceiver方法中不能執(zhí)行比較耗時(shí)的操作,也不能在onReceiver方法中開啟子線程,因?yàn)楫?dāng)父線程被殺死后,他的子線程也會(huì)被殺死,從而導(dǎo)致線程不安全。
廣播分為有序廣播和無序廣播
無序廣播:通過sendBoradcast()方法發(fā)送的廣播,普通廣播對(duì)于接收器來說是無序的,沒有優(yōu)先級(jí),每個(gè)接收器都無需等待即可以接收到廣播,接收器之間相互是沒有影響的,這種廣播無法被終止,即無法阻止其他接收器的接收動(dòng)作。
有序廣播:通過sendOrderedBroadcast()方法發(fā)送的廣播,有序廣播對(duì)于接收器來說是有序的,有優(yōu)先級(jí)區(qū)分的,接收者有權(quán)終止廣播,使后續(xù)接收者無法接收到廣播,并且接收者接收到廣播后可以對(duì)結(jié)果對(duì)象進(jìn)行操作。
注冊(cè)廣播接收器的方式:配置文件靜態(tài)注冊(cè)和在代碼中動(dòng)態(tài)注冊(cè)。
配置文件中靜態(tài)注冊(cè):BoradcastReceiver組件使用標(biāo)簽配置,寫在application標(biāo)簽內(nèi)部,receiver標(biāo)簽內(nèi)的標(biāo)簽用于設(shè)置廣播接收器能夠響應(yīng)的廣播動(dòng)作。
使用代碼動(dòng)態(tài)注冊(cè):使用context中的registerReceiver(BoradcastReceiver receiver ,IntentFileter filter)方法,參數(shù)receiver是需要注冊(cè)的廣播接收器,參數(shù)filter是用于選擇相匹配的廣播接收器,使用這種方法注冊(cè)廣播接收器,最后必須解除注冊(cè),解除注冊(cè)使用context的unregisterReceiver(BoradcastReceiverreceiver)方法,參數(shù)receiver為要解除注冊(cè)的廣播接收器。
配置文件靜態(tài)注冊(cè)和在代碼中動(dòng)態(tài)注冊(cè)兩種方式的區(qū)別
靜態(tài)注冊(cè)(也稱常駐型注冊(cè)):這種廣播接收器需要在Androidmainfest.xml中進(jìn)行注冊(cè),這種方式注冊(cè)的廣播,不受頁面生命周期的影響,即使退出了頁面,也可以收到廣播,這種廣播一般用于開機(jī)自啟動(dòng),由于這種注冊(cè)方式的廣播是常駐型廣播,所以會(huì)占用CPU資源。
動(dòng)態(tài)注冊(cè):是在代碼中注冊(cè)的,這種注冊(cè)方式也叫非常駐型注冊(cè),會(huì)受到頁面生命周期的影響,退出頁面后就不會(huì)收到廣播,我們通常將其運(yùn)用在更新UI方面,這種注冊(cè)方式優(yōu)先級(jí)較高,最后需要解綁(解除注冊(cè)),否則會(huì)導(dǎo)致內(nèi)存泄漏)。
要點(diǎn):使用廣播機(jī)制更新UI的思路,在需要更新的Activity內(nèi)定義一個(gè)繼承自BroadcastReceiver的 內(nèi)部類,在Activty中動(dòng)態(tài)注冊(cè)該廣播接收器,通過廣播接收器的的onReceiver()方法來更新UI。
ContentProvider(內(nèi)容提供者)組件
Android系統(tǒng)將所有的數(shù)據(jù)都規(guī)定為私有,無法直接訪問應(yīng)用程序之外的數(shù)據(jù),如果需要訪問其他程序的數(shù)據(jù)或向其他程序提供數(shù)據(jù),需要用到ContentProvider抽象類,該抽象類為存儲(chǔ)和獲取數(shù)據(jù)提供了統(tǒng)一的接口,ContentProvider對(duì)數(shù)據(jù)進(jìn)行了封裝,也在使用時(shí)不必關(guān)心數(shù)據(jù)的存儲(chǔ)方式。
URI(統(tǒng)一資源標(biāo)識(shí)符):一個(gè)Uri一般有三部分組成,分別是訪問協(xié)議、唯一性標(biāo)識(shí)字符串(或訪問權(quán)限字符串)、資源部份。
ContentProvider實(shí)現(xiàn)對(duì)外部程序數(shù)據(jù)操作的思路:
在一個(gè)程序中自定義ContentProvider類(繼承自ContentProvider),并實(shí)現(xiàn)其中的抽象方法,在配置文件中使用標(biāo)簽對(duì)其進(jìn)行注冊(cè)(標(biāo)簽中有3個(gè)很重要的屬性:name為指明自定義的ContentProvider的全路徑,authories為訪問標(biāo)識(shí),exported是否允許外部應(yīng)用訪問),在另一個(gè)需要操作該程序數(shù)據(jù)的程序中,通過context類的getContentResolver方法獲得ContentResolver類的實(shí)例對(duì)象,ContentResolver類執(zhí)行增、刪、改、查操作的方法與ContentProvider類的幾乎一致,通過調(diào)用ContentResolver類實(shí)現(xiàn)類的方法來實(shí)現(xiàn)數(shù)據(jù)操作(在這些方法中仍然會(huì)用到Uri參數(shù),其應(yīng)與被操作數(shù)據(jù)的所在程序中的定義的Uri一致)。
Fragment
Android3.0引入Fragment技術(shù),譯為“碎片、片段”,在Activity中可以通過FragmentManager來添加、移除和管理所加入的Fragment,每個(gè)Fragment都有自己的布局、生命周期、交互事件的處理,由于Fragment是嵌入到Activity中的,所以Fragment的生命周期又和Activity的生命周期有密切的關(guān)聯(lián)。
Fragment的生命周期的方法:
onAttach( ):當(dāng)Fagment和Activity產(chǎn)生關(guān)聯(lián)時(shí)被調(diào)用;
onCreate( ):當(dāng)Fragment被創(chuàng)建時(shí)調(diào)用;
onCreateView():創(chuàng)建并返回與Fragment相關(guān)的view界面;
onViewCreate():在onCreateView執(zhí)行完后立即執(zhí)行;
onActivityCreated( ):通知Fragment,他所關(guān)聯(lián)的Activity已經(jīng)完成了onCreate的調(diào)用;
onStart( ):讓Fragment準(zhǔn)備可以被用戶所見,該方法和Activity的onStart()方法相關(guān)聯(lián);
onResume():Fragment可見,可以和用戶交互,該方法和Activity的onResume方法相關(guān)聯(lián);
onPause():當(dāng)用戶離開Fragment時(shí)調(diào)用該方法,此操作是由于所在的Activity被遮擋或者是在Activity中的另一個(gè)Fragment操作所引起的;
onStop():對(duì)用戶而言,F(xiàn)ragment不可見時(shí)調(diào)用該方法,此操作是由于他所在的Activity不再可見或者是在Activity中的一個(gè)Fragment操作所引起的;
onDestroyView():ment清理和它的view相關(guān)的資源;
onDestroy():最終清理Fragment的狀態(tài);
onDetach():Fragment與Activity不再產(chǎn)生關(guān)聯(lián);
Fragment加載布局文件是在onCreateView()方法中使用LayoutInflater(布局加載器)的inflate( )方法加載布局文件。
Fragment中傳遞數(shù)據(jù):當(dāng)Activity加載Fragment的時(shí)候,往其內(nèi)部傳遞參數(shù),官方推薦使用Fragment的setArguments(Bundle bundle)方式傳遞參數(shù),具體方法為在Activity中,對(duì)要傳遞參數(shù)的Fragment調(diào)用setArguments(Bundle bundle)方法,在接收參數(shù)的Fragment中調(diào)用context的getArguments()方法獲取Bundle參數(shù);
管理Fragment:通過調(diào)用context的getFragmentManager( )方法或者getsupportFragmentManager( )方法(這兩個(gè)方法需要導(dǎo)入的包不同)來獲取FragmentManager,使用FragmentManager對(duì)象,主要可以調(diào)用如下方法:
findFragmentById/findFragmentByTag:獲取Activity中已經(jīng)存在的Fragment;
getFragments( ):獲取所有加入Activity中的Fragment;
begainTransaction( ):獲取一個(gè)FragmentTransaction對(duì)象,用來執(zhí)行Fragment的事物;
popBackStack():從Activity的后退棧中彈出Fragment;
addOnBackChagedListerner:注冊(cè)一個(gè)偵聽器以監(jiān)視后退棧的變化;
FragmentTransaction:
在Activity中對(duì)Fragment進(jìn)行添加、刪除、替換以及執(zhí)行其他的動(dòng)作將引起Fragment的變化,叫做一個(gè)事務(wù),事務(wù)是通過FragmentTransaction來執(zhí)行的,可以用add、remove、replace、show、hide( )等方法構(gòu)成事務(wù),最后使用commit提交事務(wù),參考代碼:
getSupportFragmentManager( ).beginTransaction() .add(R.id.main_container,tFragment) .add(R.id.main_container,eFragment) .hide(eFragment).commit( );
其中R.id.main_container為布局中容納Fragment的view的ID。
ViewPager
ViewPager是google SDK自帶的一個(gè)附加包(android.support.V4)中的一個(gè)類,可視為一個(gè)可以實(shí)現(xiàn)一種卡片式左右滑動(dòng)的View容器,使用該類類似于ListView,需要用到自定義的適配器PageAdapter,區(qū)別在于每次要獲取一個(gè)view的方式,ViewPager準(zhǔn)確的說應(yīng)該是一個(gè)ViewGroup。
PagerAdapter是ViewPager的支持者,ViewPager調(diào)用它來獲取所需顯示的頁面,而PagerAdapter也會(huì)在數(shù)據(jù)變化時(shí),通知ViewPager,這個(gè)類也是FragmentPagerAdapter和FragmentStatePagerAdapter的基類。
FragmentPageAdapter和FragmentStatePagerAdapter的區(qū)別
FragmentPageAdapter:和PagerAdapter一樣,只會(huì)緩存當(dāng)前Fragment以及左邊一個(gè)和右邊一個(gè),如果左邊或右邊不存在Fragment則不緩存;
FragmentStatePagerAdapter:當(dāng)Fragment對(duì)用戶不可見時(shí),整個(gè)Fragment會(huì)被銷毀,只會(huì)保存Fragment的狀態(tài),而在頁面需要重新顯示的時(shí)候,會(huì)生成新的頁面;
綜上,F(xiàn)ragmentPagerAdapter適合頁面較少的場合,而FragmentStatePagerAdapter則適合頁面較多或頁面的內(nèi)容非常復(fù)雜(需要占用大量內(nèi)存)的情況。
當(dāng)實(shí)現(xiàn)一個(gè)PagerAdapter時(shí),需要重寫相關(guān)方法:
getCount( ):獲得viewPager中有多少個(gè)view ;
destroyItem(viewGroup ,interesting,object):移除一個(gè)給定位置的頁面;
instantiateItem(ViewGroup ,int ):將給定位置的view添加到viewgroup(容器中),創(chuàng)建并顯示出來,返回一個(gè)代表新增頁面的Object(key),key和每個(gè)view要有一一對(duì)應(yīng)的關(guān)系;
isviewFromObject( ):判斷instantiateItem(ViewGroup,int )函數(shù)所返回的key和每一個(gè)頁面視圖是否是代表的同一個(gè)視圖;
綜合使用ViewPager、Fragment和FragmentPagerAdapter:
自定義一個(gè)繼承FragmentPagerAdapter的子類,重寫其相關(guān)方法,當(dāng)在實(shí)例化該子類時(shí),可以傳入Fragmnt集合(即要使用到的全部Fragmnet的集合),將ViewPager與該自定義的適配器實(shí)例綁定,為ViewPager設(shè)置OnPagerListener( )監(jiān)聽事件,重寫OnPagerChangeListener的onPageSelected( )方法,實(shí)現(xiàn)頁面的翻轉(zhuǎn)。
關(guān)于Fragment中的控件的事件的監(jiān)聽:
在Fragment中的onActivityCreated( )生命周期方法中通過context的getActivity()方法獲取到Fragment類相關(guān)聯(lián)的Activity,并就此Activity通過findViewById( )方法來獲取相關(guān)組件,再為組件添加監(jiān)聽事件。
Android的事件傳遞(分發(fā))機(jī)制
基礎(chǔ)概念:
事件分發(fā)的對(duì)象:點(diǎn)擊事件(Touch事件),當(dāng)用戶觸摸屏幕時(shí)(view或viewGroup派生的控件),將產(chǎn)生點(diǎn)擊事件(Touch事件),Touch事件的相關(guān)細(xì)節(jié)(發(fā)生觸摸的位置、時(shí)間等)被封裝程MotionEvent對(duì)象;
事件的類型:
MotionEvent . ACTION_DOWN 按下view(所有事件的開始)
MotionEvent .ACTION_UP 抬起view(與DOWN對(duì)應(yīng))
MotionEvent .ACTION_MOVE 滑動(dòng)view
MotionEvent .ACTION_CANCEL 結(jié)束事件(非人為的原因)
事件列:從手指觸摸至離開屏幕,這個(gè)過程中產(chǎn)生的一系列事件為事件列,一般情況下,事件列都是以DOWN事件開始,UP事件結(jié)束,中間有無數(shù)的MOVE事件;
事件分發(fā)的本質(zhì):將點(diǎn)擊事件(MotionEvent)傳遞到某個(gè)具體的View&處理的整個(gè)過程,即事件的傳遞過程=分發(fā)過程;
事件在哪些對(duì)象之間傳遞:Activity、viewGroup、view等對(duì)象;
事件分發(fā)過程中協(xié)作完成的方法:
dispatchTouchEvent() 、onInterceptTouchEvent()和onTouchEvent()方法;
Android事件傳遞機(jī)制跟布局的層次密切相關(guān),一個(gè)典型的布局包括根節(jié)點(diǎn)ViewGroup、子ViewGroup、以及最末端的view(如下圖):
在這種結(jié)構(gòu)中最上層的是RootViewGroup,下層是子view,當(dāng)我們點(diǎn)擊子view的時(shí)候,點(diǎn)擊事件從上層往下層依次傳遞(即下沉傳遞Activity——ViewGroup——View),傳遞的過程中調(diào)用dispatchTouchEvent和onInterceptTouchEvent函數(shù),當(dāng)事件傳遞到被點(diǎn)擊的子view之后,停止事件的傳遞,開始改變方向(即冒泡響應(yīng)View ——ViewGroup——Activity),依次向上層響應(yīng),響應(yīng)的過程調(diào)用onTouch或onTouchEvent方法。
傳遞過程中的協(xié)作方法:
public boolean dispatchTouchEvent(MotionEvent ev):事件分發(fā)處理函數(shù),通常會(huì)在Activity層根據(jù)UI的情況,把事件傳遞給相應(yīng)的ViewGroup;
public boolean onIntereptTouchEvent(MotionEvent ev):對(duì)分發(fā)的事件進(jìn)行攔截(注意攔截ACTION_DOWN和其他ACTION的差異),有兩種情況:
第一種情況:如果ACTION_DOWN的事件沒有被攔截,順利找到了TargetView,那么后續(xù)的MOVE和UP都能夠下發(fā),如果后續(xù)的MOVE與UP下發(fā)時(shí)還有繼續(xù)攔截的話,事件只能傳遞到攔截層,并且發(fā)出ACTION_CANCEL;
第二種情況:如果ACTION_DOWN的事件下發(fā)時(shí)被攔截,導(dǎo)致沒有找到TargetView,那么后續(xù)的MOVE和UP都無法向下派發(fā)了,在Activity層就終止了傳遞。
public boolean onTouchEvent(MotionEvent ev):響應(yīng)處理函數(shù),如果設(shè)置對(duì)應(yīng)的Listener的話,這里還會(huì)與OnTouch、onClick、onLongClick相關(guān)聯(lián),具體的執(zhí)行順序是:
onTouch()——>onTouchEvent()——>onClick()——>onLongClick(),是否能夠順序執(zhí)行,取決于每個(gè)方法返回值是true還是false。
Bitmap的使用及內(nèi)存優(yōu)化
位圖是相對(duì)于矢量圖而言的,也稱為點(diǎn)陣圖,位圖由像素組成,圖像的清晰度由單位長度內(nèi)的像素的多少來決定的,在Android系統(tǒng)中,位圖使用Bitmap類來表示,該類位于android.graphics包中,被final所修飾,不能被繼承,創(chuàng)建Bitmap對(duì)象可使用該類的靜態(tài)方法createBitmap,也可以借助BitmapFactory類來實(shí)現(xiàn)。Bitmap可以獲取圖像文件信息,如寬高尺寸等,可以進(jìn)行圖像剪切、旋轉(zhuǎn)、縮放等操作.
BitmapFactory是創(chuàng)建Bitmap的工具類,能夠以文件、字節(jié)數(shù)組、輸入流的形式創(chuàng)建位圖對(duì)象,BitmapFactory類提供的都是靜態(tài)方法,可以直接調(diào)用,BitmapFactory. Options類是BitmapFactory的靜態(tài)內(nèi)部類,主要用于設(shè)定位圖的解析參數(shù)。在解析位圖時(shí),將位圖進(jìn)行相應(yīng)的縮放,當(dāng)位圖資源不再使用時(shí),強(qiáng)制資源回收,可以有效避免內(nèi)存溢出。
縮略圖:不加載位圖的原有尺寸,而是根據(jù)控件的大小呈現(xiàn)圖像的縮小尺寸,就是縮略圖。
將大尺寸圖片解析為控件所指的尺寸的思路:
實(shí)例化BitmapFactory . Options對(duì)象來獲取解析屬性對(duì)象,設(shè)置BitmapFactory. Options的屬性inJustDecodeBounds為true后,再解析位圖時(shí)并不分配存儲(chǔ)空間,但可以計(jì)算出原始圖片的寬度和高度,即outWidth和outHeight,將這兩個(gè)數(shù)值與控件的寬高尺寸相除,就可以得到縮放比例,即inSampleSize的值,然后重新設(shè)置inJustDecodeBounds為false,inSampleSize為計(jì)算所得的縮放比例,重新解析位圖文件,即可得到原圖的縮略圖。
獲取控件寬高屬性的方法:可以利用控件的getLayoutParams()方法獲得控件的LayoutParams對(duì)象,通過LayoutParams的Width和Height屬性來得到控件的寬高,同樣可以利用控件的setLayoutParams( )方法來動(dòng)態(tài)的設(shè)置其寬高,其中LayoutParams是繼承于Android.view.viewGroup.LayoutParams,LayoutParams類是用于child view(子視圖)向parent view(父視圖)傳遞布局(Layout)信息包,它封裝了Layout的位置、寬、高等信息。
Bitmap的內(nèi)存優(yōu)化:
及時(shí)回收Bitmap的內(nèi)存:Bitmap類有一個(gè)方法recycle( ),用于回收該Bitmap所占用的內(nèi)存,當(dāng)保證某個(gè)Bitmap不會(huì)再被使用(因?yàn)锽itamap被強(qiáng)制釋放后,再次使用它會(huì)拋出異常)后,能夠在Activity的onStop()方法或onDestory()方法中將其回收,回收方法:
if ( bitmap !=null && !bitmap.isRecycle ( ) ){ bitmap.recycle( ); bitmap=null ; } System.gc( ) ;
System.gc()方法可以加快系統(tǒng)回收內(nèi)存的到來;
捕獲異常:為了避免應(yīng)用在分配Bitmap內(nèi)存時(shí)出現(xiàn)OOM異常以后Crash掉,需在對(duì)Bitmap實(shí)例化的過程中進(jìn)行OutOfMemory異常的捕獲;
緩存通用的Bitmap對(duì)象:緩存分為硬盤緩存和內(nèi)存緩存,將常用的Bitmap對(duì)象放到內(nèi)存中緩存起來,或?qū)木W(wǎng)絡(luò)上獲取到的數(shù)據(jù)保存到SD卡中;
壓縮圖片:即以縮略圖的形式顯示圖片。
使用View繪制視圖
View類是Android平臺(tái)中各種控件的父類,是UI(用戶界面)的基礎(chǔ)構(gòu)件,View相當(dāng)于屏幕上的一塊矩形區(qū)域,其重復(fù)繪制這個(gè)區(qū)域和處理事件,View是所有Weight類(組件類)的父類,其子類ViewGroup是所有Layout類的父類,如果需要自定義控件,也要繼承View,實(shí)現(xiàn)onDraw方法和事件處理方法。
View繪制的流程:OnMeasure()——>OnLayout()——>OnDraw()
OnMeasure():測量視圖大小,從頂層父View到子View遞歸調(diào)用measure方法,measure方法又回調(diào)OnMeasure。
OnLayout():確定View的位置,進(jìn)行頁面的布局,從頂層父View向子View的遞歸調(diào)用View.Layout方法的過程,即父View根據(jù)上一步measure子View所得到的布局大小和布局參數(shù),將子View放到合適的位置上。
OnDraw():繪制視圖,ViewGroup創(chuàng)建一個(gè)Canvas對(duì)象,調(diào)用OnDraw()方法,但OnDraw方法是個(gè)空方法,需要自己根據(jù)OnMeasure和OnLayout獲取得到參數(shù)進(jìn)行自定義繪制。
注意:在Activity的生命周期中沒有辦法獲取View的寬高,原因就是View沒有測量完。
Canvas類:Canvas類位于android.graphics包中,它表示一塊畫布,可以使用該類提供的方法,繪制各種圖形圖像,Canvas對(duì)象的獲取有兩種方式:一種是重寫View.onDraw方法時(shí),在該方法中Canvas對(duì)象會(huì)被當(dāng)作參數(shù)傳遞過來,在該Canvas上繪制的圖形圖像會(huì)一直顯示在View中,另一種是借助位圖創(chuàng)建Canvas,參考代碼:
Bitmap bitmap = Bitmap.CreatBitmap(100,100,Bitmap.Config,ARGB_888); Canvas canvas = new Canvas(bitmap);
Android中頁面的橫屏與豎屏操作:
在配置文件中為Activity設(shè)置屬性 android:screenOrientation = “ string ” ; 其中string為屬性值,landscape(橫屏)或portrait(豎屏);
在代碼中設(shè)置:
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);為防止切換后重新啟動(dòng)當(dāng)前Activity,應(yīng)在配置文件中添加android:configChanges=“keyboardHidden|orientation”屬性,并在Activity中重寫onConfigurationChanged方法。
獲取手機(jī)中屏幕的寬和高的方法:
通過Context的getWindowManager()方法獲得WindowManager對(duì)象,再從WindowManager對(duì)象中通過getDefaultDisplay獲得Display對(duì)象,再從Display對(duì)象中獲得屏幕的寬和高。
Android內(nèi)存泄漏及管理
內(nèi)存溢出(out of memory):是指程序在申請(qǐng)內(nèi)存時(shí),沒有足夠的內(nèi)存空間供其使用,出現(xiàn)out of Memory 的錯(cuò)誤,比如申請(qǐng)了一個(gè)integer,但給它存了long才能存下的數(shù),這就是內(nèi)存溢出,通俗來講,內(nèi)存溢出就是內(nèi)存不夠用。
內(nèi)存泄漏(Memory Leak):是指程序使用new關(guān)鍵字向系統(tǒng)申請(qǐng)內(nèi)存后,無法釋放已經(jīng)申請(qǐng)的內(nèi)存空間,一次內(nèi)存泄漏可以忽略,但多次內(nèi)存泄漏堆積后會(huì)造成很嚴(yán)重的后果,會(huì)占用光內(nèi)存空間。
以發(fā)生的方式來看,內(nèi)存泄漏可以分為以下幾類:
常發(fā)性內(nèi)存泄漏:發(fā)生內(nèi)存泄漏的代碼會(huì)被執(zhí)行多次,每次執(zhí)行都會(huì)導(dǎo)致一塊內(nèi)存泄漏;
偶發(fā)性內(nèi)存泄漏:發(fā)生內(nèi)存泄露的代碼只有在某些特定的環(huán)境或情況下才會(huì)發(fā)生;
一次性內(nèi)存泄漏:發(fā)生內(nèi)存泄漏的代碼只會(huì)被執(zhí)行一次,或是由于算法上的缺陷,導(dǎo)致總會(huì)有一塊且僅有一塊內(nèi)存發(fā)生泄漏;
隱式內(nèi)存泄漏:程序在運(yùn)行過程中不停的分配內(nèi)存,但是直到程序結(jié)束的時(shí)候才會(huì)釋放內(nèi)存;
常見造成內(nèi)存泄漏的原因:
單例模式引起的內(nèi)存泄漏:由于單例的靜態(tài)特性使得單例的生命周期和程序的生命周期一樣長,如果一個(gè)對(duì)象(如Context)已經(jīng)不再使用,而單例對(duì)象還持有對(duì)象的引用就會(huì)造成這個(gè)對(duì)象不能正?;厥?;
Handler引起的內(nèi)存泄漏:子線程執(zhí)行網(wǎng)絡(luò)任務(wù),使用Handler處理子線程發(fā)送的消息,由于Hnadler對(duì)象是非靜態(tài)匿名內(nèi)部類的實(shí)例對(duì)象,持有外部類(Activity)的引用,在Handler Message中,Looper線程不斷輪詢處理消息,當(dāng)Activity退出時(shí)還有未處理或正在處理的消息時(shí),消息隊(duì)列中的消息持有Handler對(duì)象引用,Handler又持有Activity,導(dǎo)致Activity的內(nèi)存和資源不能及時(shí)回收;
線程造成內(nèi)存泄漏:匿名內(nèi)部類Runnable和AsyncTask對(duì)象執(zhí)行異步任務(wù),當(dāng)前Activity隱式引用,當(dāng)前Activity銷毀之前,任務(wù)還沒有執(zhí)行完,將導(dǎo)致Activity的內(nèi)存和資源不能及時(shí)回收;
資源對(duì)象未關(guān)閉造成的內(nèi)存泄漏:對(duì)于使用了BroadcastReceiver、File、Bitmap等資源,應(yīng)該在Activity銷毀之前及時(shí)關(guān)閉或注銷它們,否則這些資源將不會(huì)回收,造成內(nèi)存泄漏;
內(nèi)存泄漏的檢測工具:LeakCanary
LeakCanary是Square公司開源的一個(gè)檢測內(nèi)存泄漏的函數(shù)庫,可以在開發(fā)的項(xiàng)目中集成,在Debug版本中監(jiān)控Activity、Fragment等的內(nèi)存泄漏,LeakCanaty集成到項(xiàng)目之后,在檢測內(nèi)存泄漏時(shí),會(huì)發(fā)送消息到系統(tǒng)通知欄,點(diǎn)擊后會(huì)打開名稱DisplayLeakActivity的頁面,并顯示泄露的跟蹤信息,Logcat上面也會(huì)有對(duì)應(yīng)的日志輸出。
Android設(shè)計(jì)模式之MVC
MVC即Model-View-Controller,M是模型,V是視圖,C是控制器,MVC模式下系統(tǒng)框架的類庫被劃分為模型(Model)、視圖(View)、控制器(Controller),模型對(duì)象負(fù)責(zé)建立數(shù)據(jù)結(jié)構(gòu)和相應(yīng)的行為操作處理,視圖負(fù)責(zé)在屏幕上渲染出相應(yīng)的圖形信息,展示給用戶看,控制器對(duì)象負(fù)責(zé)截獲用戶的按鍵和屏幕觸摸事件,協(xié)調(diào)Model對(duì)象和View對(duì)象。
用戶與視圖交互,視圖接收并反饋用戶的動(dòng)作,視圖把用戶的請(qǐng)求傳給相應(yīng)的控制器,由控制器決定調(diào)用哪個(gè)模型,然后由模型調(diào)用相應(yīng)的業(yè)務(wù)邏輯對(duì)用戶請(qǐng)求進(jìn)行加工處理,如果需要返回?cái)?shù)據(jù),模型會(huì)把相應(yīng)的數(shù)據(jù)返回給控制器,由控制器調(diào)用相應(yīng)的視圖,最終由視圖格式化和渲染返回的數(shù)據(jù),一個(gè)模型可以有多個(gè)視圖,一個(gè)視圖可以有多個(gè)控制器,一個(gè)控制器可以有多個(gè)模型。
Model(模型):Model是一個(gè)應(yīng)用系統(tǒng)的核心部分,代表了該系統(tǒng)實(shí)際要實(shí)現(xiàn)的所有功能處理,比如在視頻播放器中,模型代表了一個(gè)視頻數(shù)據(jù)庫及播放視頻的程序和函數(shù)代碼,Model在values目錄下通過xml文件格式生成,也可以由代碼動(dòng)態(tài)生成,View和Model是通過橋梁Adapter來連接起來的;
View(視圖):View是軟件應(yīng)用傳給用戶的一個(gè)反饋結(jié)果,它代表了軟件應(yīng)用中的圖形展示,聲音播放、觸覺反饋等,視圖的根節(jié)點(diǎn)是應(yīng)用程序的自身窗口,View在Layout目錄中通過xml文件格式生成,用findViewById()獲取,也可以通過代碼動(dòng)態(tài)生成;
Controller(控制器):Controller在軟件應(yīng)用中負(fù)責(zé)對(duì)外部事件的響應(yīng),包括:鍵盤敲擊、屏幕觸摸、電話呼入等,Controller實(shí)現(xiàn)了一個(gè)事件隊(duì)列,每一個(gè)外部事件均在事件隊(duì)列中被唯一標(biāo)識(shí),框架依次將事件從隊(duì)列中移出并派發(fā)出去;
JVM運(yùn)行原理詳解
JVM簡析:說起Java,我們首先想到的是Java編程語言,然而事實(shí)上,Java是一種技術(shù),它由四方面組成:Java編程語言、Java類文件格式、Java虛擬機(jī)和Java應(yīng)用程序接口(Java API)。它們的關(guān)系如下圖所示:
Java平臺(tái)由Java虛擬機(jī)和Java應(yīng)用程序接口搭建,Java語言則是進(jìn)入這個(gè)平臺(tái)的通道,用Java語言編寫并編譯的程序可以運(yùn)行在這個(gè)平臺(tái)上。這個(gè)平臺(tái)的結(jié)構(gòu)如下圖所示: 運(yùn)行期環(huán)境代表著Java平臺(tái),開發(fā)人員編寫Java代碼(.java文件),然后將之編譯成字節(jié)碼(.class文件),再然后字節(jié)碼被裝入內(nèi)存,一旦字節(jié)碼進(jìn)入虛擬機(jī),它就會(huì)被解釋器解釋執(zhí)行,或者是被即時(shí)代碼發(fā)生器有選擇的轉(zhuǎn)換成機(jī)器碼執(zhí)行。
JVM在它的生存周期中有一個(gè)明確的任務(wù),那就是運(yùn)行Java程序,因此當(dāng)Java程序啟動(dòng)的時(shí)候,就產(chǎn)生JVM的一個(gè)實(shí)例;當(dāng)程序運(yùn)行結(jié)束的時(shí)候,該實(shí)例也跟著消失了。在Java平臺(tái)的結(jié)構(gòu)中, 可以看出,Java虛擬機(jī)(JVM) 處在核心的位置,是程序與底層操作系統(tǒng)和硬件無關(guān)的關(guān)鍵。它的下方是移植接口,移植接口由兩部分組成:適配器和Java操作系統(tǒng), 其中依賴于平臺(tái)的部分稱為適配器;JVM 通過移植接口在具體的平臺(tái)和操作系統(tǒng)上實(shí)現(xiàn);在JVM 的上方是Java的基本類庫和擴(kuò)展類庫以及它們的API, 利用Java API編寫的應(yīng)用程序(application) 和小程序(Java applet) 可以在任何Java平臺(tái)上運(yùn)行而無需考慮底層平臺(tái), 就是因?yàn)橛蠮ava虛擬機(jī)(JVM)實(shí)現(xiàn)了程序與操作系統(tǒng)的分離,從而實(shí)現(xiàn)了Java 的平臺(tái)無關(guān)性。
下面我們從JVM的基本概念和運(yùn)過程程這兩個(gè)方面入手來對(duì)它進(jìn)行深入的研究。
2.JVM基本概念
(1)基本概念:JVM是可運(yùn)行Java代碼的假想計(jì)算機(jī) ,包括一套字節(jié)碼指令集、一組寄存器、一個(gè)棧、一個(gè)垃圾回收堆和一個(gè)存儲(chǔ)方法域。JVM是運(yùn)行在操作系統(tǒng)之上的,它與硬件沒有直接的交互。
(2)運(yùn)行過程:我們都知道Java源文件,通過編譯器,能夠生產(chǎn)相應(yīng)的.Class文件,也就是字節(jié)碼文件,而字節(jié)碼文件又通過Java虛擬機(jī)中的解釋器,編譯成特定機(jī)器上的機(jī)器碼 。也就是如下: ① Java源文件—->編譯器—->字節(jié)碼文件;② 字節(jié)碼文件—->JVM—->機(jī)器碼
每一種平臺(tái)的解釋器是不同的,但是實(shí)現(xiàn)的虛擬機(jī)是相同的,這也就是Java為什么能夠跨平臺(tái)的原因了 ,當(dāng)一個(gè)程序從開始運(yùn)行,這時(shí)虛擬機(jī)就開始實(shí)例化了,多個(gè)程序啟動(dòng)就會(huì)存在多個(gè)虛擬機(jī)實(shí)例。程序退出或者關(guān)閉,則虛擬機(jī)實(shí)例消亡,多個(gè)虛擬機(jī)實(shí)例之間數(shù)據(jù)不能共享。
(3)三種JVM:① Sun公司的HotSpot;② BEA公司的JRockit;③ IBM公司的J9JVM;
在JDK1.7及其以前我們所使用的都是Sun公司的HotSpot,但由于Sun公司和BEA公司都被oracle收購,jdk1.8將采用Sun公司的HotSpot和BEA公司的JRockit兩個(gè)JVM中精華形成jdk1.8的JVM。
3.JVM的體系結(jié)構(gòu)
(1)Class Loader類加載器: 負(fù)責(zé)加載 .class文件,class文件在文件開頭有特定的文件標(biāo)示,并且ClassLoader負(fù)責(zé)class文件的加載等,至于它是否可以運(yùn)行,則由Execution Engine決定。①定位和導(dǎo)入二進(jìn)制class文件;② 驗(yàn)證導(dǎo)入類的正確性;③ 為類分配初始化內(nèi)存;④ 幫助解析符號(hào)引用.
(2)Native Interface本地接口:本地接口的作用是融合不同的編程語言為Java所用,它的初衷是融合C/C++程序,Java誕生的時(shí)候C/C++橫行的時(shí)候,要想立足,必須有調(diào)用C/C++程序,于是就在內(nèi)存中專門開辟了一塊區(qū)域處理標(biāo)記為native的代碼,它的具體作法是Native Method Stack中登記native方法,在Execution Engine執(zhí)行時(shí)加載native libraies。
(3)Execution Engine 執(zhí)行引擎:執(zhí)行包在裝載類的方法中的指令,也就是方法。
(4)Runtime data area 運(yùn)行數(shù)據(jù)區(qū):虛擬機(jī)內(nèi)存或者Jvm內(nèi)存,沖整個(gè)計(jì)算機(jī)內(nèi)存中開辟一塊內(nèi)存存儲(chǔ)Jvm需要用到的對(duì)象,變量等,運(yùn)行區(qū)數(shù)據(jù)有分很多小區(qū),分別為:方法區(qū),虛擬機(jī)棧,本地方法棧,堆,程序計(jì)數(shù)器。
4.JVM數(shù)據(jù)運(yùn)行區(qū)詳解(棧管運(yùn)行,堆管存儲(chǔ)):
說明:JVM調(diào)優(yōu)主要就是優(yōu)化 Heap堆 和 Method Area 方法區(qū)。
(1)Native Method Stack本地方法棧:它的具體做法是Native Method Stack中登記native方法,在Execution Engine執(zhí)行時(shí)加載native libraies。
(2)PC Register程序計(jì)數(shù)器:每個(gè)線程都有一個(gè)程序計(jì)算器,就是一個(gè)指針,指向方法區(qū)中的方法字節(jié)碼(下一個(gè)將要執(zhí)行的指令代碼),由執(zhí)行引擎讀取下一條指令,是一個(gè)非常小的內(nèi)存空間,幾乎可以忽略不記。
(3)Method Area方法區(qū):方法區(qū)是被所有線程共享,所有字段和方法字節(jié)碼,以及一些特殊方法如構(gòu)造函數(shù),接口代碼也在此定義。簡單說,所有定義的方法的信息都保存在該區(qū)域,此區(qū)域?qū)儆诠蚕韰^(qū)間。靜態(tài)變量+常量+類信息+運(yùn)行時(shí)常量池存在方法區(qū)中,實(shí)例變量存在堆內(nèi)存中。
(4)Stack 棧: ① 棧是什么,棧也叫棧內(nèi)存,主管Java程序的運(yùn)行,是在線程創(chuàng)建時(shí)創(chuàng)建,它的生命期是跟隨線程的生命期,線程結(jié)束棧內(nèi)存也就釋放,對(duì)于棧來說不存在垃圾回收問題,只要線程一結(jié)束該棧就Over,生命周期和線程一致,是線程私有的。基本類型的變量和對(duì)象的引用變量都是在函數(shù)的棧內(nèi)存中分配。② 棧存儲(chǔ)什么?棧幀中主要保存3類數(shù)據(jù):本地變量(Local Variables):輸入?yún)?shù)和輸出參數(shù)以及方法內(nèi)的變量;棧操作(Operand Stack):記錄出棧、入棧的操作; 棧幀數(shù)據(jù)(Frame Data):包括類文件、方法等等。③棧運(yùn)行原理,棧中的數(shù)據(jù)都是以棧幀(Stack Frame)的格式存在,棧幀是一個(gè)內(nèi)存區(qū)塊,是一個(gè)數(shù)據(jù)集,是一個(gè)有關(guān)方法和運(yùn)行期數(shù)據(jù)的數(shù)據(jù)集,當(dāng)一個(gè)方法A被調(diào)用時(shí)就產(chǎn)生了一個(gè)棧幀F(xiàn)1,并被壓入到棧中,A方法又調(diào)用了B方法,于是產(chǎn)生棧幀F(xiàn)2也被壓入棧,B方法又調(diào)用了C方法,于是產(chǎn)生棧幀F(xiàn)3也被壓入棧……依次執(zhí)行完畢后,先彈出后進(jìn)…F3棧幀,再彈出F2棧幀,再彈出F1棧幀。遵循“先進(jìn)后出”/“后進(jìn)先出”原則。
(5) Heap 堆:堆這塊區(qū)域是JVM中最大的,應(yīng)用的對(duì)象和數(shù)據(jù)都是存在這個(gè)區(qū)域,這塊區(qū)域也是線程共享的,也是 gc 主要的回收區(qū),一個(gè) JVM 實(shí)例只存在一個(gè)堆類存,堆內(nèi)存的大小是可以調(diào)節(jié)的。類加載器讀取了類文件后,需要把類、方法、常變量放到堆內(nèi)存中,以方便執(zhí)行器執(zhí)行,堆內(nèi)存分為三部分:
① 新生區(qū):新生區(qū)是類的誕生、成長、消亡的區(qū)域,一個(gè)類在這里產(chǎn)生,應(yīng)用,最后被垃圾回收器收集,結(jié)束生命。新生區(qū)又分為兩部分:伊甸區(qū)(Eden space)和幸存者區(qū)(Survivor pace),所有的類都是在伊甸區(qū)被new出來的。幸存區(qū)有兩個(gè):0區(qū)(Survivor0 space)和1區(qū)(Survivor 1 space)。當(dāng)伊甸園的空間用完時(shí),程序又需要?jiǎng)?chuàng)建對(duì)象,JVM的垃圾回收器將對(duì)伊甸園進(jìn)行垃圾回收(Minor GC),將伊甸園中的剩余對(duì)象移動(dòng)到幸存0區(qū)。若幸存0區(qū)也滿了,再對(duì)該區(qū)進(jìn)行垃圾回收,然后移動(dòng)到1區(qū)。那如果1去也滿了呢?再移動(dòng)到養(yǎng)老區(qū)。若養(yǎng)老區(qū)也滿了,那么這個(gè)時(shí)候?qū)a(chǎn)生Major GC(FullGCC),進(jìn)行養(yǎng)老區(qū)的內(nèi)存清理。若養(yǎng)老區(qū)執(zhí)行Full GC 之后發(fā)現(xiàn)依然無法進(jìn)行對(duì)象的保存,就會(huì)產(chǎn)生OOM異常“OutOfMemoryError”。如果出現(xiàn)java.lang.OutOfMemoryError: Java heap space異常,說明Java虛擬機(jī)的堆內(nèi)存不夠。原因有二: a.Java虛擬機(jī)的堆內(nèi)存設(shè)置不夠,可以通過參數(shù)-Xms、-Xmx來調(diào)整。b.代碼中創(chuàng)建了大量大對(duì)象,并且長時(shí)間不能被垃圾收集器收集(存在被引用)。
養(yǎng)老區(qū):養(yǎng)老區(qū)用于保存從新生區(qū)篩選出來的 JAVA 對(duì)象,一般池對(duì)象都在這個(gè)區(qū)域活躍。
③ 永久區(qū):永久存儲(chǔ)區(qū)是一個(gè)常駐內(nèi)存區(qū)域,用于存放JDK自身所攜帶的 Class,Interface 的元數(shù)據(jù),也就是說它存儲(chǔ)的是運(yùn)行環(huán)境必須的類信息,被裝載進(jìn)此區(qū)域的數(shù)據(jù)是不會(huì)被垃圾回收器回收掉的,關(guān)閉 JVM 才會(huì)釋放此區(qū)域所占用的內(nèi)存。 如果出現(xiàn)java.lang.OutOfMemoryError:PermGen space,說明是Java虛擬機(jī)對(duì)永久代Perm內(nèi)存設(shè)置不夠。原因有二:
a.程序啟動(dòng)需要加載大量的第三方j(luò)ar包。例如:在一個(gè)Tomcat下部署了太多的應(yīng)用。
b.大量動(dòng)態(tài)反射生成的類不斷被加載,最終導(dǎo)致Perm區(qū)被占滿。
方法區(qū)和堆內(nèi)存的異議: 實(shí)際而言,方法區(qū)和堆一樣,是各個(gè)線程共享的內(nèi)存區(qū)域,它用于存儲(chǔ)虛擬機(jī)加載的:類信息+普通常量+靜態(tài)常量+編譯器編譯后的代碼等等,雖然JVM規(guī)范將方法區(qū)描述為堆的一個(gè)邏輯部分,但它卻還有一個(gè)別名叫做Non-Heap(非堆),目的就是要和堆分開。
對(duì)于HotSpot虛擬機(jī),很多開發(fā)者習(xí)慣將方法區(qū)稱之為“永久代(Parmanent Gen)”,但嚴(yán)格本質(zhì)上說兩者不同,或者說使用永久代來實(shí)現(xiàn)方法區(qū)而已,永久代是方法區(qū)的一個(gè)實(shí)現(xiàn),jdk1.7的版本中,已經(jīng)將原本放在永久代的字符串常量池移走。
常量池(Constant Pool)是方法區(qū)的一部分,Class文件除了有類的版本、字段、方法、接口等描述信息外,還有一項(xiàng)信息就是常量池,這部分內(nèi)容將在類加載后進(jìn)入方法區(qū)的運(yùn)行時(shí)常量池中存放。
Android平臺(tái)的虛擬機(jī)Dalvik
Dalvik概述:Dalvik是Google公司自己設(shè)計(jì)用于Android平臺(tái)的Java虛擬機(jī)。它可以支持已轉(zhuǎn)換為.dex(即Dalvik Executable)格式的Java應(yīng)用程序的運(yùn)行,.dex格式是專為Dalvik設(shè)計(jì)的一種壓縮格式,可以減少整體文件尺寸,提高I/o操作的類查找速度所以適合內(nèi)存和處理器速度有限的系統(tǒng)。
Dalvik虛擬機(jī)(DVM)和Java 虛擬機(jī)(JVM)首要差別:Dalvik 基于寄存器,而JVM 基于棧。性能有很大的提升?;诩拇嫫鞯奶摂M機(jī)對(duì)于更大的程序來說,在它們編譯的時(shí)候,花費(fèi)的時(shí)間更短。
寄存器的概念:寄存器是中央處理器內(nèi)的組成部分。寄存器是有限存貯容量的高速存貯部件,它們可用來暫存指令、數(shù)據(jù)和位址。在中央處理器的控制部件中,包含的寄存器有指令寄存器(IR)和程序計(jì)數(shù)器(PC),在中央處理器的算術(shù)及邏輯部件中,包含的寄存器有累加器(ACC)。
棧的概念:棧是線程獨(dú)有的,保存其運(yùn)行狀態(tài)和局部自動(dòng)變量的(所以多線程中局部變量都是相互獨(dú)立的,不同于類變量)。棧在線程開始的時(shí)候初始化(線程的Start方法,初始化分配棧),每個(gè)線程的?;ハ嗒?dú)立。每個(gè)函數(shù)都有自己的棧,棧被用來在函數(shù)之間傳遞參數(shù)。操作系統(tǒng)在切換線程的時(shí)候會(huì)自動(dòng)的切換棧,就是切換SS/ESP寄存器。??臻g不需要在高級(jí)語言里面顯式的分配和釋放。
DVM進(jìn)程的設(shè)計(jì)規(guī)則:
每個(gè)應(yīng)用程序都運(yùn)行在它自己的Linux空間。在需要執(zhí)行該應(yīng)用程序時(shí)Android將啟動(dòng)該進(jìn)程,當(dāng)不再需要該應(yīng)用程序,并且系統(tǒng)資源分配不夠時(shí),則系統(tǒng)終止該進(jìn)程。
每個(gè)應(yīng)用程序都有自己的(DVM),所以任一應(yīng)用程序的代碼與其他應(yīng)用程序的代碼是相互隔離的。
默認(rèn)情況下,每個(gè)應(yīng)用程序都給分配一個(gè)唯一的Linux用戶ID。所以應(yīng)用程序的文件只能對(duì)該應(yīng)用程序可見。
所以說每個(gè)應(yīng)用程序都擁有一個(gè)獨(dú)立的DVM,而每個(gè)DVM在Linux中又是一個(gè)進(jìn)程,所以說DVM進(jìn)程和Linux進(jìn)程可以說是一個(gè)概念。
Android 應(yīng)用程序的編譯:Android所有類都通過JAVA編譯器編譯,然后通過Android SDK的“dex文件轉(zhuǎn)換工具”轉(zhuǎn)換為“dex”的字節(jié)文件,再由DVM載入執(zhí)行。
Android ART模式簡介:Android4.4引入ART模式來代替Dalvik虛擬機(jī)。ART是AndroidRuntime的縮寫,它提供了以AOT(Ahead-Of-Time)的方式運(yùn)行Android應(yīng)用程序的機(jī)制。所謂AOT是指在運(yùn)行前就把中間代碼靜態(tài)編譯成本地代碼,這就節(jié)省了JIT運(yùn)行時(shí)的轉(zhuǎn)換時(shí)間。因此,和采用JIT的Dalvik相比,ART模式在總體性能有了很大的提升,應(yīng)用程序不但運(yùn)行效率更高,耗電量更低,而且占用的內(nèi)存也更少;ART和dalvik相比,系統(tǒng)的性能得到了顯著提升,同時(shí)占用的內(nèi)存更少,因此能支持配置更低的設(shè)備。但是ART模式下編譯出來的文件會(huì)比以前增大10%-20%,系統(tǒng)需要更多的存儲(chǔ)空間,同時(shí)因?yàn)樵诎惭b時(shí)要執(zhí)行編譯,應(yīng)用的安裝時(shí)間也比以前更長了;ART和dalvik相比,系統(tǒng)的性能得到了顯著提升,同時(shí)占用的內(nèi)存更少,因此能支持配置更低的設(shè)備。但是ART模式下編譯出來的文件會(huì)比以前增大10%-20%,系統(tǒng)需要更多的存儲(chǔ)空間,同時(shí)因?yàn)樵诎惭b時(shí)要執(zhí)行編譯,應(yīng)用的安裝時(shí)間也比以前更長了。
Java的內(nèi)存分配
Java內(nèi)存分配主要包括以下幾個(gè)區(qū)域: 1. 寄存器:我們?cè)诔绦蛑袩o法控制;2. 棧:存放基本類型的數(shù)據(jù)和對(duì)象的引用,但對(duì)象本身不存放在棧中,而是存放在堆中;3. 堆:存放用new產(chǎn)生的數(shù)據(jù);4. 靜態(tài)域:存放在對(duì)象中用static定義的靜態(tài)成員;5. 常量池:存放常量;6. 非RAM(隨機(jī)存取存儲(chǔ)器)存儲(chǔ):硬盤等永久存儲(chǔ)空間。
Java內(nèi)存分配中的棧:在函數(shù)中定義的一些基本類型的變量數(shù)據(jù)和對(duì)象的引用變量都在函數(shù)的棧內(nèi)存中分配。當(dāng)在一段代碼塊定義一個(gè)變量時(shí),Java就在棧中為這個(gè)變量分配內(nèi)存空間,當(dāng)該變量退出該作用域后,Java會(huì)自動(dòng)釋放掉為該變量所分配的內(nèi)存空間,該內(nèi)存空間可以立即被另作他用。
Java內(nèi)存分配中的堆:堆內(nèi)存用來存放由new創(chuàng)建的對(duì)象和數(shù)組。在堆中分配的內(nèi)存,由Java虛擬機(jī)的自動(dòng)垃圾回收器來管理。在堆中產(chǎn)生了一個(gè)數(shù)組或?qū)ο蠛?,還可以 在棧中定義一個(gè)特殊的變量,讓棧中這個(gè)變量的取值等于數(shù)組或?qū)ο笤诙褍?nèi)存中的首地址,棧中的這個(gè)變量就成了數(shù)組或?qū)ο蟮囊米兞?。引用變量就相?dāng)于是為數(shù)組或?qū)ο笃鸬囊粋€(gè)名稱,以后就可以在程序中使用棧中的引用變量來訪問堆中的數(shù)組或?qū)ο?。引用變量就相?dāng)于是為數(shù)組或者對(duì)象起的一個(gè)名稱。引用變量是普通的變量,定義時(shí)在棧中分配,引用變量在程序運(yùn)行到其作用域之外后被釋放。而數(shù)組和對(duì)象本身在堆中分配,即使程序運(yùn)行到使用 new 產(chǎn)生數(shù)組或者對(duì)象的語句所在的代碼塊之外,數(shù)組和對(duì)象本身占據(jù)的內(nèi)存不會(huì)被釋放,數(shù)組和對(duì)象在沒有引用變量指向它的時(shí)候,才變?yōu)槔?,不能在被使用,但仍然占?jù)內(nèi)存空間不放,在隨后的一個(gè)不確定的時(shí)間被垃圾回收器收走(釋放掉)。這也是 Java 比較占內(nèi)存的原因。實(shí)際上,棧中的變量指向堆內(nèi)存中的變量,這就是Java中的指針!
Java內(nèi)存分配中的常量池 (constant pool):常量池指的是在編譯期被確定,并被保存在已編譯的.class文件中的一些數(shù)據(jù)。除了包含代碼中所定義的各種基本類型(如int、long等等)和對(duì)象型(如String及數(shù)組)的常量值(final)還包含一些以文本形式出現(xiàn)的符號(hào)引用,比如: 1.類和接口的全限定名;2.字段的名稱和描述符; 3.方法和名稱和描述符。虛擬機(jī)必須為每個(gè)被裝載的類型維護(hù)一個(gè)常量池。常量池就是該類型所用到常量的一個(gè)有序集和,包括直接常量(string,integer和 floating point常量)和對(duì)其他類型,字段和方法的符號(hào)引用。對(duì)于String常量,它的值是在常量池中的。而JVM中的常量池在內(nèi)存當(dāng)中是以表的形式存在的,對(duì)于String類型,有一張固定長度的CONSTANT_String_info表用來存儲(chǔ)文字字符串值,注意:該表只存儲(chǔ)文字字符串值,不存儲(chǔ)符號(hào)引用。說到這里,對(duì)常量池中的字符串值的存儲(chǔ)位置應(yīng)該有一個(gè)比較明了的理解了。在程序執(zhí)行的時(shí)候,常量池會(huì)儲(chǔ)存在Method Area,而不是堆中。
堆與棧:Java的堆是一個(gè)運(yùn)行時(shí)數(shù)據(jù)區(qū),類的(對(duì)象從中分配空間。這些對(duì)象通過new、newarray、 anewarray和multianewarray等指令建立,它們不需要程序代碼來顯式的釋放。堆是由垃圾回收來負(fù)責(zé)的,堆的優(yōu)勢是可以動(dòng)態(tài)地分配內(nèi)存大小,生存期也不必事先告訴編譯器,因?yàn)樗窃谶\(yùn)行時(shí)動(dòng)態(tài)分配內(nèi)存的,Java的垃圾收集器會(huì)自動(dòng)收走這些不再使用的數(shù)據(jù)。但缺點(diǎn)是,由于要在運(yùn)行時(shí)動(dòng)態(tài)分配內(nèi)存,存取速度較慢。 棧的優(yōu)勢是,存取速度比堆要快,僅次于寄存器,棧數(shù)據(jù)可以共享。但缺點(diǎn)是,存在棧中的數(shù)據(jù)大小與生存期必須是確定的,缺乏靈活性。棧中主要存放一些基本類型的變量數(shù)據(jù)(int, short, long, byte, float, double, boolean, char)和對(duì)象句柄(引用)。棧有一個(gè)很重要的特殊性,就是存在棧中的數(shù)據(jù)可以共享
Android中的Binder機(jī)制
Binder是Android系統(tǒng)進(jìn)程間通信(IPC)方式之一。Linux已經(jīng)擁有的進(jìn)程間通信IPC手段包括(Internet Process Connection):管道(Pipe)、信號(hào)(Signal)和跟蹤(Trace)、插口(Socket)、報(bào)文隊(duì)列(Message)、共享內(nèi)存(Share Memory)和信號(hào)量(Semaphore)。Binder可以提供系統(tǒng)中任何程序都可以訪問的全局服務(wù)。Android的Binder的框架如下:
上圖中涉及到Binder模型的4類角色:Binder驅(qū)動(dòng),ServiceManager,Server和Client。 因?yàn)楹竺嬲鹿?jié)講解Binder時(shí),都是以MediaPlayerService和MediaPlayer為代表進(jìn)行講解的;這里就使用MediaPlayerService代表了Server,而MediaPlayer則代表了Client。
Binder機(jī)制的目的是實(shí)現(xiàn)IPC(Inter-ProcessCommunication),即實(shí)現(xiàn)進(jìn)程間通信。在上圖中,由于MediaPlayerService是Server的代表,而MediaPlayer是Client的代表;因此,對(duì)于上圖而言,Binder機(jī)制則表現(xiàn)為"實(shí)現(xiàn)MediaPlayerService和MediaPlayer之間的通信"。
Android中的緩存機(jī)制
移動(dòng)開發(fā)本質(zhì)上就是手機(jī)和服務(wù)器之間進(jìn)行通信,需要從服務(wù)端獲取數(shù)據(jù)。反復(fù)通過網(wǎng)絡(luò)獲取數(shù)據(jù)是比較耗時(shí)的,特別是訪問比較多的時(shí)候,會(huì)極大影響了性能,Android中可通過緩存機(jī)制來減少頻繁的網(wǎng)絡(luò)操作,減少流量、提升性能。
實(shí)現(xiàn)原理:把不需要實(shí)時(shí)更新的數(shù)據(jù)緩存下來,通過時(shí)間或者其他因素 來判別是讀緩存還是網(wǎng)絡(luò)請(qǐng)求,這樣可以緩解服務(wù)器壓力,一定程度上提高應(yīng)用響應(yīng)速度,并且支持離線閱讀。
Bitmap的緩存:在許多的情況下(像 ListView, GridView 或 ViewPager 之類的組件 )我們需要一次性加載大量的圖片,在屏幕上顯示的圖片和所有待顯示的圖片有可能需要馬上就在屏幕上無限制的進(jìn)行滾動(dòng)、切換。像ListView, GridView 這類組件,它們的子項(xiàng)當(dāng)不可見時(shí),所占用的內(nèi)存會(huì)被回收以供正在前臺(tái)顯示子項(xiàng)使用。垃圾回收器也會(huì)釋放你已經(jīng)加載了的圖片占用的內(nèi)存。如果你想讓你的UI運(yùn)行流暢的話,就不應(yīng)該每次顯示時(shí)都去重新加載圖片。保持一些內(nèi)存和文件緩存就變得很有必要了。
使用內(nèi)存緩存:通過預(yù)先消耗應(yīng)用的一點(diǎn)內(nèi)存來存儲(chǔ)數(shù)據(jù),便可快速的為應(yīng)用中的組件提供數(shù)據(jù),是一種典型的以空間換時(shí)間的策略。LruCache 類(Android v4 Support Library 類庫中開始提供)非常適合來做圖片緩存任務(wù),它可以使用一個(gè)LinkedHashMap 的強(qiáng)引用來保存最近使用的對(duì)象,并且當(dāng)它保存的對(duì)象占用的內(nèi)存總和超出了為它設(shè)計(jì)的最大內(nèi)存時(shí)會(huì)把不經(jīng)常使用的對(duì)象成員踢出以供垃圾回收器回收。給LruCache 設(shè)置一個(gè)合適的內(nèi)存大小,需考慮如下因素:
還剩余多少內(nèi)存給你的activity或應(yīng)用使用屏幕上需要一次性顯示多少張圖片和多少圖片在等待顯示 手機(jī)的大小和密度是多少(密度越高的設(shè)備需要越大的 緩存) 圖片的尺寸(決定了所占用的內(nèi)存大?。?圖片的訪問頻率(頻率高的在內(nèi)存中一直保存)保存圖片的質(zhì)量(不同像素的在不同情況下顯示);
使用磁盤緩存:內(nèi)存緩存能夠快速的獲取到最近顯示的圖片,但不一定就能夠獲取到。當(dāng)數(shù)據(jù)集過大時(shí)很容易把內(nèi)存緩存填滿(如GridView )。你的應(yīng)用也有可能被其它的任務(wù)(比如來電)中斷進(jìn)入到后臺(tái),后臺(tái)應(yīng)用有可能會(huì)被殺死,那么相應(yīng)的內(nèi)存緩存對(duì)象也會(huì)被銷毀。當(dāng)你的應(yīng)用重新回到前臺(tái)顯示時(shí),你的應(yīng)用又需要一張一張的去加載圖片了。磁盤文件緩存能夠用來處理這些情況,保存處理好的圖片,當(dāng)內(nèi)存緩存不可用的時(shí)候,直接讀取在硬盤中保存好的圖片,這樣可以有效的減少圖片加載的次數(shù)。讀取磁盤文件要比直接從內(nèi)存緩存中讀取要慢一些,而且需要在一個(gè)UI主線程外的線程中進(jìn)行,因?yàn)榇疟P的讀取速度是不能夠保證的,磁盤文件緩存顯然也是一種以空間換時(shí)間的策略。
使用SQLite進(jìn)行緩存:網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)完成后,把文件的相關(guān)信息(如url(一般作為唯一標(biāo)示),下載時(shí)間,過期時(shí)間)等存放到數(shù)據(jù)庫。下次加載的時(shí)候根據(jù)url先從數(shù)據(jù)庫中查詢,如果查詢到并且時(shí)間未過期,就根據(jù)路徑讀取本地文件,從而實(shí)現(xiàn)緩存的效果。
文件緩存:思路和一般緩存一樣,把需要的數(shù)據(jù)存儲(chǔ)在文件中,下次加載時(shí)判斷文件是否存在和過期(使用File.lastModified()方法得到文件的最后修改時(shí)間,與當(dāng)前時(shí)間判斷),存在并未過期就加載文件中的數(shù)據(jù),否則請(qǐng)求服務(wù)器重新下載。
Android 中圖片的三級(jí)緩存策略
三級(jí)緩存: ?內(nèi)存緩存,優(yōu)先加載,速度最快; ?本地緩存,次優(yōu)先加載,速度快;?網(wǎng)絡(luò)緩存,最后加載,速度慢,浪費(fèi)流量 ;
三級(jí)緩存策略,最實(shí)在的意義就是 減少不必要的流量消耗,增加加載速度。
三級(jí)緩存的原理:
- 首次加載的時(shí)候通過網(wǎng)絡(luò)加載,獲取圖片,然后保存到內(nèi)存和 SD 卡中。
- 之后運(yùn)行 APP 時(shí),優(yōu)先訪問內(nèi)存中的圖片緩存。
- 如果內(nèi)存沒有,則加載本地 SD 卡中的圖片。
具體的緩存策略可以是這樣的:內(nèi)存作為一級(jí)緩存,本地作為二級(jí)緩存,網(wǎng)絡(luò)加載為最后。其中,內(nèi)存使用 LruCache ,其內(nèi)部通過 LinkedhashMap 來持有外界緩存對(duì)象的強(qiáng)引用;對(duì)于本地緩存,使用 DiskLruCache。加載圖片的時(shí)候,首先使用 LRU 方式進(jìn)行尋找,找不到指定內(nèi)容,按照三級(jí)緩存的方式,進(jìn)行本地搜索,還沒有就網(wǎng)絡(luò)加載。
相關(guān)文章
- 這篇文章主要介紹了華為Android三面成功通過,面試官都問了什么,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-26
- 這篇文章主要介紹了Android 一線大廠面試總結(jié),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-01-08
2019年Android高級(jí)面試題與相關(guān)知識(shí)點(diǎn)總結(jié)
這篇文章主要介紹了2019年Android高級(jí)面試題與相關(guān)知識(shí)點(diǎn),總結(jié)分析了Android開發(fā)中涉及的各種常見知識(shí)點(diǎn)與相關(guān)原理,需要的朋友可以參考下2019-11-21- 這篇文章主要介紹了2019 Android 面試真題集錦,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)2019-08-28
2019 金三銀四:阿里P9架構(gòu)的Android大廠面試題總結(jié)
面試是一道坎,很多人會(huì)恐懼面試,即使是工作很多年的老鳥,可能仍存在面試的焦慮。 今天介紹了阿里P9架構(gòu)的Android大廠面試題總結(jié)的相關(guān)資料,小編覺得挺不錯(cuò)的,現(xiàn)在分2019-05-05這20道題,聽說只有大廠的Android工程師能全對(duì)
這篇文章主要介紹了這20道題,聽說只有大廠的Android工程師能全對(duì),趕快一起來測試一下自己把,看看是不是都掌握了2020-05-25