2019 必看 Android 高級面試題總結

說下你所知道的設計模式與使用場景

a.建造者模式:將一個複雜對象的構建與它的表示分離,使得一樣的構建過程能夠建立不一樣的表示。使用場景好比最多見的AlertDialog,拿咱們開發過程當中舉例,好比Camera開發過程當中,可能須要設置一個初始化的相機配置,設置攝像頭方向,閃光燈開閉,成像質量等等,這種場景下就可使用建造者模式前端

裝飾者模式:動態的給一個對象添加一些額外的職責,就增長功能來講,裝飾模式比生成子類更爲靈活。裝飾者模式能夠在不改變原有類結構的狀況下曾強類的功能,好比Java中的BufferedInputStream 包裝FileInputStream,舉個開發中的例子,好比在咱們現有網絡框架上須要增長新的功能,那麼再包裝一層便可,裝飾者模式解決了繼承存在的一些問題,好比多層繼承代碼的臃腫,使代碼邏輯更清晰觀察者模式:代理模式:門面模式:單例模式:生產者消費者模式:java

java語言的特色與OOP思想

這個經過對比來描述,好比面向對象和麪向過程的對比,針對這兩種思想的對比,還能夠舉個開發中的例子,好比播放器的實現,面向過程的實現方式就是將播放視頻的這個功能分解成多個過程,好比,加載視頻地址,獲取視頻信息,初始化解碼器,選擇合適的解碼器進行解碼,讀取解碼後的幀進行視頻格式轉換和音頻重採樣,而後讀取幀進行播放,這是一個完整的過程,這個過程當中不涉及類的概念,而面向對象最大的特色就是類,封裝繼承和多態是核心,一樣的以播放器爲例,一面向對象的方式來實現,將會針對每個功能封裝出一個對象,吧如說Muxer,獲取視頻信息,Decoder,解碼,格式轉換器,視頻播放器,音頻播放器等,每個功能對應一個對象,由這個對象來完成對應的功能,而且遵循單一職責原則,一個對象只作它相關的事情android

說下java中的線程建立方式,線程池的工做原理

java中有三種建立線程的方式,或者說四種1.繼承Thread類實現多線程2.實現Runnable接口3.實現Callable接口4.經過線程池算法

線程池的工做原理:線程池能夠減小建立和銷燬線程的次數,從而減小系統資源的消耗,當一個任務提交到線程池時a. 首先判斷核心線程池中的線程是否已經滿了,若是沒滿,則建立一個核心線程執行任務,不然進入下一步b. 判斷工做隊列是否已滿,沒有滿則加入工做隊列,不然執行下一步c. 判斷線程數是否達到了最大值,若是不是,則建立非核心線程執行任務,不然執行飽和策略,默認拋出異常sql

說下 handler 原理

Handler,Message,looper 和 MessageQueue 構成了安卓的消息機制,handler建立後能夠經過 sendMessage 將消息加入消息隊列,而後 looper不斷的將消息從 MessageQueue 中取出來,回調到 Hander 的 handleMessage方法,從而實現線程的通訊。數據庫

從兩種狀況來講,第一在UI線程建立Handler,此時咱們不須要手動開啓looper,由於在應用啓動時,在ActivityThread的main方法中就建立了一個當前主線程的looper,並開啓了消息隊列,消息隊列是一個無限循環,爲何無限循環不會ANR?由於能夠說,應用的整個生命週期就是運行在這個消息循環中的,安卓是由事件驅動的,Looper.loop不斷的接收處理事件,每個點擊觸摸或者Activity每個生命週期都是在Looper.loop的控制之下的,looper.loop一旦結束,應用程序的生命週期也就結束了。咱們能夠想一想什麼狀況下會發生ANR,第一,事件沒有獲得處理,第二,事件正在處理,可是沒有及時完成,而對事件進行處理的就是looper,因此只能說事件的處理若是阻塞會致使ANR,而不能說looper的無限循環會ANR編程

