##1、java中==和#equals和hashCode的差異
類別的==比較的記憶體的位址,即是否為同一個對象,在不覆寫equals的情況下,同比較記憶體位址,原實作也為== ,如String等重寫了equals方法.
hashCode也是Object類別的一個方法。傳回一個離散的int型整數。在集合類別運算中使用,為了提高查詢速度。 (HashMap,HashSet等比較是否為同一個)
2、int#與integer##的差異 int 基本類型
integer 物件int的封裝類別
、#String 、StringBuffer、StringBuilder#區別 String:字符串常量不適用於經常要改變值得情況,每次改變相當於產生一個新的物件
StringBuffer:字串變數(執行緒安全)StringBuilder:字串變數(執行緒不安全) 確保單執行緒下可用,效率略高於StringBuffer
、什麼是內部類別?內部類別的作用 內部類別可直接存取外部類別的屬性
Java中內部類別主要分為成員內部類別
、局部內部類別(嵌套在方法與作用域內)、匿名內部類別(沒建構方法)、靜態內部類別(static修飾的類別,不能使用任何外圍類別的非static成員變數和方法, 不依賴外圍類別)
、進程和執行緒的區別
6、final,finally,##finalize的區別
final:修飾類別、成員變數和成員方法,類別不可繼承,成員變數不可變,成員方法不可重寫finally:與try...catch...共同使用,確保無論是否出現異常都能被呼叫到
finalize:類別的方法,垃圾回收之前會呼叫此方法,子類別可以重寫finalize()方法實現資源的回收
7、#Serializable 與Parcelable 的差異
Serializable Java 序列化介面在硬碟上讀寫讀寫過程中有大量臨時變數的生成,內部執行大量的i/o操作,效率很低。Parcelable Android 序列化介面效率高使用麻煩在記憶體中讀寫(AS有相關外掛程式一鍵產生所需方法) ,物件無法儲存到磁碟中
8、靜態屬性和靜態方法是否可以被繼承?是否可以被重寫?以及原因?
可繼承 不可重寫 而是被隱藏如果子類別裡面定義了靜態方法和屬性,那麼這時候父類別的靜態方法或屬性稱之為"隱藏"。如果你想要呼叫父類別的靜態方法和屬性,直接透過父類別名稱.方法或變數名稱完成。
9、成員內部類別、靜態內部類別、局部內部類別和匿名內部類別的理解,以及專案中的應用
ava中內部類別主要分為成員內部類別、局部內部類別(嵌套在方法與作用域內)、匿名內部類別(沒建構方法)、 靜態內部類別(static修飾的類,不能使用任何外圍類別的非static成員變數和方法, 不依賴外圍類別)
使用內部類別最吸引人的原因是:每個內部類別都能獨立地繼承一個(介面的)實現,所以無論外圍類別是否已經繼承了某個(介面的)實現,對於內部類別都沒有影響。因為Java不支援多重繼承,支援實作多個介面。但有時候會存在一些使用介面很難解決的問題,這個時候我們可以利用內部類別提供的、可以繼承多個具體的或是抽象的類別的能力來解決這些程式設計問題。可以這樣說,介面只是解決了部分問題,而內部類別使得多重繼承的解決方案變得更加完整。
10、string 轉換成 integer的方式與原則
String integer Intrger.parseInt(string);Integerstring Integer.toString();#11、哪些情況下的物件會被垃圾回收機制處理掉?
1.所有實例都沒有活動執行緒存取。 2.沒有被其他任何實例存取的循環引用實例。 3.Java 中有不同的引用型別。判斷實例是否符合垃圾收集的條件都依賴它的參考類型。 要判斷怎樣的物件是沒用的物件。這裡有2種方法:1.採用標記計數的方法:給記憶體中的物件給打上標記,物件被引用一次,計數就加1,引用被釋放了,數就減一,當這個數為0的時候,這個物件就可以被回收了。當然,這也引發了一個問題:循環引用的物件是無法被辨識出來並且被回收的。所以就有了第二種方法:2.採用根搜尋演算法:從一個根出發,搜尋所有的可達對象,這樣剩下的那些對象就是需要被回收的12、靜態代理程式和動態代理程式的區別,什麼場景使用?
靜態代理程式類別: 由程式設計師建立或由特定工具自動產生原始程式碼,然後再編譯。在程式運行前,代理類別的.class檔案就已經存在了。動態代理類別:在程式運作時,運用反射機制動態創建。14、Java#中實作多態的機制是什麼?
答:方法的重寫Overriding與重載Overloading是Java多態性的不同表現重寫Overriding是父類別與子類別之間多態性的一種表現重載Overloading是一個類別中多型性的一種表現.16、說說你對
################################################## Java######反射的理解### ####JAVA反射機制是在運行狀態中, 對於任意一個類別, 都能夠知道這個類別的所有屬性和方法; 對於任意一個物件, 都能夠呼叫它的任意一個方法和屬性。從物件出發,透過反射(Class類別)可以取得取得類別的完整資訊(類別名稱Class類型,所在套件、具有的所有方法Method[]類型、某個方法的完整資訊(包括修飾符、傳回值類型、異常、參數類型)、所有屬性Field[]、某個屬性的完整資訊、建構器Constructors),呼叫類別的屬性或方法自己的總結: 在運行過程中獲得類別、物件、方法的所有資訊。
17、說說你對Java註解的理解
#元註解元註解的功能就是負責註解其他註解。 java5.0的時候,定義了4個標準的meta-annotation類型,它們用來提供對其他註解的類型作說明。
1.@Target2.@Retention3.@Documented4.@Inherited 18 、
Java
中
String
#的了解
# #在原始碼中string是用final 進行修飾,它是不可更改,不可繼承的常數。 19、String#為什麼要設計成不可變的? 1、字串池的需求字串池是方法區(Method Area)中的一塊特殊的儲存區域。當一個字串已經被建立並且該字串在 池 中,該字串的參考會立即傳回給變量,而不是重新建立一個字串再將引用傳回給變數。如果字串不是不可變的,那麼改變一個引用(如: string2)的字串將會導致另一個引用(如: string1)出現髒資料。
2、允許字串快取哈希碼在java中常常會用到字串的雜湊碼,例如: HashMap 。 String的不變性保證雜湊碼始終一,因此,他可以不用擔心變化的出現。這種方法意味著不必每次使用時都重新計算一次雜湊碼——這樣,效率會高很多。 3、安全性String廣泛的用於java 類別中的參數,如:網路連線(Network connetion),開啟檔案(opening files )等等。如果String不是不可變的,網路連線、檔案將會改變—這將會導致一系列的安全威脅。操作的方法以為連接了一台機器,但實際上卻不是。由於反射中的參數都是字串,同樣,也會造成一系列的安全問題。20、Object#類別的#equal#、
hashCode
方法重寫,為什麼?
首先equals與hashcode間的關係是這樣的:1、如果兩個物件相同(即用equals比較傳回true),那麼它們的hashCode值一定要相同;2、如果兩個物件的hashCode相同,它們並不一定相同(即用equals比較返回false) 由於為了提高程式的效率才實現了hashcode方法,先進行hashcode的比較,如果不同,那沒就不必在進行equals的比較了,這樣就大大減少了equals比較的次數,這對比需要比較的數量很大的效率提高是很明顯的21
###、######List,Set,Map######的差異###### #########Set是最簡單的一種集合。集合中的物件不按特定的方式排序,並且沒有重複物件。 Set介面主要實作了兩個實作類別:HashSet: HashSet類別依照雜湊演算法來存取集合中的對象,存取速度比較快 ######TreeSet :TreeSet類別實作了SortedSet接口,能夠對集合中的物件進行排序。 ######List的特徵是其元素以線性方式存儲,集合中可以存放重複物件。 ######ArrayList() : 代表長度可以改變得數組。可以對元素進行隨機的訪問,向ArrayList()中插入與刪除元素的速度慢。 ######LinkedList(): 在實作中採用鍊錶資料結構。插入和刪除速度快,存取速度慢。 ######Map 是一種把鍵物件和值物件映射的集合,它的每個元素都包含一對鍵物件和值物件。 Map沒有繼承於Collection介面 從Map集合中檢索元素時,只要給出鍵對象,就會傳回對應的值對象。 ######HashMap:Map基於散列表的實作。插入和查詢「鍵值對」的開銷是固定的。可以透過構造器設定容量capacity和負載因子load factor,以調整容器的效能。 ######LinkedHashMap: 類似HashMap,但是迭代遍歷它時,取得「鍵值對」的順序是其插入次序,或是最近最少使用(LRU)的次序。只比HashMap慢一點。而在迭代存取時發而更快,因為它使用鍊錶來維護內部次序。 ###TreeMap : 基於紅黑樹資料結構的實作。當查看「鍵」或「鍵值對」時,它們會被排序(順序由Comparabel或Comparator決定)。 TreeMap的特徵在 於,你得到的結果是經過排序的。 TreeMap是唯一的帶有subMap()方法的Map,它可以傳回一個子樹。
WeakHashMao :弱鍵(weak key)Map,Map中使用的物件也被允許釋放: 這是為解決特殊問題而設計的。如果沒有map之外的引用指向某個“鍵”,則此“鍵”可以被垃圾收集器回收。
26、ArrayMap和HashMap的對比
## 1、儲存方式不同
HashMap內部有一個HashMapEntry
2、加入資料時擴容時的處理不一樣,進行了new操作,重新建立對象,開銷很大。 ArrayMap用的是copy數據,所以效率相對要高。
3、ArrayMap提供了數組收縮的功能,在clear或remove後,會重新收縮數組,是否空間4、ArrayMap採用二分法查找; 29、HashMap與HashTable
#
##1 HashMap不是執行緒安全的,效率高一點、方法不是Synchronize的要提供外同步,有containsvalue和containsKey方法。 hashtable是,執行緒安全,不允許有null的鍵和值,效率稍低,方法是是Synchronize的。有contains方法方法。 Hashtable 繼承於Dictionary 類別 30、HashMap與HashSet
的區別
hashMap:HashMap實作了Map介面,HashMap儲存鍵值對,使用put()方法將元素放入map中,HashMap中使用鍵物件來計算hashcode值,HashMap比較快,因為是使用唯一的鍵來取得物件。 HashSet實作了Set接口,HashSet只儲存對象,使用add()方法將元素放入set中,HashSet使用成員物件來計算hashcode值,對於兩個物件來說hashcode可能相同,所以equals ()方法用來判斷物件的相等性,如果兩個物件不同的話,那麼回傳false。 HashSet較HashMap來說比較慢。
31、HashSet
與
HashMap
怎麼判斷集合元素重複? HashSet不能加入重複的元素,當呼叫add(Object)方法時候,
首先會呼叫Object的hashCode方法判hashCode是否已經存在,如不存在則直接插入元素;如果已存在則呼叫Object物件的equals方法判斷是否回傳true,如果為true則表示元素已經存在,如為false則插入元素。 ###33######、######ArrayList######與######LinkedList######的區別,以及應用場景###### #########ArrayList是基於陣列實作的,ArrayList執行緒不安全。 ######LinkedList是基於雙鍊錶實現的:######使用場景:#######(1)如果應用程式對各個索引位置的元素進行大量的存取或刪除操作, ArrayList物件遠優於LinkedList物件;######( 2 ) 如果應用程式主要是對清單進行循環,且循環時候進行插入或刪除操作,LinkedList物件遠優於ArrayList物件;#### ## ###34######、陣列和鍊錶的差異##########陣列:是將元素在記憶體中連續儲存的;它的優點:因為資料是連續儲存的,記憶體位址連續,所以在查找資料的時候效率比較高;它的缺點:在儲存之前,我們需要申請一塊連續的記憶體空間,並且在編譯的時候就必須確定好它的空間的大小。在運行的時候空間的大小是無法隨著你的需要進行增加和減少而改變的,當數據兩比較大的時候,有可能會出現越界的情況,數據比較小的時候,又有可能會浪費掉內存空間。改變資料個數時,增加、插入、刪除資料效率比較低。 ######鍊錶:是動態申請記憶體空間,不需要像陣列需要提前申請好記憶體的大小,鍊錶只需在用的時候申請就可以,根據需要來動態申請或刪除記憶體空間,對於資料增加和刪除以及插入比數組靈活。還有就是鍊錶中資料在記憶體中可以在任意的位置,透過應用來關聯資料(就是透過存在元素的指標來聯繫)###### ###35######、開啟線程的三種方式? ### ######ava有三種建立執行緒的方式,分別是繼承Thread類別、實作Runable介面和使用執行緒池 ####36、執行緒與進程的差別?
執行緒是行程的子集,一個行程可以有很多執行緒,每個執行緒並行執行不同的任務。不同的行程使用不同的記憶體空間,而所有的執行緒共享一片相同的記憶體空間。別把它和棧記憶體搞混,每個執行緒都有單獨的棧記憶體用來儲存本地資料。
38、run()和start()方法區別
這個問題常被問到,但還是能從此區分出面試者對Java執行緒模型的理解程度。 start()方法被用來啟動新建立的線程,而且start()內部呼叫了run()方法,這和直接呼叫run()方法的效果不一樣。當你調用run()方法的時候,只會在原來的線程中調用,沒有新的線程啟動,start()方法才會啟動新線程。
39、如何控制某個方法允許並發存取執行緒的個數?
semaphore.acquire() 請求一個信號量,這時候的信號量個數-1(一旦沒有可使用的信號量,也即信號量個數變為負數時,再次請求的時候就會阻塞,直到其他線程釋放了信號量)
semaphore.release() 釋放一個信號量,此時信號量個數1
40 、在Java中wait#、seelp#方法的不同;
Java程式中wait 和sleep都會造成某種形式的暫停,它們可以滿足不同的需求。 wait()方法用於執行緒間通信,如果等待條件為真且其它執行緒被喚醒時它會釋放鎖,而sleep()方法僅釋放CPU資源或讓目前執行緒停止執行一段時間,但不會釋放鎖定。
41、談wait/notify關鍵字的理解
等待物件的同步鎖定,需要取得該物件的同步鎖定才可以呼叫這個方法,否則編譯可以通過,但執行時會收到一個例外:IllegalMonitorStateException。
呼叫任意物件的 wait() 方法導致該執行緒阻塞,該執行緒不可繼續執行,並且該物件上的鎖被釋放。
喚醒在等待該物件同步鎖定的執行緒(只喚醒一個,如果有多個在等待),注意的是在呼叫此方法的時候,並不能確切的喚醒某一個等待狀態的線程,而是由JVM決定要喚醒哪個線程,而且不是按優先順序。
呼叫任意物件的notify()方法則導致因呼叫該物件的 wait()方法而阻塞的執行緒中隨機選擇的一個解除阻塞(但要等到獲得鎖後才真正可執行)。
42、什麼導致執行緒阻塞?執行緒如何關閉?
阻塞式方法是指程式會一直等待方法完成期間不做其他事情,ServerSocket的accept()方法就是一直等待客戶端連線。這裡的阻塞是指呼叫結果回傳之前,當前執行緒會被掛起,直到得到結果之後才會回傳。此外,還有非同步和非阻塞式方法在任務完成前就回傳。
一種是呼叫它裡面的stop()方法
另一個就是你自己設定一個停止執行緒的標記(推薦這個)
#43 、如何保證執行緒安全?
1.synchronized;
2.Object方法中的wait,notify;
3.ThreadLocal機制 來實現的。
44、如何實作執行緒同步?
1、synchronized關鍵字修改的方法。 2、synchronized關鍵字修飾的語句區塊3、使用特殊領域變數(volatile)實作執行緒同步
45、執行緒間操作#List
List list = Collections.synchronizedList(new ArrayList());
46、談對#Synchronized 關鍵字,類別鎖,方法鎖,重入鎖的理解
#java的物件鎖定和類別鎖定:java的物件鎖定和類別鎖定在鎖定的概念上基本上和內建鎖是一致的,但是,兩個鎖實際上是有很大的區別的,物件鎖是用於物件實例方法,或者一個物件實例上的,類別鎖是用於類別的靜態方法或是一個類別的class物件上的。我們知道,類別的物件實例可以有很多個,但是每個類別只有一個class對象,所以不同物件實例的物件鎖是互不干擾的,但是每個類別只有一個類別鎖。但有一點必須注意的是,其實類別鎖定只是一個概念上的東西,並不是真實存在的,它只是用來幫助我們理解鎖定實例方法和靜態方法的區別的
49、synchronized 與##volatile 關鍵字的差異
1.volatile本質是在告訴jvm目前變數在暫存器(工作記憶體)中的值是不確定的,需要從主記憶體讀取;synchronized則是鎖定目前變量,只有當前線程可以存取該變量,其他線程被阻塞住。 2.volatile僅能使用在變數層級;synchronized則可使用在變數、方法、和類別層級的3.volatile僅能實現變數的修改可見性,無法保證原子性;而synchronized則可以保證變數的修改可見性和原子性4.volatile不會造成執行緒的阻塞;synchronized可能會造成執行緒的阻塞。 5.volatile標記的變數不會被編譯器最佳化;synchronized標記的變數可以被編譯器最佳化51、ReentrantLock 、synchronized#與volatile比較
#ava在過去很長一段時間只能透過synchronized關鍵字來實現互斥,它有一些缺點。例如你不能擴充鎖之外的方法或是塊邊界,嘗試取得鎖時不能中途取消等。 Java 5 透過Lock介面提供了更複雜的控制來解決這些問題。 ReentrantLock 類別實作了 Lock,它擁有與 synchronized 相同的並發性和記憶體語義且它還具有可擴展性。53、死鎖的四個必要條件?
死鎖產生的原因1. 系統資源的競爭系統資源的競爭導致系統資源不足,以及資源分配不當,導致死鎖。 2. 進程運行推進順序不合適互斥條件:一個資源每次只能被一個進程使用,即在一段時間內某項資源僅為一個進程所佔有。此時若有其他進程請求該資源,則請求進程只能等待。 請求與保持條件:進程已經保持了至少一個資源,但又提出了新的資源請求,而該資源已被其他進程佔有,此時請求進程被阻塞,但對自己已獲得的資源保持不放。 不可剝奪條件:進程所獲得的資源在未使用完畢之前,不能被其他進程強行奪走,即只能 由獲得該資源的進程自己來釋放(只能是主動釋放)。 循環等待條件: 若干進程間形成首尾相接循環等待資源的關係這四個條件是死鎖的必要條件,只要係統發生死鎖,這些條件必然成立,而只要上述條件之一不滿足,就不會發生死鎖。 死鎖的避免與預防:死鎖避免的基本思想:#系統對進程發出每一個系統能夠滿足的資源申請進行動態檢查,並根據檢查結果決定是否分配資源,如果分配後系統可能發生死鎖,則不予分配,否則予以分配。這是一種保證系統不會進入死鎖狀態的動態策略。 了解死鎖的原因,尤其是產生死鎖的四個必要條件,就可以最大可能地避免、預防和解除死鎖。所以,在系統設計、流程排程等方面注意如何讓這四個必要條件不成立,如何決定資源的合理分配演算法,避免進程永久佔據系統資源。此外,也要防止進程在等待狀態的情況下佔用資源。因此,對資源的分配要給予合理的規劃。 死鎖避免和死鎖預防的區別:死鎖預防是設法至少破壞產生死鎖的四個必要條件之一,嚴格的防止死鎖的出現,而死鎖避免則不那麼嚴格的限制產生死鎖的必要條件的存在,因為即使死鎖的必要條件存在,也不一定發生死鎖。死鎖避免是在系統運作過程中註意避免死鎖的最終發生。56、什麼是執行緒池,如何使用#?
建立執行緒要花昂貴的資源和時間,如果任務來了才創建執行緒那麼回應時間就會變長,而且一個行程能創建的執行緒數有限。為了避免這些問題,在程式啟動的時候就創建若干執行緒來回應處理,它們被稱為執行緒池,裡面的執行緒叫做工作執行緒。從JDK1.5開始,Java API提供了Executor框架讓你可以建立不同的執行緒池。例如單執行緒池,每次處理一個任務;數目固定的執行緒池或是快取執行緒池(一個適合很多生存期短的任務的程式的可擴展執行緒池)。57、Java#中堆疊和堆疊有什麼不同?
為什麼要把這個問題歸類在多執行緒和並發面試題裡?因為棧是一塊和線程緊密相關的記憶體區域。每個線程都有自己的棧內存,用於存儲本地變量,方法參數和棧調用,一個線程中存儲的變量對其它線程是不可見的。而堆是所有執行緒共享的一片公用記憶體區域。物件都在堆裡創建,為了提升效率線程會從堆中弄一個緩存到自己的棧,如果多個線程使用該變量就可能引發問題,這時volatile 變量就可以發揮作用了,它要求線程從主存中讀取變數的值。
58、有三個執行緒T1,T2,T3,要怎麼確保它們能依序執行?
在多線程中有多種方法讓線程按特定順序執行,你可以用線程類別的join()方法在一個線程中啟動另一個線程,另外一個線程完成該線程繼續執行。為了確保三個執行緒的順序你應該先啟動最後一個(T3呼叫T2,T2呼叫T1),這樣T1就會先完成而T3最後完成。
執行緒間通訊
我們知道執行緒是CPU調度的最小單位。在Android中主執行緒是不能夠做耗時操作的,子執行緒是不能夠更新UI的。而線程間通訊的方式有很多,例如廣播,Eventbus,介面回掉,在Android中主要是使用handler。 handler透過呼叫sendmessage方法,將保存訊息的Message傳送到Messagequeue中,而looper物件不斷的呼叫loop方法,從messageueue中取出message,交給handler處理,從而完成線程間通訊。
執行緒池
Android中常見的執行緒池有四種,FixedThreadPool、CachedThreadPool、ScheduledThreadPool、SingleThreadExecutor。
FixedThreadPool執行緒池是透過Executors的new FixedThreadPool方法來建立。它的特點是該線程池中的線程數量是固定的。即使執行緒處於閒置的狀態,它們也不會被回收,除非執行緒池被關閉。當所有的執行緒都處於活躍狀態的時候,新任務就處於佇列中等待執行緒來處理。注意,FixedThreadPool只有核心線程,沒有非核心線程。
CachedThreadPool執行緒池是透過Executors的newCachedThreadPool進行建立的。它是一種線程數目不固定的線程池,它沒有核心線程,只有非核心線程,當線程池中的線程都處於活躍狀態,就會創建新的線程來處理新的任務。否則就會利用閒置的線程來處理新的任務。執行緒池中的執行緒都有超時機制,這個超時機制時長是60s,超過這個時間,閒置的執行緒就會被回收。這種執行緒池適合處理大量且耗時較少的任務。這裡得說一下,CachedThreadPool的任務佇列,基本上都是空的。
ScheduledThreadPool執行緒池是透過Executors的newScheduledThreadPool進行建立的,它的核心執行緒是固定的,但是非核心執行緒數是不固定的,並且當非核心執行緒一處於空閒狀態,就立即被回收。這種執行緒適合執行定時任務和具有固定週期的重複任務。
SingleThreadExecutor線程池是透過Executors的newSingleThreadExecutor方法來創建的,這類線程池中只有一個核心線程,也沒有非核心線程,這確保了所有任務能夠在同一個線程並且按照順序來執行,這樣就不需要考慮線程同步的問題。
AsyncTask的工作原理
#AsyncTask是Android本身提供的一種輕量級的非同步任務類別。它可以在執行緒池中執行後台任務,然後把執行的進度和最終的結果傳遞給主執行緒更新UI。實際上,AsyncTask內部是封裝了Thread和Handler。雖然AsyncTask很方便的執行後台任務,以及在主執行緒上更新UI,但是,AsyncTask並不合適進行特別耗時的後台操作,對於特別耗時的任務,個人還是建議使用執行緒池。
AsyncTask提供有4個核心方法:
1、onPreExecute():該方法在主執行緒中執行,在執行非同步任務之前會被調用,一般用於一些準備工作。
2、doInBackground(String... params):這個方法是在執行緒池中執行,此方法用於執行非同步任務。在這個方法中可以透過publishProgress方法來更新任務的進度,publishProgress方法會呼叫onProgressUpdate方法,另外,任務的結果回傳給onPostExecute方法。
3、onProgressUpdate(Object... values):該方法在主執行緒中執行,主要用於任務進度更新的時候,該方法會被呼叫。
4、onPostExecute(Long aLong):在主執行緒中執行,在非同步任務執行完畢之後,該方法會被調用,該方法的參數及為後台的返回結果。
除了這幾個方法之外還有一些不太常用的方法,如onCancelled(),在非同步任務取消的情況下,該方法會被呼叫。
原始碼可以知道從上面的execute方法內部呼叫的是executeOnExecutor()方法,即executeOnExecutor(sDefaultExecutor, params);而sDefaultExecutor實際上是一個序列的線程池。而onPreExecute()方法在這裡就會被呼叫了。接著看這個線程池。 AsyncTask的執行是排隊執行的,因為有關鍵字synchronized,而AsyncTask的Params參數就封裝成為FutureTask類,FutureTask這個類別是一個並發類,在這裡它充當了Runnable的作用。接著FutureTask會交給SerialExecutor的execute方法去處理,而SerialExecutor的executor方法會先將FutureTask加入mTasks佇列中,如果這個時候沒有任務,就會呼叫scheduleNext()方法,執行下一個任務。如果有任務的話,則執行完畢後最後在呼叫 scheduleNext();執行下一個任務。直到所有任務都被執行完畢。而AsyncTask的建構方法中有一個call()方法,而這個方法由於會被FutureTask的run方法執行。所以最終這個call方法會在執行緒池中執行。而doInBackground這個方法就是在這裡被呼叫的。我們好好研究一下這個call()方法。 mTaskInvoked.set(true);表示目前任務已經執行過了。接著執行doInBackground方法,最後將結果透過postResult(result);方法傳遞。 postResult()方法中透過sHandler來傳送訊息,sHandler的中透過訊息的類型來判斷一個MESSAGE_POST_RESULT,這種情況就是呼叫onPostExecute(result)方法或是onCancelled(result)。另一種訊息類型是MESSAGE_POST_PROGRESS則呼叫更新進度onProgressUpdate。
Binder的工作機制
直觀來說,Binder是Android中的一個類,它實現了IBinder接口,從IPC的角度來說,Binder是Android中的一種跨進程通訊的一種方式,同時也可以理解為是一種虛擬的實體設備,它的設備驅動是/dev/binder/。從Framework角度來說,Binder是ServiceManager的橋樑。從應用層來說,Binder是客戶端和服務端進行通訊的媒介。
我們先來了解這個類別中每個方法的意義:
DESCRIPTOR:Binder的唯一標識,一般用於目前Binder的類別名稱表示。
asInterface(android.os.IBinder obj):用於將服務端的Binder物件轉換成客戶端所需的AIDL介面類型的對象,這種轉換過程是區分進程的,如果客戶端和服務端位於同一個進程,那麼這個方法回傳的就是服務端的stub物件本身,否則回傳的是系統封裝後的Stub.proxy物件。
asBinder():用於傳回目前Binder物件。
onTransact:此方法運行在服務端的Binder執行緒池中,當客戶端發起跨進程通訊請求的時候,遠端請求透過系統底層封裝後交給該方法處理。注意這個方法public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags),服務端透過code可以確定客戶端所要求的目標方法是什麼,接著從data中取出目標方法所需的參數,然後執行目標方法。當目標方法執行完畢後,就像reply中寫入回傳值。這個方法的執行過程就是這樣的。如果這個方法回傳false,客戶端是會要求失敗的,所以我們可以在這個方法中做一些安全性驗證。
#Binder的工作機制但要注意一些問題:1、當客戶啟動請求時,由於目前執行緒會被掛起,直到服務端回傳數據,如果這個遠端方法很耗時的話,那麼是不能夠在UI線程,也就是主線程中發起這個遠端請求的。
2、由於Service的Binder方法運行在執行緒池中,所以Binder方法不管是耗時還是不耗時都應該採用同步的方式,因為它已經運行在一個執行緒中了。
view的事件分發和view#的工作原理
##Android自訂view,我們都知道實作有三部曲,onMeasure(),onLayout(),onDraw()。 View的繪製流程是從viewRoot的perfromTraversal方法開始的。它經過measure,layout,draw方法才能夠將view繪製出來。其中measure是測量寬高的,layout是確定view在父容器上的擺佈位置的,draw是將view繪製到螢幕上的。Measure:
view的測量是需要MeasureSpc(測量規格),它代表一個32位元int值,高2位元代表SpecMode(測量模式),低(30)位的代表SpecSize(某種測量模式下的規格大小)。而一組SpecMode和SpeSize可以打包為一個MeasureSpec,反之,MeasureSpec可以解包得到SpecMode和SpeSize的值。 SpecMode有三類:unSpecified:父容器不對view有任何限制,要多大有多大。一般系統用這個多。Exactly:父容器已經偵測出view所需的精確大小,這個時候,view的大小就是SpecSize所指定的值,它對應者layout佈局中的math_parent或是具體的數值
#At_most :父容器指定了一個可用大小的SpecSize,view的大小不能夠大於這個值,它對應這佈局中的wrao_content.
對於普通的view,它的MeasureSpec是由父容器的MeasureSpec和自身的layoutParam共同決定的,一旦MeasureSpec確定後,onMeasure就可以確定view的寬高了。
View的measure過程:
onMeasure方法中有個setMeasureDimenSion方法來設定view的寬高測量值,而setMeasureDimenSion有個getDefaultSize()方法作為參數。一般情況下,我們只需要關注at_most和exactly兩種情況,getDefaultSize的回傳值就是measureSpec中的SpecSize,而這個值基本上就是view測量後的大小。而UnSpecified這種情況,一般是系統內部的測量過程,它是需要考慮view的背景這些因素的。
前面說的是view的測量過程,而viewGroup的measure過程:
對於viewGroup來說,除了完成自己的measure過程以外,還要遍歷去呼叫子類別的measure方法,各個子元素在遞歸執行這個過程,viewGroup是一個抽象的類,沒有提供有onMeasure方法,但是提供了一個measureChildren的方法。 measureChild方法的想法就是取出子元素的layoutParams,然後透過getChildMeasureSpec來常見子元素的MeasureSpec,然後子元素在電泳measure方法進行測量。由於viewGroup子類別有不同的佈局方式,導致他們的測量細節不一樣,所以viewGroup不能像view一樣調用onMeasure方法進行測量。
注意:在activity的生命週期中是沒有辦法正確的獲取view的寬高的,原因就是view沒有測量完。
onLayout
普通的view的話,可以透過setFrame方法來的到view四個頂點的位置,也就確定了view在父容器的位置,接著就呼叫onLayout方法,該方法是父容器決定子元素的位置。
onDraw
該方法就是將view繪製到螢幕上。分成以下幾個步驟
Android中效能最佳化
#由於手機硬體的限制,記憶體和CPU都無法像pc一樣具有超大的內存,在 Android手機上,過多的使用內存,會容易導致oom,過多的使用CPU資源,會導致手機卡頓,甚至導致anr。我主要是從幾個部分進行優化:
佈局優化,繪製優化,內存洩漏優化,響應速度優化,listview優化,bitmap優化,線程優化
佈局優化:工具hierarchyviewer,解決方式:
1、刪除無用的空間和層級。
2、選擇效能較低的viewgroup,如Relativelayout,如果可以選擇Relativelayout也可以使用LinearLayout,就優先使用LinearLayout,因為相對來說Relativelayout功能較為複雜,會佔用更多的CPU資源。
3、使用標籤
繪製最佳化
繪製最佳化指view在ondraw方法中避免大量的耗時操作,由於ondraw方法可能會被頻繁的呼叫。
1、ondraw方法中不要建立新的局部變量,ondraw方法被頻繁的調用,很容易引起GC。
2、ondraw方法不要做耗時操作。
記憶體最佳化:參考記憶體洩漏。
回應最佳化:主執行緒不能做耗時操作,觸碰事件5s,廣播10s,service20s。
listview最佳化:
1、getview方法中避免耗時操作。
2、view的複用和viewholder的使用。
3、滑動不適合開啟非同步載入。
4、分頁處理資料。
5、圖片使用三級快取。
Bitmap最佳化:
1、等比例壓縮圖片。
2、不用的圖片,及時recycler掉
線程優化
線程優化的想法是使用線程池來管理和復用線程,避免程式中有大量的Thread,同時可以控制執行緒的並發數,避免相互搶佔資源而導致執行緒阻塞。
其他優化
1、少用枚舉,枚舉佔用空間大。
2、使用Android特有的資料結構,如SparseArray來取代hashMap。
3、適當的使用軟引用和弱引用。
加密演算法(base64、MD5、對稱加密和非對稱加密)和使用場景。
什麼是Rsa加密?
RSA演算法是最受歡迎的公鑰密碼演算法,使用長度可以變化的金鑰。 RSA是第一個既能用於資料加密也能用於數位簽章的演算法。
RSA演算法原理如下:
1.隨機選擇兩個大質數p和q,p不等於q,計算N=pq;
2.選擇一個大於1小於N的自然數e,e必須與(p-1)(q-1)互素。
3.用公式計算d:d×e = 1 (mod (p-1)(q-1)) 。
4.銷毀p和q。
最終得到的N和e就是“公鑰”,d就是“私鑰”,發送方使用N去加密數據,接收者只有使用d才能解開數據內容。
RSA的安全性依賴大數分解,小於1024位元的N已經被證明是不安全的,而且由於RSA演算法進行的都是大數計算,使得RSA最快的情況也比DES慢上倍,這是RSA最大的缺陷,因此通常只能用於加密少量資料或加密金鑰,但RSA仍然不會失去為高強度的演算法。
使用場景:專案中除了登陸,支付等介面採用rsa非對稱加密,之外的採用aes對稱加密,今天我們來認識aes加密。
什麼是MD5加密?
MD5英文全名為“Message-Digest Algorithm 5”,翻譯過來是“訊息摘要演算法5”,由MD2、MD3、MD4演變過來的,是一種單向加密演算法,是不可逆的一種的加密方式。
MD5加密有哪些特點?
壓縮性:任意長度的數據,所算出的MD5值長度都是固定的。
很容易計算:從原始資料計算MD5值很容易。
抗修改性:對原始資料進行任何改動,即使只修改1個位元組,所得到的MD5值都有很大差異。
強抗碰撞:已知原始資料和其MD5值,想找出一個具有相同MD5值的資料(即偽造資料)是非常困難的。
MD5應用情境:
一致性驗證
數位簽章
安全存取認證
什麼是aes加密?
高級加密標準(英文:Advanced Encryption Standard,縮寫:AES),在密碼學中又稱Rijndael加密法,是美國聯邦政府採用的一種區塊加密標準。這個標準用來取代原先的DES,已經被多方分析且廣為全世界所使用。
HashMap的實作原理:
HashMap是基於雜湊表的map介面的非同步實現,它允許使用null值作為key和value。在Java程式語言中最基本的結構就是兩種,一種是數組,另一種是模擬指標(引用)。所有的資料結構都可以用這兩個基本的結構來構造,HashMap也不例外。 HashMap其實是一個「鍊錶散列」的資料結構。即數組和鍊錶的結合體。
HashMap底層就是一個資料結構,陣列中的每一項又是一個鍊錶。
衝突:
HashMap中呼叫Hashcode()方法計算Hashclde值,由於Java中兩個不同的物件可能有相同的Hashcode。就導致了衝突的產生。
解決:
HashMap在put時候,底層原始碼可以看出,當程式試圖將一個key-value物件放入到HashMap中,首先根據該key的hashCode()傳回值決定該Entry的儲存位置,如果兩個Entry的key的hashCode()方法回傳值相同,那麼他們的儲存位置相同,如果這兩個Entry的key透過equals比較傳回true,新加入的Entry的value將會覆蓋原來的Entry的value,但是key不會被覆蓋,反之,如果返回false,新添加的Entry將與集合中原有的Entry形成Entry鏈,新添加的位於頭部,舊的位於尾部
HashMap的實作原理:
===========================
#1、Activity生命週期?
onCreate() -> onStart() -> onResume() -> onPause() -> onStop() -> onDetroy()
# 2、Service生命週期?
service 啟動方式有兩種,一種是透過startService()方式進行啟動,另一種是透過bindService()方式進行啟動。不同的啟動方式他們的生命週期是不同.
透過startService()這種方式啟動的service,生命週期是這樣:呼叫startService() --> onCreate()--> onStartConmon()--> onDestroy()。這種方式啟動的話,需要注意幾個問題,第一:當我們透過startService被呼叫以後,多次在呼叫startService(),onCreate()方法也只會被呼叫一次,而onStartConmon()會被多次調用當我們調用stopService()的時候,onDestroy()就會被調用,從而銷毀服務。第二:當我們透過startService啟動時候,透過intent傳值,在onStartConmon()方法中取得值的時候,一定要先判斷intent是否為null。
透過bindService()方式進行綁定,這種方式綁定service,生命週期走法:bindService-->onCreate()-->onBind()-->unBind()- ->onDestroy() bingservice 這種方式進行啟動service好處是更加便利activity中操作service,例如加入service中有幾個方法,a,b ,如果要在activity中調用,在需要在activity獲取ServiceConnection對象,透過ServiceConnection來取得service中內部類別的類別對象,然後透過這個類別物件就可以呼叫類別中的方法,當然這個類別需要繼承Binder物件
3、Activity的啟動過程(不要回答生命週期)
app啟動的過程有兩種情況,第一種是從桌面launcher上點擊對應的應用程式圖標,第二種是在activity中透過呼叫startActivity來啟動一個新的activity。
我們建立一個新的項目,預設的根activity都是MainActivity,而所有的activity都是保存在堆疊中的,我們啟動一個新的activity就會放在上一個activity上面,而我們從桌面點選應用圖示的時候,由於launcher本身也是一個應用,當我們點擊圖示的時候,系統就會呼叫startActivitySately(),一般情況下,我們所啟動的activity的相關資訊都會保存在intent中,例如action ,category等等。我們在安裝這個應用程式的時候,系統也會啟動一個PackaManagerService的管理服務,這個管理服務會對AndroidManifest.xml檔案進行解析,從而得到應用程式中的相關信息,例如service,activity,Broadcast等等,然後獲得相關組件的資訊。當我們點選應用圖示的時候,就會呼叫startActivitySately()方法,而這個方法內部則是呼叫startActivty(),而startActivity()方法最終還是會呼叫startActivityForResult()這個方法。而在startActivityForResult()這個方法。因為startActivityForResult()方法是有回傳結果的,所以系統就直接給一個-1,就表示不需要結果回傳了。而startActivityForResult()這個方法實際上是透過Instrumentation類別中的execStartActivity()方法來啟動activity,Instrumentation這個類別主要作用就是監控程式和系統之間的互動。而在這個execStartActivity()方法中會取得ActivityManagerService的代理對象,透過這個代理對象進行啟動activity。啟動會就會呼叫一個checkStartActivityResult()方法,如果說沒有在設定清單中配置有這個元件,就會在這個方法中拋出例外了。當然最後是呼叫的是Application.scheduleLaunchActivity()進行啟動activity,而這個方法中透過取得得到一個ActivityClientRecord對象,而這個ActivityClientRecord透過handler來進行訊息的傳送,系統內部會將每一個activity元件使用ActivityClientRecord物件來進行描述,而ActivityClientRecord物件中保存有一個LoaderApk對象,透過這個物件呼叫handleLaunchActivity來啟動activity元件,而頁面的生命週期方法也就是在這個方法中進行呼叫。
4、Broadcast註冊方式與區別
此處延伸:什麼情況下用動態註冊
Broadcast廣播,註冊方式主要有兩種.
############################################## #第一種是靜態註冊,也可成為常駐型廣播,這種廣播需要在Androidmanifest.xml中進行註冊,這中方式註冊的廣播,不受頁面生命週期的影響,即使退出了頁面,也可以收到廣播這種廣播一般用來想開機自啟動啊等等,由於這種註冊的方式的廣播是常駐型廣播,所以會佔用CPU的資源。 ###### 第二種是動態註冊,而動態註冊的話,是在程式碼中註冊的,這種註冊方式也叫非常駐型廣播,收到生命週期的影響,退出頁面後,就不會收到廣播,我們通常運用在更新UI方面。這種註冊方式優先順序較高。最後需要解綁,否會會記憶體外洩######廣播是分為有序廣播與無序廣播。 ###### 5、HttpClient與HttpUrlConnection的差異 #######此處延伸:Volley裡用的哪一種請求方式(2.3前HttpClient,2.3後HttpUrlConnection)###首先HttpClient和HttpUrlConnection 這兩種方式都支援Https協議,都是以流的形式進行上傳或下載數據,也可以說是以流的形式進行數據的傳輸,還有ipv6,以及連接池等功能。 HttpClient這個擁有非常多的API,所以如果想要進行擴展的話,並且不破壞它的兼容性的話,很難進行擴展,也就是這個原因,Google在Android6.0的時候,直接就棄用了這個HttpClient .
而HttpUrlConnection相對來說就是比較輕量級了,API比較少,容易擴展,並且能夠滿足Android大部分的資料傳輸。比較經典的一個框架volley,在2.3版本以前都是使用HttpClient,在2.3以後就使用了HttpUrlConnection。
6、java虛擬機器和Dalvik虛擬機的區別
Java虛擬機:
1、java虛擬機基於堆疊。 基於堆疊的機器必須使用指令來載入和操作棧上數據,所需指令更多更多。
2、java虛擬機器運行的是java字節碼。 (java類別會被編譯成一個或多個字節碼.class檔案)
Dalvik虛擬機器:
1、dalvik虛擬機器是基於暫存器的
##2 、Dalvik運行的是自訂的.dex字節碼格式。 (java類別被編譯成.class檔案後,會透過一個dx工具將所有的.class檔案轉換成一個.dex文件,然後dalvik虛擬機會從其中讀取指令和資料#3、常數池已修改為只使用32位元的索引,以簡化解釋器。4、一個應用,一個虛擬機器實例,一個行程(所有android應用程式的執行緒都是對應一個linux線程,都運行在在自己的沙盒中,不同的應用在不同的進程中運行。每個android dalvik應用程式都被賦予了一個獨立的linux PID(app_*)) 7、進程保活(不死進程)此處延伸:進程的優先順序是什麼當前業界的Android進程保活手段主要分為** 黑、白、灰**三種,其大致的實現想法如下:黑色保活:不同的app進程,用廣播相互喚醒(包括利用系統提供的廣播進行喚醒)白色保活:啟動前台Service灰色保活:利用系統的漏洞啟動前台Service黑色保活所謂黑色保活,就是利用不同的app進程使用廣播來進行相互喚醒。舉個3個比較常見的場景:場景1:開機,網路切換、拍照、拍影片時候,利用系統產生的廣播喚醒app場景2:接取第三方SDK也會喚醒對應的app進程,如微信sdk會喚醒微信,支付寶sdk會喚醒支付寶。由此發散開去,就會直接觸發了下面的場景3場景3:假如你手機裡裝了支付寶、淘寶、天貓、UC等阿里系的app,那麼你打開任意一個阿里系的app後,有可能就順便把其他阿里系的app給喚醒了。(只是拿阿里打個比方,其實BAT係都差不多) 白色保活白色保活手段非常簡單,就是呼叫系統api啟動一個前台的Service進程,這樣會在系統的通知欄產生一個Notification,用來讓使用者知道有這樣一個app在運作著,即使目前的app退到了後台。如下方的LBE和QQ音樂這樣:灰色保活灰色保活,這種保活手段是應用範圍最廣泛。它是利用系統的漏洞來啟動一個前台的Service進程,與普通的啟動方式區別在於,它不會在系統通知欄處出現一個Notification,看起來就如同運行著一個後台Service進程一樣。這樣做帶來的好處就是,使用者無法察覺到你運行著一個前台進程(因為看不到Notification),但你的進程優先級又是高於普通後台進程的。那麼如何利用系統的漏洞呢,大致的實作思路與程式碼如下:思路一:API < 18,啟動前台Service時直接傳入new Notification();##思路二:API >= 18,同時啟動兩個id相同的前台Service,然後再將後啟動的Service做stop處理
熟悉Android系統的童鞋都知道,系統出於體驗和性能上的考慮,app在退到後台時系統並不會真正的kill掉這個進程,而是將其緩存起來。開啟的應用程式越多,後台快取的進程也越多。在系統記憶體不足的情況下,系統開始依據自身的一套進程回收機制來判斷要kill掉哪些進程,以騰出記憶體來供給需要的app。這套殺進程回收記憶體的機制就叫 Low Memory Killer ,它是基於Linux核心的 OOM Killer(Out-Of-Memory killer)機制誕生。
處理程序的重要性,分割5級:
前台程序(Foreground process)
可見程式(Visible process)
服務程序(Service process )
後台進程(Background process)
空進程(Empty process)
了解完 Low Memory Killer,再科普一下oom_adj。什麼是oom_adj?它是linux核心分配給每個系統行程的一個值,代表行程的優先權,行程回收機制就是根據這個優先權來決定是否要回收。對於oom_adj的作用,你只需要記住以下幾點即可:
進程的oom_adj越大,表示此進程優先權越低,越容易被殺回收;越小,表示進程優先權越高,越不容易被殺回收
普通app進程的oom_adj>=0,系統進程的oom_adj才可能<0
有些手機廠商把這些知名的app放入了自己的白名單中,保證了進程不死來提高用戶體驗(如微信、QQ、陌陌都在小米的白名單中)。如果從白名單中移除,他們終究還是和普通app一樣躲避不了被殺的命運,為了盡量避免被殺,還是老老實實去做好優化工作吧。
所以,進程保活的根本方案終究還是回到了效能最佳化上,進程永生不死終究是個徹頭徹尾的偽命題!
8、講解一下Context
Context是一個抽象基底類別。在翻譯為上下文,也可以理解為環境,是提供一些程式的運行環境基礎資訊。 Context下有兩個子類,ContextWrapper是上下文功能的封裝類,而ContextImpl則是上下文功能的實作類別。而ContextWrapper又有三個直接的子類, ContextThemeWrapper、Service和Application。其中,ContextThemeWrapper是一個帶有主題的封裝類,而它有一個直接子類別就是Activity,所以Activity和Service以及Application的Context是不一樣的,只有Activity需要主題,Service不需要主題。 Context一共有三種類型,分別是Application、Activity和Service。這三個類別雖然分別各種承擔著不同的作用,但它們都屬於Context的一種,而它們具體Context的功能則是由ContextImpl類去實現的,因此在絕大多數場景下,Activity、Service和Application這三種類型的Context都是可以通用的。不過有幾種場景比較特殊,像是啟動Activity,還有彈出Dialog。出於安全原因的考慮,Android是不允許Activity或Dialog憑空出現的,一個Activity的啟動必須建立在另一個Activity的基礎之上,也就是以此形成的回傳堆疊。而Dialog則必須在一個Activity上面彈出(除非是System Alert類型的Dialog),因此在這個場景下,我們只能使用Activity類型的Context,否則將會出錯。
getApplicationContext()和getApplication()方法得到的對像都是同一個application對象,只是對象的類型不一樣。
Context數量= Activity數量Service數量1 (1為Application)
9、理解Activity,View,Window三者關係
這個問題真的很不好回答。所以這裡先來個算是比較恰當的比喻來形容下它們的關係吧。 Activity像工匠(控制單元),Window像窗戶(承載模型),View像窗花(顯示視圖)LayoutInflater像剪刀,Xml配置像窗花圖。
1:Activity構造的時候會初始化一個Window,準確的說是PhoneWindow。
2:這個PhoneWindow有一個“ViewRoot”,這個“ViewRoot”是一個View或者說ViewGroup,是最初始的根視圖。
3:「ViewRoot」透過addView方法來一個個的新增View。例如TextView,Button等
4:這些View的事件監聽,是由WindowManagerService來接受訊息,並且回呼Activity函數。例如onClickListener,onKeyDown等。
10、四種LaunchMode及其使用場景
此處延伸:堆疊(First In Last Out)與佇列(First In First Out)的差異
堆疊與佇列的差異:
1. 佇列先進先出,堆疊先進後出
2. 對插入與刪除操作的"限定"。棧是限定只能在表的一端進行插入和刪除操作的線性表。隊列是限定只能在表的一端進行插入和在另一端進行刪除操作的線性表。
3. 遍歷資料速度不同
standard 模式
這是預設模式,每次啟動Activity時都會建立Activity實例,並放入任務堆疊中。使用場景:大多數Activity。
singleTop 模式
如果在任務的堆疊頂部剛好存在該Activity的實例,就重用該實例( 會呼叫實例的onNewIntent() ),否則就會建立新的實例並放入棧頂,即使堆疊中已經存在該Activity的實例,只要不在堆疊頂,就會建立新的實例。使用場景如新聞類或閱讀類App的內容頁面。
singleTask 模式
如果在堆疊中已經有該Activity的實例,就重複使用該實例(會呼叫實例的 onNewIntent() )。重複使用時,會讓該實例回到棧頂,因此在它上面的實例將會被移出堆疊。如果堆疊中不存在該實例,將會建立新的實例放入堆疊中。使用場景如瀏覽器的主介面。不管從多少個應用程式啟動瀏覽器,只會啟動主介面一次,其餘情況都會走onNewIntent,並且會清空主介面上面的其他頁面。
singleInstance 模式
在一個新堆疊中建立該Activity的實例,並讓多個應用程式共用該堆疊中的該Activity實例。一旦該模式的Activity實例已經存在於某個堆疊中,任何應用程式再啟動該Activity時都會重複使用該堆疊中的實例( 會呼叫實例的 onNewIntent() )。其效果相當於多個應用程式共享一個應用,不管誰啟動該 Activity 都會進入同一個應用程式。使用場景如鬧鈴提醒,將鬧鈴提醒與鬧鈴設定分開。 singleInstance不要用於中間頁面,如果用於中間頁面,跳轉會有問題,例如:A -> B (singleInstance) -> C,完全退出後,在此啟動,首先打開的是B。
11、View的繪圖流程
自訂控制項:
#1、組合控制項。這種自訂控制項不需要我們自己繪製,而是使用原生控制項組合成的新控制項。如標題列。
2、繼承原有的控制項。這種自訂控制項在原生控制項提供的方法外,可以自行加入一些方法。如製作圓角,圓形圖。
3、完全自訂控制:這個View上所展現的內容全部都是我們自己繪製出來的。比如說製作水波紋進度條。
View的繪圖流程:OnMeasure()——>OnLayout()——>OnDraw()
第一步:OnMeasure():測量視圖大小。從頂層父View到子View遞歸呼叫measure方法,measure方法又回調OnMeasure。
第二步:OnLayout():決定View位置,進行頁面佈局。從頂層父View向子View的遞歸呼叫view.layout方法的過程,即父View根據上一步measure子View所得到的佈局大小和佈局參數,將子View放在適當的位置。
第三步:OnDraw():繪製視圖。 ViewRoot建立一個Canvas對象,然後呼叫OnDraw()。六個步驟:①、繪製視圖的背景;②、保存畫布的圖層(Layer);③、繪製View的內容;④、繪製View子視圖,如果沒有就不用;
⑤、還原圖層(Layer);⑥、繪製捲軸。
12、View,ViewGroup事件分
1. Touch事件分發中只有兩位主角:ViewGroup和View。 ViewGroup包含onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent三個相關事件。 View包含dispatchTouchEvent、onTouchEvent兩個相關事件。其中ViewGroup又繼承於View。
2.ViewGroup和View組成了一個樹狀結構,根節點為Activity內部包含的一個ViwGroup。
3.觸摸事件由Action_Down、Action_Move、Aciton_UP組成,其中一次完整的觸摸事件中,Down和Up都只有一個,Move有若干個,可以為0個。
4.當Acitivty接收到Touch事件時,將遍歷子View進行Down事件的分發。 ViewGroup的遍歷可以看成是遞歸的。分發的目的是為了找到真正要處理本次完整觸控事件的View,這個View會在onTouchuEvent結果回傳true。
5.當某個子View回傳true時,會中止Down事件的分發,同時在ViewGroup中記錄該子View。接下去的Move和Up事件將由該子View直接處理。由於子View是保存在ViewGroup中的,多層ViewGroup的節點結構時,上級ViewGroup保存的會是真實處理事件的View所在的ViewGroup對象:如ViewGroup0-ViewGroup1-TextView的結構中,TextView返回了true,它將會儲存在ViewGroup1中,而ViewGroup1也會回傳true,並儲存在ViewGroup0中。當Move和UP事件來時,會先從ViewGroup0傳遞至ViewGroup1,再由ViewGroup1傳遞至TextView。
6.當ViewGroup中所有子View都不捕獲Down事件時,將觸發ViewGroup自身的onTouch事件。觸發的方式是呼叫super.dispatchTouchEvent函數,也就是父類別View的dispatchTouchEvent方法。在所有子View都不處理的情況下,觸發Acitivity的onTouchEvent方法。
7.onInterceptTouchEvent有兩個作用:1.攔截Down事件的分發。 2.中止Up和Move事件向目標View傳遞,使得目標View所在的ViewGroup捕捉Up和Move事件。
13、保存Activity狀態
onSaveInstanceState(Bundle)會在activity轉入後台狀態之前被調用,也就是onStop()方法之前,onPause方法之後被調用;
14、Android中的幾種動畫
影格動畫:指透過指定每個畫面的圖片和播放時間,有順序的進行播放而形成動畫效果,例如想聽的律動條。
補間動畫:指透過指定View的初始狀態、變化時間、方式,透過一系列的演算法去進行圖形變換,從而形成動畫效果,主要有Alpha、Scale、Translate、Rotate四種效果。注意:只是在視圖層實現了動畫效果,並沒有真正改變View的屬性,例如滑動列表,改變標題列的透明度。
屬性動畫:在Android3.0的時候才支持,透過不斷的改變View的屬性,不斷的重繪而形成動畫效果。相較於視圖動畫,View的屬性是真正改變了。例如view的旋轉,放大,縮小。
15、Android中跨進程通訊的幾種方式
Android 跨進程通信,像intent,contentProvider,廣播,service都可以跨進程通信。
intent:這種跨進程方式並不是存取記憶體的形式,它需要傳遞一個uri,比如說打電話。
contentProvider:這種形式,是使用資料共享的形式進行資料共享。
service:遠端服務,aidl
廣播
16、AIDL瞭解
此處延伸:簡述Binder
AIDL:每個進程都有自己的Dalvik VM實例,都有自己的一塊獨立的內存,都在自己的內存上存儲自己的數據,執行著自己的操作,都在自己的那片狹小的空間裡過完自己的一生。而aidl就類似與兩個進程之間的橋樑,使得兩個進程之間可以進行資料的傳輸,跨進程通信有多種選擇,比如BroadcastReceiver , Messenger 等,但是BroadcastReceiver 佔用的系統資源比較多,如果是頻繁的跨進程通訊的話顯然是不可取的;Messenger 進行跨進程通訊時請求佇列是同步進行的,無法並發執行。
Binde機制簡單理解:
在Android系統的Binder機制中,是有Client,Service,ServiceManager,Binder驅動程式組成的,其中Client,service,Service Manager運行在用戶空間,Binder驅動程式是運行在核心空間的。而Binder就是把這4種元件黏合在一塊的黏合劑,其中核心的元件就是Binder驅動程序,Service Manager提供輔助管理的功能,而Client和Service正是在Binder驅動程式和Service Manager提供的基礎設施上實現C/S 之間的通訊。其中Binder驅動程式提供裝置檔案/dev/binder與使用者控制項進行交互,
Client、Service,Service Manager透過open和ioctl檔案操作對應的方法與Binder驅動程式進行通訊。而Client和Service之間的進程間通訊是透過Binder驅動程式間接實現的。而Binder Manager是一個守護程序,用來管理Service,並向Client提供查詢Service介面的能力。
17、Handler的原理
Android中主執行緒是不能進行耗時操作的,子執行緒是不能進行更新UI的。所以就有了handler,它的作用就是實作執行緒之間的通訊。
handler整個流程中,主要有四個對象,handler,Message,MessageQueue,Looper。當應用程式建立的時候,就會在主執行緒中建立handler對象,
我們透過要傳送的訊息儲存到Message中,handler透過呼叫sendMessage方法將Message傳送到MessageQueue中,Looper物件就會不斷的呼叫loop()方法
不斷的從MessageQueue中取出Message交給handler處理。從而實現線程之間的通訊。
18、Binder機制原則
在Android系統的Binder機制中,是有Client,Service,ServiceManager,Binder驅動程式組成的,其中Client,service,Service Manager運行在使用者空間,Binder驅動程式是運行在核心空間的。而Binder就是把這4種元件黏合在一塊的黏合劑,其中核心的元件就是Binder驅動程序,Service Manager提供輔助管理的功能,而Client和Service正是在Binder驅動程式和Service Manager提供的基礎設施上實現C/S 之間的通訊。其中Binder驅動程式提供裝置檔案/dev/binder與使用者控制項進行交互,Client、Service,Service Manager透過open和ioctl檔案操作對應的方法與Binder驅動程式進行通訊。而Client和Service之間的進程間通訊是透過Binder驅動程式間接實現的。而Binder Manager是一個守護程序,用來管理Service,並向Client提供查詢Service介面的能力。
19、熱修復的原理
我們知道Java虛擬機器- JVM 是載入類別的class檔案的,而Android虛擬機器-Dalvik/ART VM 是載入類別的dex文件,
而他們載入類別的時候都需要ClassLoader,ClassLoader有一個子類別BaseDexClassLoader,而BaseDexClassLoader下有一個
陣列-DexPathList,是用來存放dex文件,當BaseDexClassLoader透過當呼叫findClass方法時,其實就是遍歷數組,
找到對應的dex文件,找到,則直接將它return。而熱修復的解決方法就是將新的dex添加到該集合中,並且是在舊的dex的前面,
所以就會優先被取出並且return返回。
20、Android記憶體外洩及管理
(1)記憶體溢位(OOM)和記憶體外洩(物件無法回收)的差異。
(2)造成記憶體外洩的原因
(3) 記憶體外洩偵測工具------>LeakCanary
記憶體溢位out of memory:是指程式在申請記憶體時,沒有足夠的記憶體空間供其使用,出現out of memory;例如申請了一個integer,但給它存了long才能存下的數,那就是記憶體溢位。記憶體溢位通俗的講就是記憶體不夠用。
記憶體洩漏memory leak:是指程式在申請記憶體後,無法釋放已申請的記憶體空間,一次記憶體外洩危害可以忽略,但記憶體外洩堆積後果很嚴重,無論多少記憶體,遲早會被佔光
記憶體外洩原因:
一、Handler 造成的記憶體洩漏。
解決:將Handler宣告為靜態內部類,就不會持有外部類SecondActivity的引用,其生命週期就和外部類無關,
如果Handler裡面需要context的話,可以以弱引用方式引用外部類別
二、單例模式造成的記憶體洩漏。
解決:Context是ApplicationContext,由於ApplicationContext的生命週期是和app一致的,不會導致記憶體洩漏
三、非靜態內部類別創建靜態實例引起的記憶體洩漏。
解決:把內部類別修改為靜態的就可以避免記憶體洩漏了
四、非靜態匿名內部類別所造成的記憶體洩漏。
解決:將匿名內部類別設定為靜態的。
五、註冊/反註冊未成對使用所造成的記憶體洩漏。
註冊廣播接受器、EventBus等,記得解綁。
六、資源物件沒有關閉所造成的記憶體洩漏。
在這些資源不使用的時候,記得呼叫對應的類似close()、destroy()、recycler()、release()等方法釋放。
七、集合物件沒有及時清理所造成的記憶體洩漏。
通常會把一些物件裝入到集合中,當不使用的時候一定要記得及時清理集合,讓相關物件不再被引用。
21、Fragment與Fragment、Activity通訊的方式
1.直接在一個Fragment中呼叫另外一個Fragment中的方法
2.使用介面回呼
3.使用廣播
4.Fragment直接呼叫Activity中的public方法
22、Android UI適配
字體使用sp,使用dp,多使用match_parent,wrap_content,weight
圖片資源,不同圖片的的分辨率,放在相應的資料夾下可使用百分比代替。
23、app優化
app優化:(工具:Hierarchy Viewer 分析佈局 工具:TraceView 測試分析耗時的)
App啟動優化
佈局優化
響應優化
記憶體最佳化
電池使用最佳化
網路優化
App啟動優化(針對冷啟動)
App啟動的方式有三種:
冷啟動:App沒有啟動過或App程序被killed, 系統中不存在該App程序, 此時啟動App即為冷啟動。
熱啟動:熱啟動意味著你的App進程只是處於後台, 系統只是將其從後台帶到前台, 展示給用戶。
介於冷啟動和熱啟動之間, 一般來說在以下兩種情況下發生:
(1)用戶back退出了App, 然後又啟動. App進程可能還在運行, 但是activity需要重建。
(2)用戶退出App後, 系統可能由於記憶體原因將App殺死, 進程和activity都需要重啟, 但是可以在onCreate中將被動殺死鎖保存的狀態(saved instance state)恢復。
最佳化:
Application的onCreate(特別是第三方SDK初始化),首屏Activity的渲染都不要進行耗時操作,如果有,就可以放到子執行緒或IntentService中
佈局優化
盡量不要過於複雜的巢狀。可以使用
回應最佳化
##Android系統每隔16ms就會發出VSYNC訊號重繪我們的介面(Activity)。 頁面卡頓的原因:(1)過於複雜的佈局.#(2)UI執行緒的複雜運算(3)頻繁的GC,導致頻繁GC有兩個原因:1、記憶體抖動, 即大量的物件被創建又在短時間內馬上被釋放.2、瞬間產生大量的物件會嚴重佔用記憶體區域。 記憶體最佳化:參考記憶體外洩與記憶體溢出部分電池使用最佳化(使用工具:Batterystats & bugreport)(1)最佳化網路請求(2)定位中使用GPS, 請記得及時關閉網路優化(網路連線對使用者的影響:流量,電量,使用者等待)可在Android studio下方logcat旁邊那個工具Network Monitor偵測API設計:App與Server之間的API設計要考慮網路請求的頻次, 資源的狀態等. 以便App可以以較少的請求來完成業務需求和介面的展示. Gzip壓縮:使用Gzip來壓縮request和response, 減少傳輸資料量, 從而減少流量消耗.圖片的Size:可以在獲取圖片時告知伺服器需要的圖片的寬高, 以便伺服器給出合適的圖片, 避免浪費.網路快取:適當的快取, 既可以讓我們的應用看起來更快, 也能避免一些不必要的流量消耗. #24、圖片優化(1)對圖片本身進行操作。盡量不要使用setImageBitmap、setImageResource、BitmapFactory.decodeResource來設定一張大圖,因為這些方法在完成decode後,最終都是透過java層的createBitmap來完成的,需要消耗更多記憶體.(2)圖片進行縮放的比例,SDK中建議其值是2的指數值,值越大會導致圖片不清晰。 (3)不用的圖片記得呼叫圖片的recycle()方法25、HybridApp WebView和JS互動Android與JS透過WebView互相呼叫方法,實際上是:Android去呼叫JS的程式碼1. 透過WebView的loadUrl(),使用此方法比較簡潔,方便。但是效率比較低,取得回傳值比較困難。
2. 透過WebView的evaluateJavascript(),該方法效率高,但是4.4以上的版本才支持,4.4以下版本不支援。所以建議兩者混合使用。
JS去呼叫Android的程式碼
1. 透過WebView的addJavascriptInterface()進行物件映射,該方法使用簡單,僅將Android物件和JS物件映射即可,但是存在比較大的漏洞。
漏洞產生原因是:當JS拿到Android這個物件後,就可以呼叫這個Android物件中所有的方法,包含系統類別(java.lang.Runtime 類別),以便進行任意程式碼執行。
解決方式:
(1)Google 在Android 4.2 版本中規定對被呼叫的函數以 @JavascriptInterface進行註解從而避免漏洞攻擊。
(2)在Android 4.2版本之前採用攔截prompt()進行漏洞修復。
2. 透過 WebViewClient 的shouldOverrideUrlLoading ()方法回呼攔截 url 。這種方式的優點:不存在方式1的漏洞;缺點:JS取得Android方法的回傳值複雜。 (ios主要用的是這個方式)
(1)Android透過WebViewClient 的回呼方法shouldOverrideUrlLoading ()攔截url
(2)解析該url 的協定
( 3)如果偵測到是預先約定好的協議,就呼叫對應方法
3. 透過WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回呼攔截JS對話框alert()、confirm( )、prompt()訊息
這種方式的優點:不存在方式1的漏洞;缺點:JS取得Android方法的回傳值複雜。
26、JAVA GC原理
垃圾收集演算法的核心思想是:對虛擬機器可用記憶體空間,即堆空間中的物件進行識別,如果物件正在被引用,那麼稱其為存活對象
,反之,如果對像不再被引用,則為垃圾對象,可以回收其佔據的空間,用於再分配。垃圾收集演算法的選擇和垃圾收集系統參數的合理調節直接影響系統性能。
27、ANR
ANR全名Application Not Responding, 也就是"應用無回應". 當操作在一段時間內系統無法處理時, 系統層面會彈出上圖那樣的ANR對話框.
產生原因:
(1)5s內無法回應使用者輸入事件(例如鍵盤輸入, 觸控螢幕等).
(2)BroadcastReceiver在10s內部無法結束
(3)Service 20s內無法結束(低機率)
解決方式:
(1)不要在主執行緒中做耗時的操作,而應放在子線程中來實現。如onCreate()和onResume()裡盡可能少的去做創建操作。
(2)應用程式應該避免在BroadcastReceiver裡做耗時的操作或計算。
(3)避免在Intent Receiver裡啟動一個Activity,因為它會創造一個新的畫面,並從目前使用者正在執行的程式中搶奪焦點。
(4)service是運行在主執行緒的,所以在service中做耗時操作,必須放在子執行緒中。
28、設計模式
此處延伸:Double Check的寫法被要求寫出來。
單一範例模式:分為惡漢式與懶漢式
惡漢式:
public class Singleton { private static Singleton instance = new Singleton(); public static Singleton getInstance() { return instance ; } }
懶漢式:
public class Singleton02 { private static Singleton02 instance; public static Singleton02 getInstance() { if (instance == null) { synchronized (Singleton02.class) { if (instance == null) { instance = new Singleton02(); } } } return instance; } }
29、RxJava
30、MVP,MVC,MVVM
此處延伸:手寫mvp例子,與mvc之間的區別,mvp的優勢
MVP模式,對應著Model--業務邏輯和實體模型,view--對應著activity,負責View的繪製以及與用戶交互,Presenter--負責View和Model之間的交互,MVP模式是在MVC模式的基礎上,將Model與View徹底分離使得專案的耦合性更低,在Mvc中專案中的activity對應著mvc中的C--Controllor,而專案中的邏輯處理都是在這個C中處理,同時View與Model之間的交互,也是也就是說,mvc中所有的邏輯交互和使用者交互,都是放在Controllor中,也就是activity。 View和model是可以直接通訊的。而MVP模式則是分離的更加徹底,分工更加明確Model--業務邏輯和實體模型,view--負責與用戶交互,Presenter 負責完成View於Model間的交互,MVP和MVC最大的區別是MVC中是允許Model和View進行互動的,而MVP中很明顯,Model與View之間的互動由Presenter完成。還有一點就是Presenter與View之間的互動是透過介面的
31、手寫演算法(選擇冒泡必須會)
32、JNI
(1 )安裝與下載Cygwin,下載Android NDK
(2)在ndk專案中JNI介面的設計
(3)使用C/C 實作本機方法
(4 )JNI產生動態連結庫.so檔案
(5)將動態連結庫複製到java工程,在java工程中調用,執行java工程即可
33、RecyclerView和ListView的區別
RecyclerView可以完成ListView,GridView的效果,也可以完成瀑布流的效果。同時也可以設定清單的滾動方向(垂直或水平);
RecyclerView中view的複用不需要開發者自己寫程式碼,系統已經幫封裝完成了。
RecyclerView可以進行局部刷新。
RecyclerView提供了API來實現item的動畫效果。
在性能上:
如果需要頻繁的刷新數據,需要添加動畫,則RecyclerView有較大的優勢。
如果只是作為清單展示,則兩者差異並不是很大。
34、Universal-ImageLoader,Picasso,Fresco,Glide對比
Fresco 是Facebook 推出的開源圖片快取工具,主要特點包括:兩個記憶體快取加上Native 快取構成了三級緩存,
優點:
1. 圖片儲存在安卓系統的匿名共享內存, 而不是虛擬機的堆內存中, 圖片的中間緩衝數據也存放在本地堆內存, 所以, 應用程式有更多的記憶體使用, 不會因為圖片載入而導致oom, 同時也減少垃圾回收器頻繁調用回收Bitmap 導致的介面卡頓, 效能更高。
2. 漸進式載入 JPEG 圖片, 支援圖片從模糊到清晰載入。
3. 圖片可以任意的中心點顯示在 ImageView, 而不僅僅是圖片的中心。
4. JPEG 圖片改變大小也是在 native 進行的, 不是在虛擬機的堆內存, 同樣減少 OOM。
5. 很好的支援 GIF 圖片的顯示。
缺點:
1. 框架較大, 影響Apk 體積
2. 使用較繁瑣
Universal-ImageLoader:(估計由於HttpClient被Google放棄,作者就放棄維護這個框架)
優點:
1.支援下載進度監聽
2.可以在View 滾動中暫停圖片加載,透過PauseOnScrollListener 介面可以在View 捲動中暫停圖片載入。
3.預設實作多種記憶體快取演算法這幾個圖片快取都可以配置快取演算法,不過ImageLoader 預設實作了較多快取演算法,如Size 最大先刪除、使用最少先刪除、最近最少使用、先進先刪除、時間最長先刪除等。
4.支援本機快取檔案名稱規則定義
Picasso 優點
1. 自備統計監控功能。支援圖片快取使用的監控,包括快取命中率、已使用記憶體大小、節省的流量等。
2.支援優先權處理。每次任務排程前會選擇優先順序高的任務,例如 App 頁面中 Banner 的優先權高於 Icon 時就很適用。
3.支援延遲到圖片尺寸計算完成載入
4.支援飛航模式、並發執行緒數依網路類型而變。手機切換到飛航模式或網路類型變換時會自動調整線程池最大並發數,例如 wifi 最大並發為 4,4g 為 3,3g 為 2。 這裡 Picasso 根據網路類型來決定最大並發數,而不是 CPU 核數。
5.「無」本機快取。無」本地緩存,不是說沒有本地緩存,而是Picasso 自己沒有實現,交給了Square 的另外一個網絡庫okhttp 去實現,這樣的好處是可以透過請求Response Header 中的Cache-Control 及Expired 控制圖片的過期時間。
Glide 優點
1. 不僅可以進行圖片快取還可以快取媒體檔案。Glide 不僅是一個圖片緩存,它支援Gif、WebP、縮圖。甚至是Video ,所以更該當做一個媒體快取。
2. 支援優先權處理。
3. 與Activity/Fragment 生命週期一致,支援trimMemory。Glide 對每個context 保持一個RequestManager ,透過FragmentTransaction 保持與Activity/Fragment 生命週期一致,並且有對應的trimMemory 介面實作可供呼叫。
4.支援okhttp、Volley。Glide 預設透過UrlConnection 取得數據,可以配合okhttp 或是Volley 使用配合okhttp 或是Volley 使用配合okhttp 。實際ImageLoader、Picasso 也都支援okhttp、Volley。
5. 記憶體友善。Glide 的記憶體快取有個active 的設計,從記憶體快取取資料時,不像一般的實作用get,而是用remove,再將這個快取資料放到一個value 為軟引用的activeResources map 中,併計數引用數,在圖片載入完成後進行判斷,如果引用計數為空則回收。記憶體快取較小圖片,Glide以url、view_width、view_height、螢幕的解析度等做為聯合key,將處理後的圖片快取在記憶體快取中,而不是原始圖片以節省大小與Activity/Fragment 生命週期一致,支援trimMemory。圖片預設使用預設RGB_565 而不是ARGB_888,雖然清晰度差些,但圖片更小,也可配置到ARGB_888。
6.Glide 可以透過signature 或不使用本地快取支援url 過期
42、 Xutils, OKhttp, Volley, Retrofit對比
Xutils這個框架非常全面,可以進行網路請求,可以進行圖片載入處理,可以資料儲存,還可以對view進行註解,使用這個框架非常方便,但是缺點也是非常明顯的,使用這個項目,會導致項目對這個框架依賴非常的嚴重,一旦這個框架出現問題,那麼對項目來說影響非常大的。 、
OKhttp:Android開發中是可以直接使用現成的api進行網路請求的。就是使用HttpClient,HttpUrlConnection來操作。 okhttp針對Java和Android程序,封裝的一個高效能的http請求庫,支援同步,異步,而且okhttp又封裝了執行緒池,封裝了資料轉換,封裝了參數的使用,錯誤處理等。 API使用起來更方便的。但是我們在專案中使用的時候仍然需要自己在做一層封裝,這樣才能使用的更加的順手。
Volley:Volley是Google官方出的一套小而巧的非同步請求庫,該框架封裝的擴展性很強,支援HttpClient、HttpUrlConnection, 甚至支援OkHttp,而且Volley裡面也封裝了ImageLoader,所以如果你願意你甚至不需要使用圖片載入框架,不過這塊功能沒有一些專門的圖片載入框架強大,對於簡單的需求可以使用,稍複雜點的需求還是需要用到專門的圖片載入框架。 Volley也有缺陷,例如不支援post大數據,所以不適合上傳檔案。不過Volley設計的初衷本身也就是為頻繁的、數據量小的網路請求而生。
Retrofit:Retrofit是Square公司出品的預設基於OkHttp封裝的一套RESTful網路請求框架,RESTful是目前流行的一套api設計的風格, 並不是標準。 Retrofit的封裝可以說是很強大,裡面涉及到一堆的設計模式,可以透過註解直接配置請求,可以使用不同的http客戶端,雖然預設是用http ,可以使用不同Json Converter 來序列化數據,同時提供對RxJava的支持,使用Retrofit OkHttp RxJava Dagger2 可以說是目前比較潮的一套框架,但需要有比較高的門檻。
Volley VS OkHttp
Volley的優點在於封裝的更好,而使用OkHttp你需要有足夠的能力再進行一次封裝。而OkHttp的優勢在於效能更高,因為 OkHttp是基於NIO和Okio ,所以效能上要比 Volley更快。 IO 和NIO這兩個都是Java中的概念,如果我從硬碟讀取數據,第一種方式就是程式一直等,數據讀完後才能繼續操作這種是最簡單的也叫阻塞式IO,還有一種是你讀你的,程式接著往下執行,等資料處理完你再來通知我,然後再處理回調。而第二種就是 NIO 的方式,非阻塞式, 所以NIO當然要比IO的性能要好了,而 Okio是 Square 公司基於IO和NIO基礎上做的一個更簡單、高效處理數據流的一個庫。理論上如果Volley和OkHttp比較的話,更傾向於使用 Volley,因為Volley內部同樣支持使用OkHttp,這點OkHttp的性能優勢就沒了, 而且 Volley 本身封裝的也更易用,擴展性更好些。
OkHttp VS Retrofit
毫無疑問,Retrofit 預設是基於 OkHttp 而做的封裝,這點來說沒有可比性,肯定首選 Retrofit。
Volley VS Retrofit
這兩個庫都做了不錯的封裝,但Retrofit解耦的更徹底,尤其Retrofit2.0出來,Jake對之前1.0設計不合理的地方做了大量重構, 職責更細分,而且Retrofit默認使用OkHttp,性能上也要比Volley佔優勢,再有如果你的項目如果採用了RxJava ,那更該使用 Retrofit 。所以這兩個函式庫相比,Retrofit更有優勢,在能掌握兩個框架的前提下該優先使用 Retrofit。但Retrofit門檻要比Volley稍高些,要理解他的原理,各種用法,想徹底搞明白還是需要花些功夫的,如果你對它一知半解,那還是建議在商業項目使用Volley吧。
Java
1、執行緒中sleep和wait的區別
(1)這兩個方法來自不同的類,sleep是來自Thread,wait是來自Object;
(2)sleep方法沒有釋放鎖定,而wait方法釋放了鎖定。
(3)wait,notify,notifyAll只能在同步控制方法或同步控制區塊裡面使用,而sleep可以在任何地方使用。
2、Thread中的start()和run()方法有什麼區別
start()方法是用來啟動新建立的線程,而start()內部呼叫了run( )方法,這和直接呼叫run()方法是不一樣的,如果直接呼叫run()方法,
則和普通的方法沒有什麼不同。
3、關鍵字final和static是怎麼使用的。
final:
1、final變數即為常數,只能賦值一次。
2、final方法不能被子類別重寫。
3、final類別不能被繼承。
static:
1、static變數:對於靜態變數在記憶體中只有一個拷貝(節省記憶體),JVM只為靜態分配一次內存,
在載入類的過程中完成靜態變數的記憶體分配,可用類別名稱直接存取(方便),當然也可以透過物件來存取(但是這是不建議的)。
2、static程式碼區塊
static程式碼區塊是類別載入時,初始化自動執行的。
3、static方法
static方法可以直接透過類別名稱調用,任何的實例也都可以調用,因此static方法中不能用this和super關鍵字,
不能直接存取所屬類別的實例變數和實例方法(就是不帶static的成員變數和成員成員方法),只能存取所屬類別的靜態成員變數和成員方法。
4、String,StringBuffer,StringBuilder區別
1、三者在執行速度上:StringBuilder > StringBuffer > String (由於String是常數,不可改變,拼接時會重新創建新的對象)。
2、StringBuffer是線程安全的,StringBuilder是線程不安全的。 (由於StringBuffer有緩衝區)
5、Java中重載和重寫的區別:
1、重載:一個類別中可以有多個相同方法名稱的,但是參數類型和個數都不一樣。這是重載。
2、重寫:子類別繼承父類,則子類別可以透過實作父類別中的方法,從而新的方法把父類別舊的方法覆寫。
6、Http https區別
此處延伸:https的實作原理
1、https協定需要到ca申請證書,一般免費證書較少,因而需要一定費用。
2、http是超文本傳輸協議,訊息是明文傳輸,https則是具有安全性的ssl加密傳輸協議。
3、http和https使用的是完全不同的連接方式,用的連接埠也不一樣,前者是80,後者是443。
4、http的連接很簡單,是無狀態的;HTTPS協議是由SSL HTTP協議構建的可進行加密傳輸、身份認證的網路協議,比http協議安全。
https實作原則:
(1)客戶使用https的URL存取Web伺服器,要求與Web伺服器建立SSL連線。
(2)Web伺服器收到客戶端請求後,會將網站的憑證資訊(憑證中包含公鑰)傳送一份給客戶端。
(3)客戶端的瀏覽器與Web伺服器開始協商SSL連線的安全等級,也就是資訊加密的等級。
(4)用戶端的瀏覽器根據雙方同意的安全等級,建立會話金鑰,然後利用網站的公鑰將會話金鑰加密,並傳送給網站。
(5)Web伺服器利用自己的私鑰解密出會話金鑰。
(6)Web伺服器利用會話金鑰加密與客戶端之間的通訊。
7、Http位於TCP/IP模型中的第幾層?為什麼說Http是可靠的資料傳輸協定?
tcp/ip的五層模型:
從下到上:物理層->資料鏈結層->網路層->傳輸層->應用層
其中tcp/ip位於模型中的網路層,處於同一層的還有ICMP(網路控制資訊協定)。 http位於模型中的應用層
由於tcp/ip是面向連接的可靠協議,而http是在傳輸層基於tcp/ip協議的,所以說http是可靠的數據傳輸協議。
8、HTTP連結的特性
HTTP連線最顯著的特點是用戶端發送的每次要求都需要伺服器回送回應,在請求結束後,會主動釋放連線。
從建立連線到關閉連線的過程稱為「一次連線」。
9、TCP和UDP的區別
tcp是面向連接的,由於tcp連接需要三次握手,所以能夠最低限度的降低風險,並保證連接的可靠性。
udp 不是面向連接的,udp建立連接前不需要與物件建立連接,無論是發送還是接收,都沒有發送確認訊號。所以說udp是不可靠的。
由於udp不需要進行確認連接,使得UDP的開銷更小,傳輸速率更高,所以實時行更好。
10、Socket建立網路連線的步驟
建立Socket連線至少需要一對套接字,其中一個運行與客戶端--ClientSocket,一個運行於服務端--ServiceSocket
1、伺服器監聽:伺服器端套接字並不定位特定的客戶端套接字,而是處於等待連線的狀態,即時監控網路狀態,等待客戶端的連線要求。
2、客戶端請求:指客戶端的套接字提出連線請求,要連線的目標是伺服器端的套接字。注意:客戶端的套接字必須描述他要連接的伺服器的套接字,
指出伺服器套接字的位址和連接埠號,然後就像伺服器端套接字提出連接請求。
3、連線確認:當伺服器端套接字監聽到客戶端套接字的連線請求時,就回應客戶端套接字的請求,建立一個新的線程,把伺服器端套接字的描述
發給客戶端,一旦客戶端確認了此描述,雙方就正式建立連線。而服務端套接字則繼續處於監聽狀態,繼續接收其他客戶端套接字的連線請求。
11、Tcp/IP三次握手,四次揮手
#
以上是中高級的Android面試題(含答案)的詳細內容。更多資訊請關注PHP中文網其他相關文章!