面試,無非都是問上面這些問題(挺多的 - -!),聘請中高級的安卓開發會往深的去問,而且會問一延伸二。如下我先提出幾點重點,是面試官基本必問的問題,請必定要去了解!javascript
急急忙忙投簡歷,趕面試,還不如沉澱一兩天時間,再過一遍以上內容。想穩妥拿到一個offer,最好能理解實現原理,而且知道使用場景了。不要去背!要去理解!面試官聽了一天這些內容是很厭倦的,最好能說出一些本身的看法。前端
Java中引用類型的區別,具體的使用場景java
Java中引用類型分爲四類:強引用、軟引用、弱引用、虛引用。面試
強引用:強引用指的是經過new對象建立的引用,垃圾回收器即便是內存不足也不會回收強引用指向的對象。算法
軟引用:軟引用是經過SoftRefrence實現的,它的生命週期比強引用短,在內存不足,拋出OOM以前,垃圾回收器會回收軟引用引用的對象。軟引用常見的使用場景是存儲一些內存敏感的緩存,當內存不足時會被回收。設計模式
弱引用:弱引用是經過WeakRefrence實現的,它的生命週期比軟引用還短,GC只要掃描到弱引用的對象就會回收。弱引用常見的使用場景也是存儲一些內存敏感的緩存。數組
虛引用:虛引用是經過FanttomRefrence實現的,它的生命週期最短,隨時可能被回收。若是一個對象只被虛引用引用,咱們沒法經過虛引用來訪問這個對象的任何屬性和方法。它的做用僅僅是保證對象在finalize後,作某些事情。虛引用常見的使用場景是跟蹤對象被垃圾回收的活動,當一個虛引用關聯的對象被垃圾回收器回收以前會收到一條系統通知。瀏覽器
Exception和Error都繼承於Throwable,在Java中,只有Throwable類型的對象才能被throw或者catch,它是異常處理機制的基本組成類型。緩存
Exception和Error體現了Java對不一樣異常狀況的分類。Exception是程序正常運行中,能夠預料的意外狀況,可能而且應該被捕獲,進行相應的處理。安全
Error是指在正常狀況下,不大可能出現的狀況,絕大部分Error都會使程序處於非正常、不可恢復的狀態。既然是非正常,因此不便於也不須要捕獲,常見的OutOfMemoryError就是Error的子類。
Exception又分爲checked Exception和unchecked Exception。checked Exception在代碼裏必須顯式的進行捕獲,這是編譯器檢查的一部分。unchecked Exception也就是運行時異常,相似空指針異常、數組越界等,一般是能夠避免的邏輯錯誤,具體根據需求來判斷是否須要捕獲,並不會在編譯器強制要求。
通常提到volatile,就不得不提到內存模型相關的概念。咱們都知道,在程序運行中,每條指令都是由CPU執行的,而指令的執行過程當中,勢必涉及到數據的讀取和寫入。程序運行中的數據都存放在主存中,這樣會有一個問題,因爲CPU的執行速度是要遠高於主存的讀寫速度,因此直接從主存中讀寫數據會下降CPU的效率。爲了解決這個問題,就有了高速緩存的概念,在每一個CPU中都有高速緩存,它會事先從主存中讀取數據,在CPU運算以後在合適的時候刷新到主存中。
這樣的運行模式在單線程中是沒有任何問題的,但在多線程中,會致使緩存一致性的問題。舉個簡單的例子:i=i+1 ,在兩個線程中執行這句代碼,假設i的初始值爲0。咱們指望兩個線程運行後獲得2,那麼有這樣的一種狀況,兩個線程都從主存中讀取i到各自的高速緩存中,這時候兩個線程中的i都爲0。在線程1執行完畢獲得i=1,將之刷新到主存後,線程2開始執行,因爲線程2中的i是高速緩存中的0,因此在執行完線程2以後刷新到主存的i仍舊是1。
因此這就致使了對共享變量的緩存一致性的問題,那麼爲了解決這個問題,提出了緩存一致性協議:當CPU在寫數據時,若是發現操做的是共享變量,它會通知其餘CPU將它們內部的這個共享變量置爲無效狀態,當其餘CPU讀取緩存中的共享變量時,發現這個變量是無效的,它會重新從主存中讀取最新的值。
在Java的多線程開發中,有三個重要概念:原子性、可見性、有序性。
原子性:一個或多個操做要麼都不執行,要麼都執行。
可見性:一個線程中對共享變量(類中的成員變量或靜態變量)的修改,在其餘線程當即可見。
有序性:程序執行的順序按照代碼的順序執行。
把一個變量聲明爲volatile,其實就是保證了可見性和有序性。
可見性我上面已經說過了,在多線程開發中是頗有必要的。這個有序性仍是得說一下,爲了執行的效率,有時候會發生指令重排,這在單線程中指令重排以後的輸出與咱們的代碼邏輯輸出仍是一致的。但在多線程中就可能發生問題,volatile在必定程度上能夠避免指令重排。
volatile的原理是在生成的彙編代碼中多了一個lock前綴指令,這個前綴指令至關於一個內存屏障,這個內存屏障有3個做用:
http 狀態碼
http 與 https 的區別?https 是如何工做的?
http是超文本傳輸協議,而https能夠簡單理解爲安全的http協議。https經過在http協議下添加了一層ssl協議對數據進行加密從而保證了安全。https的做用主要有兩點:創建安全的信息傳輸通道,保證數據傳輸安全;確認網站的真實性。
http與https的區別主要以下:
https的工做流程
提到https的話首先要說到加密算法,加密算法分爲兩類:對稱加密和非對稱加密。
對稱加密:加密和解密用的都是相同的祕鑰,優勢是速度快,缺點是安全性低。常見的對稱加密算法有DES、AES等等。
非對稱加密:非對稱加密有一個祕鑰對,分爲公鑰和私鑰。通常來講,私鑰本身持有,公鑰能夠公開給對方,優勢是安全性比對稱加密高,缺點是數據傳輸效率比對稱加密低。採用公鑰加密的信息只有對應的私鑰能夠解密。常見的非對稱加密包括RSA等。
在正式的使用場景中通常都是對稱加密和非對稱加密結合使用,使用非對稱加密完成祕鑰的傳遞,而後使用對稱祕鑰進行數據加密和解密。兩者結合既保證了安全性,又提升了數據傳輸效率。
https的具體流程以下:
Android面試題
進程間通訊的方式有哪幾種
AIDL 、廣播、文件、socket、管道
廣播靜態註冊和動態註冊的區別
Android性能優化工具使用(這個問題建議配合Android中的性能優化)
Android中經常使用的性能優化工具包括這些:Android Studio自帶的Android Profiler、LeakCanary、BlockCanary
Android自帶的Android Profiler其實就很好用,Android Profiler能夠檢測三個方面的性能問題:CPU、MEMORY、NETWORK。
LeakCanary是一個第三方的檢測內存泄漏的庫,咱們的項目集成以後LeakCanary會自動檢測應用運行期間的內存泄漏,並將之輸出給咱們。
BlockCanary也是一個第三方檢測UI卡頓的庫,項目集成後Block也會自動檢測應用運行期間的UI卡頓,並將之輸出給咱們。
Android中的類加載器
PathClassLoader,只能加載系統中已經安裝過的apk
DexClassLoader,能夠加載jar/apk/dex,能夠從SD卡中加載未安裝的apk
Android中的動畫有哪幾類,它們的特色和區別是什麼
Android中動畫大體分爲3類:幀動畫、補間動畫(View Animation)、屬性動畫(Object Animation)。
補間動畫與屬性動畫的區別:
Handler機制
說到Handler,就不得不提與之密切相關的這幾個類:Message、MessageQueue,Looper。
prepare()。這個方法作了兩件事:首先經過ThreadLocal.get()獲取當前線程中的Looper,若是不爲空,則會拋出一個RunTimeException,意思是一個線程不能建立2個Looper。若是爲null則執行下一步。第二步是建立了一個Looper,並經過ThreadLocal.set(looper)。將咱們建立的Looper與當前線程綁定。這裏須要提一下的是消息隊列的建立其實就發生在Looper的構造方法中。
loop()。這個方法開啓了整個事件機制的輪詢。它的本質是開啓了一個死循環,不斷的經過MessageQueue的next()方法獲取消息。拿到消息後會調用msg.target.dispatchMessage()來作處理。其實咱們在說到Message的時候提到過,msg.target其實就是發送這個消息的handler。這句代碼的本質就是調用handler的dispatchMessage()。
發送消息。其實發送消息除了sendMessage以外還有sendMessageDelayed和post以及postDelayed等等不一樣的方式。但它們的本質都是調用了sendMessageAtTime。在sendMessageAtTime這個方法中調用了enqueueMessage。在enqueueMessage這個方法中作了兩件事:經過msg.target = this實現了消息與當前handler的綁定。而後經過queue.enqueueMessage實現了消息入隊。
處理消息。消息處理的核心其實就是dispatchMessage()這個方法。這個方法裏面的邏輯很簡單,先判斷msg.callback是否爲null,若是不爲空則執行這個runnable。若是爲空則會執行咱們的handleMessage方法。
Android性能優化
Android中的性能優化在我看來分爲如下幾個方面:內存優化、佈局優化、網絡優化、安裝包優化。
內存優化:下一個問題就是。
佈局優化:佈局優化的本質就是減小View的層級。常見的佈局優化方案以下
網絡優化:常見的網絡優化方案以下
安裝包優化:安裝包優化的核心就是減小apk的體積,常見的方案以下
Android內存優化
Android的內存優化在我看來分爲兩點:避免內存泄漏、擴大內存,其實就是開源節流。
其實內存泄漏的本質就是較長生命週期的對象引用了較短生命週期的對象。
常見的內存泄漏:
擴大內存,爲何要擴大咱們的內存呢?有時候咱們實際開發中不可避免的要使用不少第三方商業的SDK,這些SDK其實有好有壞,大廠的SDK可能內存泄漏會少一些,但一些小廠的SDK質量也就不太靠譜一些。那應對這種咱們沒法改變的狀況,最好的辦法就是擴大內存。
擴大內存一般有兩種方法:一個是在清單文件中的Application下添加largeHeap=」true」這個屬性,另外一個就是同一個應用開啓多個進程來擴大一個應用的總內存空間。第二種方法其實就很常見了,比方說我使用過個推的SDK,個推的Service其實就是處在另一個單獨的進程中。
Android中的內存優化總的來講就是開源和節流,開源就是擴大內存,節流就是避免內存泄漏。
Binder機制
在Linux中,爲了不一個進程對其餘進程的干擾,進程之間是相互獨立的。在一個進程中其實還分爲用戶空間和內核空間。這裏的隔離分爲兩個部分,進程間的隔離和進程內的隔離。
既然進程間存在隔離,那其實也是存在着交互。進程間通訊就是IPC,用戶空間和內核空間的通訊就是系統調用。
Linux爲了保證獨立性和安全性,進程之間不能直接相互訪問,Android是基於Linux的,因此也是須要解決進程間通訊的問題。
其實Linux進程間通訊有不少方式,好比管道、socket等等。爲何Android進程間通訊採用了Binder而不是Linux已有的方式,主要是有這麼兩點考慮:性能和安全
性能。在移動設備上對性能要求是比較嚴苛的。Linux傳統的進程間通訊好比管道、socket等等進程間通訊是須要複製兩次數據,而Binder則只須要一次。因此Binder在性能上是優於傳統進程通訊的。
安全。傳統的Linux進程通訊是不包含通訊雙方的身份驗證的,這樣會致使一些安全性問題。而Binder機制自帶身份驗證,從而有效的提升了安全性。
Binder是基於CS架構的,有四個主要組成部分。
Binder機制主要的流程是這樣的:
LruCache的原理
LruCache的核心原理就是對LinkedHashMap的有效利用,它的內部存在一個LinkedHashMap成員變量。值得咱們關注的有四個方法:構造方法、get、put、trimToSize。
構造方法:在LruCache的構造方法中作了兩件事,設置了maxSize、建立了一個LinkedHashMap。這裏值得注意的是LruCache將LinkedHashMap的accessOrder設置爲了true,accessOrder就是遍歷這個LinkedHashMap的輸出順序。true表明按照訪問順序輸出,false表明按添加順序輸出,由於一般都是按照添加順序輸出,因此accessOrder這個屬性默認是false,但咱們的LruCache須要按訪問順序輸出,因此顯式的將accessOrder設置爲true。
get方法:本質上是調用LinkedHashMap的get方法,因爲咱們將accessOrder設置爲了true,因此每調用一次get方法,就會將咱們訪問的當前元素放置到這個LinkedHashMap的尾部。
put方法:本質上也是調用了LinkedHashMap的put方法,因爲LinkedHashMap的特性,每調用一次put方法,也會將新加入的元素放置到LinkedHashMap的尾部。添加以後會調用trimToSize方法來保證添加後的內存不超過maxSize。
trimToSize方法:trimToSize方法的內部實際上是開啓了一個while(true)的死循環,不斷的從LinkedHashMap的首部刪除元素,直到刪除以後的內存小於maxSize以後使用break跳出循環。
其實到這裏咱們能夠總結一下,爲何這個算法叫 最近最少使用 算法呢?原理很簡單,咱們的每次put或者get均可以看作一次訪問,因爲LinkedHashMap的特性,會將每次訪問到的元素放置到尾部。當咱們的內存達到閾值後,會觸發trimToSize方法來刪除LinkedHashMap首部的元素,直到當前內存小於maxSize。爲何刪除首部的元素,緣由很明顯:咱們最近常常訪問的元素都會放置到尾部,那首部的元素確定就是 最近最少使用 的元素了,所以當內存不足時應當優先刪除這些元素。
DiskLruCache原理
設計一個圖片的異步加載框架
設計一個圖片加載框架,確定要用到圖片加載的三級緩存的思想。三級緩存分爲內存緩存、本地緩存和網絡緩存。
內存緩存:將Bitmap緩存到內存中,運行速度快,可是內存容量小。
本地緩存:將圖片緩存到文件中,速度較慢,但容量較大。
網絡緩存:從網絡獲取圖片,速度受網絡影響。
若是咱們設計一個圖片加載框架,流程必定是這樣的:
上面是一些基本的概念,若是是具體的代碼實現的話,大概須要這麼幾個方面的文件:
Android中的事件分發機制
在咱們的手指觸摸到屏幕的時候,事件實際上是經過 Activity -> ViewGroup -> View 這樣的流程到達最後響應咱們觸摸事件的View。
說到事件分發,必不可少的是這幾個方法:dispatchTouchEvent()、onInterceptTouchEvent()、onTouchEvent。接下來就按照 Activity -> ViewGroup -> View 的流程來大體說一下事件分發機制。
咱們的手指觸摸到屏幕的時候,會觸發一個Action_Down類型的事件,當前頁面的Activity會首先作出響應,也就是說會走到Activity的dispatchTouchEvent()方法內。在這個方法內部簡單來講是這麼一個邏輯:
這個邏輯很好理解,getWindow().superDispatchTouchEvent()若是返回true表明當前事件已經被處理,無需調用本身的onTouchEvent;不然表明事件並無被處理,須要Activity本身處理,也就是調用本身的onTouchEvent。
複製代碼
getWindow()方法返回了一個Window類型的對象,這個咱們都知道,在Android中,PhoneWindow是Window的惟一實現類。因此這句本質上是調用了PhoneWindow中的superDispatchTouchEvent()。
而在PhoneWindow的這個方法中實際調用了mDecor.superDispatchTouchEvent(event)。這個mDecor就是DecorView,它是FrameLayout的一個子類,在DecorView中的superDispatchTouchEvent()中調用的是super.dispatchTouchEvent()。到這裏就很明顯了,DecorView是一個FrameLayout的子類,FrameLayout是一個ViewGroup的子類,本質上調用的仍是ViewGroup的dispatchTouchEvent()。
分析到這裏,咱們的事件已經從Activity傳遞到了ViewGroup,接下來咱們來分析下ViewGroup中的這幾個事件處理方法。
在ViewGroup中的dispatchTouchEvent()中的邏輯大體以下:
一般狀況下ViewGroup的onInterceptTouchEvent()都返回false,也就是不攔截。這裏須要注意的是事件序列,好比Down事件、Move事件……Up事件,從Down到Up是一個完整的事件序列,對應着手指從按下到擡起這一系列的事件,若是ViewGroup攔截了Down事件,那麼後續事件都會交給這個ViewGroup的onTouchEvent。若是ViewGroup攔截的不是Down事件,那麼會給以前處理這個Down事件的View發送一個Action_Cancel類型的事件,通知子View這個後續的事件序列已經被ViewGroup接管了,子View恢復以前的狀態便可。
這裏舉一個常見的例子:在一個Recyclerview鐘有不少的Button,咱們首先按下了一個button,而後滑動一段距離再鬆開,這時候Recyclerview會跟着滑動,並不會觸發這個button的點擊事件。這個例子中,當咱們按下button時,這個button接收到了Action_Down事件,正常狀況下後續的事件序列應該由這個button處理。但咱們滑動了一段距離,這時Recyclerview察覺到這是一個滑動操做,攔截了這個事件序列,走了自身的onTouchEvent()方法,反映在屏幕上就是列表的滑動。而這時button仍然處於按下的狀態,因此在攔截的時候須要發送一個Action_Cancel來通知button恢復以前狀態。
事件分發最終會走到View的dispatchTouchEvent()中。在View的dispatchTouchEvent()中沒有onInterceptTouchEvent(),這也很容易理解,View不是ViewGroup,不會包含其餘子View,因此也不存在攔截不攔截這一說。忽略一些細節,View的dispatchTouchEvent()中直接return了本身的onTouchEvent()。若是onTouchEvent()返回true表明事件被處理,不然未處理的事件會向上傳遞,直到有View處理了事件或者一直沒有處理,最終到達了Activity的onTouchEvent()終止。
這裏常常有人問onTouch和onTouchEvent的區別。首先,這兩個方法都在View的dispatchTouchEvent()中,是這麼一個邏輯:
View的繪製流程
視圖繪製的起點在ViewRootImpl類的performTraversals()方法,在這個方法內實際上是按照順序依次調用了mView.measure()、mView.layout()、mView.draw()
View的繪製流程分爲3步:測量、佈局、繪製,分別對應3個方法measure、layout、draw。
測量階段。measure方法會被父View調用,在measure方法中作一些優化和準備工做後會調用onMeasure方法進行實際的自我測量。onMeasure方法在View和ViewGroup作的事情是不同的:
佈局階段。layout方法會被父View調用,layout方法會保存父View傳進來的尺寸和位置,並調用onLayout進行實際的內部佈局。onLayout在View和ViewGroup中作的事情也是不同的:
繪製階段。draw方法會作一些調度工做,而後會調用onDraw方法進行View的自我繪製。draw方法的調度流程大體是這樣的:
Android源碼中常見的設計模式以及本身在開發中經常使用的設計模式
Android與js是如何交互的
在Android中,Android與js的交互分爲兩個方面:Android調用js裏的方法、js調用Android中的方法。
Android調js。Android調js有兩種方法:
js調Android。js調Android有三種方法:
熱修復原理
Activity啓動過程
SparseArray原理
SparseArray,一般來說是Android中用來替代HashMap的一個數據結構。
準確來說,是用來替換key爲Integer類型,value爲Object類型的HashMap。須要注意的是SparseArray僅僅實現了Cloneable接口,因此不能用Map來聲明。
從內部結構來說,SparseArray內部由兩個數組組成,一個是int[]類型的mKeys,用來存放全部的鍵;另外一個是Object[]類型的mValues,用來存放全部的值。
最多見的是拿SparseArray跟HashMap來作對比,因爲SparseArray內部組成是兩個數組,因此佔用內存比HashMap要小。咱們都知道,增刪改查等操做都首先須要找到相應的鍵值對,而SparseArray內部是經過二分查找來尋址的,效率很明顯要低於HashMap的常數級別的時間複雜度。提到二分查找,這裏還須要提一下的是二分查找的前提是數組已是排好序的,沒錯,SparseArray中就是按照key進行升序排列的。
綜合起來來講,SparseArray所佔空間優於HashMap,而效率低於HashMap,是典型的時間換空間,適合較小容量的存儲。
從源碼角度來講,我認爲須要注意的是SparseArray的remove()、put()和gc()方法。
圖片加載如何避免OOM
咱們知道內存中的Bitmap大小的計算公式是:長所佔像素
每一個像素所佔內存。想避免OOM有兩種方法:等比例縮小長寬、減小每一個像素所佔的內存。
大圖加載
加載高清大圖,好比清明上河圖,首先屏幕是顯示不下的,並且考慮到內存狀況,也不可能一次性所有加載到內存。這時候就須要局部加載了,Android中有一個負責局部加載的類:BitmapRegionDecoder。使用方法很簡單,經過BitmapRegionDecoder.newInstance()建立對象,以後調用decodeRegion(Rect rect, BitmapFactory.Options options)便可。第一個參數rect是要顯示的區域,第二個參數是BitmapFactory中的內部類Options。
Android三方庫的源碼分析
因爲源碼分析篇幅太大,因此這裏之貼出個人源碼分析的連接(掘金)。
OkHttp
OkHttp源碼分析
Retrofit
Retrofit源碼分析1
Retrofit源碼分析2
Retrofit源碼分析3
RxJava
RxJava源碼分析
Glide
Glide源碼分析
EventBus
EventBus源碼分析
大體是這麼一個流程:
register:
post:
unregister:
數據結構與算法
手寫快排
手寫歸併排序
手寫堆以及堆排序
說一下排序算法的區別(時間複雜度和空間複雜度)
Android面試資料和源碼解析視頻主頁查詢