另外一種狀況就是在子線程建立Handler,此時因爲這個線程中沒有默認開啓的消息隊列,因此咱們須要手動調用looper.prepare(),並經過looper.loop開啓消息設計模式

主線程Looper從消息隊列讀取消息,當讀完全部消息時,主線程阻塞。子線程往消息隊列發送消息,而且往管道文件寫數據,主線程即被喚醒,從管道文件讀取數據,主線程被喚醒只是爲了讀取消息,當消息讀取完畢,再次睡眠。所以loop的循環並不會對CPU性能有過多的消耗。數組

內存泄漏的場景和解決辦法

1.非靜態內部類的靜態實例非靜態內部類會持有外部類的引用,若是非靜態內部類的實例是靜態的,就會長期的維持着外部類的引用,組織被系統回收,解決辦法是使用靜態內部類緩存

2.多線程相關的匿名內部類和非靜態內部類匿名內部類一樣會持有外部類的引用,若是在線程中執行耗時操做就有可能發生內存泄漏,致使外部類沒法被回收,直到耗時任務結束,解決辦法是在頁面退出時結束線程中的任務

3.Handler內存泄漏Handler致使的內存泄漏也能夠被概括爲非靜態內部類致使的,Handler內部message是被存儲在MessageQueue中的,有些message不能立刻被處理,存在的時間會很長,致使handler沒法被回收,若是handler是非靜態的,就會致使它的外部類沒法被回收,解決辦法是1.使用靜態handler,外部類引用使用弱引用處理2.在退出頁面時移除消息隊列中的消息

4.Context致使內存泄漏根據場景肯定使用Activity的Context仍是Application的Context,由於兩者生命週期不一樣,對於沒必要須使用Activity的Context的場景(Dialog),一概採用Application的Context,單例模式是最多見的發生此泄漏的場景,好比傳入一個Activity的Context被靜態類引用,致使沒法回收

5.靜態View致使泄漏使用靜態View能夠避免每次啓動Activity都去讀取並渲染View,可是靜態View會持有Activity的引用,致使沒法回收,解決辦法是在Activity銷燬的時候將靜態View設置爲null(View一旦被加載到界面中將會持有一個Context對象的引用,在這個例子中,這個context對象是咱們的Activity,聲明一個靜態變量引用這個View,也就引用了activity)

6.WebView致使的內存泄漏WebView只要使用一次,內存就不會被釋放,因此WebView都存在內存泄漏的問題,一般的解決辦法是爲WebView單開一個進程,使用AIDL進行通訊,根據業務需求在合適的時機釋放掉

7.資源對象未關閉致使如Cursor,File等,內部每每都使用了緩衝,會形成內存泄漏,必定要確保關閉它並將引用置爲null

8.集合中的對象未清理集合用於保存對象,若是集合愈來愈大,不進行合理的清理,尤爲是入股集合是靜態的

9.Bitmap致使內存泄漏bitmap是比較佔內存的,因此必定要在不使用的時候及時進行清理,避免靜態變量持有大的bitmap對象

10.監聽器未關閉不少須要register和unregister的系統服務要在合適的時候進行unregister,手動添加的listener也須要及時移除

如何避免OOM?

1.使用更加輕量的數據結構:如使用ArrayMap/SparseArray替代HashMap,HashMap更耗內存,由於它須要額外的實例對象來記錄Mapping操做,SparseArray更加高效,由於它避免了Key Value的自動裝箱,和裝箱後的解箱操做

2.便面枚舉的使用,能夠用靜態常量或者註解@IntDef替代

3.Bitmap優化:a.尺寸壓縮:經過InSampleSize設置合適的縮放b.顏色質量:設置合適的format,ARGB_6666/RBG_545/ARGB_4444/ALPHA_6,存在很大差別c.inBitmap:使用inBitmap屬性能夠告知Bitmap解碼器去嘗試使用已經存在的內存區域,新解碼的Bitmap會嘗試去使用以前那張Bitmap在Heap中所佔據的pixel data內存區域,而不是去問內存從新申請一塊區域來存放Bitmap。利用這種特性,即便是上千張的圖片,也只會僅僅只須要佔用屏幕所可以顯示的圖片數量的內存大小,但複用存在一些限制,具體體如今:在Android 4.4以前只能重用相同大小的Bitmap的內存,而Android 4.4及之後版本則只要後來的Bitmap比以前的小便可。使用inBitmap參數前,每建立一個Bitmap對象都會分配一塊內存供其使用,而使用了inBitmap參數後,多個Bitmap能夠複用一塊內存,這樣能夠提升性能

4.StringBuilder替代String: 在有些時候,代碼中會須要使用到大量的字符串拼接的操做,這種時候有必要考慮使用StringBuilder來替代頻繁的「+」

5.避免在相似onDraw這樣的方法中建立對象,由於它會迅速佔用大量內存,引發頻繁的GC甚至內存抖動

6.減小內存泄漏也是一種避免OOM的方法

說下 Activity 的啓動模式,生命週期,兩個 Activity 跳轉的生命週期,若是一個 Activity 跳轉另外一個 Activity 再按下 Home 鍵在回到 Activity 的生命週期是什麼樣的

啓動模式

Standard 模式:Activity 能夠有多個實例,每次啓動 Activity,不管任務棧中是否已經有這個Activity的實例,系統都會建立一個新的Activity實例

SingleTop模式:當一個singleTop模式的Activity已經位於任務棧的棧頂,再去啓動它時,不會再建立新的實例,若是不位於棧頂,就會建立新的實例

SingleTask模式:若是Activity已經位於棧頂,系統不會建立新的Activity實例,和singleTop模式同樣。但Activity已經存在但不位於棧頂時,系統就會把該Activity移到棧頂,並把它上面的activity出棧

SingleInstance模式:singleInstance 模式也是單例的,但和singleTask不一樣,singleTask 只是任務棧內單例,系統裏是能夠有多個singleTask Activity實例的,而 singleInstance Activity 在整個系統裏只有一個實例,啓動一singleInstanceActivity 時,系統會建立一個新的任務棧,而且這個任務棧只有他一個Activity

生命週期

onCreate onStart onResume onPause onStop onDestroy

兩個 Activity 跳轉的生命週期1.啓動AonCreate - onStart - onResume

2.在A中啓動BActivityA onPauseActivityB onCreateActivityB onStartActivityB onResumeActivityA onStop

3.從B中返回A(按物理硬件返回鍵)ActivityB onPauseActivityA onRestartActivityA onStartActivityA onResumeActivityB onStopActivityB onDestroy

4.繼續返回ActivityA onPauseActivityA onStopActivityA onDestroy

onRestart 的調用場景

(1)按下home鍵以後,而後切換回來,會調用onRestart()。(2)從本Activity跳轉到另外一個Activity以後,按back鍵返回原來Activity,會調用onRestart();(3)從本Activity切換到其餘的應用,而後再從其餘應用切換回來,會調用onRestart();

說下 Activity 的橫豎屏的切換的生命週期,用那個方法來保存數據,二者的區別。觸發在何時在那個方法裏能夠獲取數據等。

是否了 SurfaceView,它是什麼?他的繼承方式是什麼?他與View的區別(從源碼角度,如加載,繪製等)。

SurfaceView中採用了雙緩衝機制,保證了UI界面的流暢性,同時 SurfaceView 不在主線程中繪製,而是另開闢一個線程去繪製,因此它不妨礙UI線程;

SurfaceView 繼承於View,他和View主要有如下三點區別:(1)View底層沒有雙緩衝機制,SurfaceView有;(2)view主要適用於主動更新,而SurfaceView適用與被動的更新,如頻繁的刷新(3)view會在主線程中去更新UI,而SurfaceView則在子線程中刷新;SurfaceView的內容不在應用窗口上,因此不能使用變換(平移、縮放、旋轉等)。也難以放在ListView或者ScrollView中,不能使用UI控件的一些特性好比View.setAlpha()

View:顯示視圖,內置畫布,提供圖形繪製函數、觸屏事件、按鍵事件函數等;必須在UI主線程內更新畫面,速度較慢。SurfaceView:基於view視圖進行拓展的視圖類,更適合2D遊戲的開發;是view的子類,相似使用雙緩機制,在新的線程中更新畫面因此刷新界面速度比view快,Camera預覽界面使用SurfaceView。GLSurfaceView:基於SurfaceView視圖再次進行拓展的視圖類,專用於3D遊戲開發的視圖;是SurfaceView的子類,openGL專用。

如何實現進程保活

a: Service 設置成 START_STICKY kill 後會被重啓(等待5秒左右),重傳Intent,保持與重啓前同樣b: 經過 startForeground將進程設置爲前臺進程, 作前臺服務,優先級和前臺應用一個級別,除非在系統內存很是缺,不然此進程不會被 killc: 雙進程Service: 讓2個進程互相保護對方,其中一個Service被清理後,另外沒被清理的進程能夠當即重啓進程d: 用C編寫守護進程(即子進程) : Android系統中當前進程(Process)fork出來的子進程,被系統認爲是兩個不一樣的進程。當父進程被殺死的時候,子進程仍然能夠存活,並不受影響(Android5.0以上的版本不可行)聯繫廠商,加入白名單e.鎖屏狀態下,開啓一個一像素Activity

說下冷啓動與熱啓動是什麼,區別,如何優化,使用場景等。
app冷啓動: 當應用啓動時,後臺沒有該應用的進程,這時系統會從新建立一個新的進程分配給該應用, 這個啓動方式就叫作冷啓動(後臺不存在該應用進程)。冷啓動由於系統會從新建立一個新的進程分配給它,因此會先建立和初始化Application類,再建立和初始化MainActivity類(包括一系列的測量、佈局、繪製),最後顯示在界面上。

app熱啓動: 當應用已經被打開, 可是被按下返回鍵、Home鍵等按鍵時回到桌面或者是其餘程序的時候,再從新打開該app時, 這個方式叫作熱啓動(後臺已經存在該應用進程)。熱啓動由於會從已有的進程中來啓動,因此熱啓動就不會走Application這步了,而是直接走MainActivity(包括一系列的測量、佈局、繪製),因此熱啓動的過程只須要建立和初始化一個MainActivity就好了,而沒必要建立和初始化Application

冷啓動的流程當點擊app的啓動圖標時,安卓系統會從Zygote進程中fork建立出一個新的進程分配給該應用,以後會依次建立和初始化Application類、建立MainActivity類、加載主題樣式Theme中的windowBackground等屬性設置給MainActivity以及配置Activity層級上的一些屬性、再inflate佈局、當onCreate/onStart/onResume方法都走完了後最後才進行contentView的measure/layout/draw顯示在界面上

冷啓動的生命週期簡要流程:Application構造方法 –> attachBaseContext()–>onCreate –>Activity構造方法 –> onCreate() –> 配置主體中的背景等操做 –>onStart() –> onResume() –> 測量、佈局、繪製顯示

冷啓動的優化主要是視覺上的優化,解決白屏問題,提升用戶體驗,因此經過上面冷啓動的過程。能作的優化以下:

減小 onCreate()方法的工做量

不要讓 Application 參與業務的操做

不要在 Application 進行耗時操做

不要以靜態變量的方式在 Application 保存數據

減小布局的複雜度和層級

減小主線程耗時

爲何冷啓動會有白屏黑屏問題?緣由在於加載主題樣式Theme中的windowBackground等屬性設置給MainActivity發生在inflate佈局當onCreate/onStart/onResume方法以前,而windowBackground背景被設置成了白色或者黑色,因此咱們進入app的第一個界面的時候會形成先白屏或黑屏一下再進入界面。解決思路以下

1.給他設置 windowBackground 背景跟啓動頁的背景相同,若是你的啓動頁是張圖片那麼能夠直接給 windowBackground 這個屬性設置該圖片那麼就不會有一閃的效果了

<style name=``"Splash_Theme"` `parent=``"@android:style/Theme.NoTitleBar"``>`
    <item name=``"android:windowBackground"``>@drawable/splash_bg</item>`
    <item name=``"android:windowNoTitle"``>``true``</item>`
</style>`

2.採用世面的處理方法,設置背景是透明的,給人一種延遲啓動的感受。,將背景顏色設置爲透明色,這樣當用戶點擊桌面APP圖片的時候,並不會"當即"進入APP,並且在桌面上停留一會,其實這時候APP已是啓動的了,只是咱們心機的把Theme裏的windowBackground 的顏色設置成透明的,強行把鍋甩給了手機應用廠商(手機反應太慢了啦)

<style name=``"Splash_Theme"` `parent=``"@android:style/Theme.NoTitleBar"``>`
    <item name=``"android:windowIsTranslucent"``>``true``</item>`
    <item name=``"android:windowNoTitle"``>``true``</item>`
</style>`

3.以上兩種方法是在視覺上顯得更快,但其實只是一種表象,讓應用啓動的更快,有一種思路,將 Application 中的沒必要要的初始化動做實現懶加載,好比,在SpashActivity 顯示後再發送消息到 Application,去初始化,這樣能夠將初始化的動做放在後邊,縮短應用啓動到用戶看到界面的時間

Android 中的線程有那些,原理與各自特色

AsyncTask,HandlerThread,IntentService

AsyncTask原理:內部是Handler和兩個線程池實現的,Handler用於將線程切換到主線程,兩個線程池一個用於任務的排隊,一個用於執行任務,當AsyncTask執行execute方法時會封裝出一個FutureTask對象,將這個對象加入隊列中,若是此時沒有正在執行的任務,就執行它,執行完成以後繼續執行隊列中下一個任務,執行完成經過Handler將事件發送到主線程。AsyncTask必須在主線程初始化,由於內部的Handler是一個靜態對象,在AsyncTask類加載的時候他就已經被初始化了。在Android3.0開始,execute方法串行執行任務的,一個一個來,3.0以前是並行執行的。若是要在3.0上執行並行任務,能夠調用executeOnExecutor方法

HandlerThread原理:繼承自 Thread,start開啓線程後,會在其run方法中會經過Looper 建立消息隊列並開啓消息循環,這個消息隊列運行在子線程中,因此能夠將HandlerThread 中的 Looper 實例傳遞給一個 Handler,從而保證這個 Handler 的 handleMessage 方法運行在子線程中,Android 中使用 HandlerThread的一個場景就是 IntentService

IntentService原理:繼承自Service,它的內部封裝了 HandlerThread 和Handler,能夠執行耗時任務,同時由於它是一個服務,優先級比普通線程高不少,因此更適合執行一些高優先級的後臺任務,HandlerThread底層經過Looper消息隊列實現的,因此它是順序的執行每個任務。能夠經過Intent的方式開啓IntentService,IntentService經過handler將每個intent加入HandlerThread子線程中的消息隊列,經過looper按順序一個個的取出並執行,執行完成後自動結束本身,不須要開發者手動關閉

ANR的緣由

1.耗時的網絡訪問2.大量的數據讀寫3.數據庫操做4.硬件操做(好比camera)5.調用thread的join()方法、sleep()方法、wait()方法或者等待線程鎖的時候6.service binder的數量達到上限7.system server中發生WatchDog ANR8.service忙致使超時無響應9.其餘線程持有鎖,致使主線程等待超時10.其它線程終止或崩潰致使主線程一直等待

三級緩存原理

當 Android 端須要得到數據時好比獲取網絡中的圖片,首先從內存中查找(按鍵查找),內存中沒有的再從磁盤文件或sqlite中去查找,若磁盤中也沒有才經過網絡獲取

LruCache 底層實現原理:

LruCache 中 Lru 算法的實現就是經過 LinkedHashMap 來實現的。LinkedHashMap 繼承於 HashMap,它使用了一個雙向鏈表來存儲 Map中的Entry順序關係,對於get、put、remove等操做,LinkedHashMap除了要作HashMap作的事情,還作些調整Entry順序鏈表的工做。LruCache中將LinkedHashMap的順序設置爲LRU順序來實現LRU緩存,每次調用get(也就是從內存緩存中取圖片),則將該對象移到鏈表的尾端。調用put插入新的對象也是存儲在鏈表尾端,這樣當內存緩存達到設定的最大值時,將鏈表頭部的對象(近期最少用到的)移除。

說下你對 Collection 這個類的理解。

Collection是集合框架的頂層接口,是存儲對象的容器,Colloction定義了接口的公用方法如add remove clear等等,它的子接口有兩個,List和Set,List的特色有元素有序,元素能夠重複,元素都有索引(角標),典型的有Vector:內部是數組數據結構,是同步的(線程安全的)。增刪查詢都很慢。ArrayList:內部是數組數據結構,是不一樣步的(線程不安全的)。替代了Vector。查詢速度快,增刪比較慢。LinkedList:內部是鏈表數據結構,是不一樣步的(線程不安全的)。增刪元素速度快。

而Set的是特色元素無序,元素不能夠重複HashSet:內部數據結構是哈希表,是不一樣步的。Set集合中元素都必須是惟一的,HashSet做爲其子類也需保證元素的惟一性。判斷元素惟一性的方式:經過存儲對象(元素)的hashCode和equals方法來完成對象惟一性的。若是對象的hashCode值不一樣,那麼不用調用equals方法就會將對象直接存儲到集合中;若是對象的hashCode值相同,那麼需調用equals方法判斷返回值是否爲true,若爲false, 則視爲不一樣元素,就會直接存儲;若爲true, 則視爲相同元素,不會存儲。若是要使用HashSet集合存儲元素,該元素的類必須覆蓋hashCode方法和equals方法。通常狀況下,若是定義的類會產生不少對象,一般都須要覆蓋equals,hashCode方法。創建對象判斷是否相同的依據。

TreeSet:保證元素惟一性的同時能夠對內部元素進行排序,是不一樣步的。判斷元素惟一性的方式:根據比較方法的返回結果是否爲0,若是爲0視爲相同元素,不存;若是非0視爲不一樣元素,則存。TreeSet對元素的排序有兩種方式:方式一:使元素(對象)對應的類實現Comparable接口,覆蓋compareTo方法。這樣元素自身具備比較功能。方式二:使TreeSet集合自身具備比較功能,定義一個比較器Comparator,將該類對象做爲參數傳遞給TreeSet集合的構造函數

說下AIDL的使用與原理

aidl是安卓中的一種進程間通訊方式

說下你對廣播的理解說下你對服務的理解,如何殺死一個服務。服務的生命週期(start與bind)。是否接觸過藍牙等開發設計一個ListView左右分頁排版的功能自定義View,說出主要的方法。-說下binder序列化與反序列化的過程,與使用過程是否接觸過JNI/NDK,java如何調用C語言的方法-如何查看模擬器中的SP與SQList文件。如何可視化查看佈局嵌套層數與加載時間。你說用的代碼管理工具什麼,爲何會產生代碼衝突,該如何解決說下你對後臺的編程有那些認識,聊些前端那些方面的知識。說下你對線程池的理解,如何建立一個線程池與使用。說下你用過那些註解框架,他們的原理是什麼。本身實現過,或是理解他的工做過程嗎?說下java虛擬機的理解,回收機制,JVM是如何回收對象的,有哪些方法等一些java與Android源碼相關知識等

大學成績大學那些專業,你哪方面學得好單片機,嵌入式,電子線路。畢業設計什麼,幾我的實現的,主要功能是什麼還有些其餘硬件相關知識本身的職業規劃與發展方向


領取資料:【Android技術開發交流②】:979045005 點擊連接加入羣聊:https://jq.qq.com/?_wv=1027&k...

相關文章
相關標籤/搜索