金三銀四Android面試的一些感覺,附加面試題

前言

對過去的兩三年作個總結,或許能幫助到些人,或者從中能得一些建議。此次出去面試主要是兩個星期的時間,第一個星期主要是投簡歷,第二個星期主要是面試,一天安排了2-3個面試。

一丶如何準備面試呢?面試的注意事項有哪些呢?

下面是我總結的一些準備面試的Tips以及面試必備的注意事項java

1.準備一份本身的自我介紹,面試的時候根據面試對象適當進行修改(突出重點,突出本身的優點在哪裏,切忌流水帳);android

2.注意隨身帶上本身的成績單和簡歷複印件; (有的公司在面試前都會讓你交一份成績單和簡歷當作面試中的參考。)git

3.若是須要筆試就提早刷一些筆試題,大部分在線筆試的類型是選擇題+編程題,有的還會有簡答題。(平時空閒時間多的能夠刷一下筆試題目(牛客網上有不少),可是不要只刷面試題,不動手code,程序員不是爲了考試而存在的。)另外,注意抓重點,由於題目太多了,可是有不少題目幾乎次次遇到,像這樣的題目必定要搞定。程序員

4.提早準備技術面試。 搞清楚本身面試中可能涉及哪些知識點、那些知識點是重點。面試中哪些問題會被常常問到、本身改如何回答。(強烈不推薦背題)
第一: 經過背這種方式你能記住多少?能記住多久?
第二: 背題的方式的學習很難堅持下去!github

5.面試以前作好定向複習。 也就是專門針對你要面試的公司來複習。好比你在面試以前能夠在網上找找有沒有你要面試的公司的面經。面試

6.準備好本身的項目介紹。 若是有項目的話,技術面試第一步,面試官通常都是讓你本身介紹一下你的項目。你能夠從下面幾個方向來考慮:算法

①對項目總體設計的一個感覺(面試官可能會讓你畫系統的架構圖;
②在這個項目中你負責了什麼、作了什麼、擔任了什麼角色;
③ 從這個項目中你學會了那些東西,使用到了那些技術,學會了那些新技術的使用;
④項目描述中,最好能夠體現本身的綜合素質,好比你是如何協調項目組成員協同開發的或者在遇到某一個棘手的問題的時候你是如何解決的又或者說你在這個項目用了什麼技術實現了什麼功能好比:Android Bitmap壓縮策略;關於HandlerThread的使用場景以及怎樣使用 HandlerThread?

提早知道有哪些技術問題常問: HashMap源碼分析、熱修復,handler等等問題我以爲面試中實在太常見了,好好準備!後面的文章會我會分類詳細介紹到那些問題最常問。編程

提早熟悉一些常問的非技術問題: 面試的時候有一些常見的非技術問題好比「面試官問你的優勢是什麼,應該如何回答?」、「面試官問你的缺點是什麼,應該如何回答?」、「若是面試官問"你有什麼問題問我嗎?"時,你該如何回答」等等,對於這些問題,如何回答本身內心要有個數,別面試的時候出了亂子。設計模式

6.面試以後記得覆盤。 面試遭遇失敗是很正常的事情,因此善於總結本身的失敗緣由纔是最重要的。若是失敗,不要灰心;若是經過,切勿狂喜。緩存

二丶面試主要印象比較深的知識點:

  • 棧和堆的區別
  • 接口和抽象類的本質區別
  • Android Jetpack最新的組件原理
  • 註解、反射、泛型
  • Handler消息機制,生產者和消費者模型
  • View、ViewGroup的事件傳遞機制,如何解決滑動衝突? 回答如何滑動-衝突最好是舉出實際的場景和怎麼解決的
  • View、ViewGroup的繪製流程
  • okHttp、Retrofit的源碼,原理
  • 解釋一下什麼是MVP架構
  • Https原理,加密算法
  • RecyclerView的緩存機制
  • 常見的設計模式主要問到了這幾個(單例、代理、適配器、建造者),先說概念,而後面試官會問具體的使用場景
  • 最新的Google AAC架構(ViewModel、LiveData、Room等等)有沒有在使用,以及背後的實現原理
  • Kotlin有沒有在使用,問這個問題的公司,基本上本身的公司在使用Kotlin開發新App,要麼在使用Kotlin遷移、重構、與java混合在一塊兒
  • Android常見的內存泄漏緣由,以及檢查工具,主要是問如何使用Android Profile檢查內存泄漏的,性能分析怎麼作?以及第三方檢查內存泄漏的工具LeakCanary的原理?
  • 開發的App有哪些亮點,難點、如何排查線上的bug,有沒有重構代碼的經驗android的進程間的通訊方式、多線程下載你是怎麼作的?
  • android的進程間的通訊方式、多線程下載你是怎麼作的

以上的面試題,主要是Android應用層知識,須要面試以前造造火箭的,還須要平時的耕耘、積累和總結。

三丶真題(附答案)

1.Java中引用類型的區別,具體的使用場景

Java中引用類型分爲四類: 強引用、軟引用、弱引用、虛引用。

強引用: 強引用指的是經過new對象建立的引用,垃圾回收器即便是內存不足也不會回收強引用指向的對象。

軟引用: 軟引用是經過SoftRefrence實現的,它的生命週期比強引用短,在內存不足,拋出OOM以前,垃圾回收器會回收軟引用引用的對象。軟引用常見的使用場景是存儲一些內存敏感的緩存,當內存不足時會被回收。

弱引用: 弱引用是經過WeakRefrence實現的,它的生命週期比軟引用還短,GC只要掃描到弱引用的對象就會回收。弱引用常見的使用場景也是存儲一些內存敏感的緩存。

虛引用: 虛引用是經過FanttomRefrence實現的,它的生命週期最短,隨時可能被回收。若是一個對象只被虛引用引用,咱們沒法經過虛引用來訪問這個對象的任何屬性和方法。它的做用僅僅是保證對象在finalize後,作某些事情。虛引用常見的使用場景是跟蹤對象被垃圾回收的活動,當一個虛引用關聯的對象被垃圾回收器回收以前會收到一條系統通知。

2.volatile

通常提到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個做用:

  • 確保指令重排的時候不會把屏障後的指令排在屏障前,確保不會把屏障前的指令排在屏障後。
  • 修改緩存中的共享變量後當即刷新到主存中。
  • 當執行寫操做時會致使其餘CPU中的緩存無效。

3.進程間通訊的方式有哪幾種

AIDL 、廣播、文件、socket、管道

4.Android性能優化工具使用(這個問題建議配合Android中的性能優化)

Android中經常使用的性能優化工具包括這些:Android Studio自帶的Android Profiler、LeakCanary、BlockCanary

Android自帶的Android Profiler其實就很好用,Android Profiler能夠檢測三個方面的性能問題:CPU、MEMORY、NETWORK。

LeakCanary是一個第三方的檢測內存泄漏的庫,咱們的項目集成以後LeakCanary會自動檢測應用運行期間的內存泄漏,並將之輸出給咱們。

BlockCanary也是一個第三方檢測UI卡頓的庫,項目集成後Block也會自動檢測應用運行期間的UI卡頓,並將之輸出給咱們。

5.Android中的類加載器

PathClassLoader,只能加載系統中已經安裝過的apk
DexClassLoader,能夠加載jar/apk/dex,能夠從SD卡中加載未安裝的apk

6.Android中的動畫有哪幾類,它們的特色和區別是什麼

Android中動畫大體分爲3類: 幀動畫、補間動畫(View Animation)、屬性動畫(Object Animation)。

幀動畫: 經過xml配置一組圖片,動態播放。不多會使用。

補間動畫(View Animation): 大體分爲旋轉、透明、縮放、位移四類操做。不多會使用。

屬性動畫(Object Animation): 屬性動畫是如今使用的最多的一種動畫,它比補間動畫更增強大。屬性動畫大體分爲兩種使用類型,分別是ViewPropertyAnimatorObjectAnimator。前者適合一些通用的動畫,好比旋轉、位移、縮放和透明,使用方式也很簡單經過View.animate()便可獲得ViewPropertyAnimator,以後進行相應的動畫操做便可。後者適合用於爲咱們的自定義控件添加動畫,固然首先咱們應該在自定義View中添加相應的getXXX()setXXX()相應屬性的gettersetter方法,這裏須要注意的是在setter方法內改變了自定義View中的屬性後要調用invalidate()來刷新View的繪製。以後調用ObjectAnimator.of屬性類型()返回一個ObjectAnimator,調用start()方法啓動動畫便可。

補間動畫與屬性動畫的區別:

  • 補間動畫是父容器不斷的繪製view,看起來像移動了效果,其實view沒有變化,還在原地。
  • 是經過不斷改變view內部的屬性值,真正的改變view。

7.Handler機制

說到Handler,就不得不提與之密切相關的這幾個類:MessageMessageQueueLooper

  • Message。 Message中有兩個成員變量值得關注:targetcallback。target其實就是發送消息的Handler對象,callback是當調用handler.post(runnable)時傳入的Runnable類型的任務。post事件的本質也是建立了一個Message,將咱們傳入的這個runnable賦值給建立的Messagecallback這個成員變量。
  • MessageQueue。消息隊列很明顯是存放消息的隊列,值得關注的是MessageQueue中的next()方法,它會返回下一個待處理的消息。
  • Looper。Looper消息輪詢器實際上是鏈接Handler和消息隊列的核心。首先咱們都知道,若是想要在一個線程中建立一個Handler,首先要經過Looper.prepare()建立Looper,以後還得調用Looper.loop()開啓輪詢。咱們着重看一下這兩個方法。

prepare()。這個方法作了兩件事:首先經過ThreadLocal.get()獲取當前線程中的Looper,若是不爲空,則會拋出一個RunTimeException,意思是一個線程不能建立2個Looper。若是爲null則執行下一步。第二步是建立了一個Looper,並經過ThreadLocal.set(looper)。將咱們建立的Looper與當前線程綁定。這裏須要提一下的是消息隊列的建立其實就發生在Looper的構造方法中。

loop()。這個方法開啓了整個事件機制的輪詢。它的本質是開啓了一個死循環,不斷的經過MessageQueuenext()方法獲取消息。拿到消息後會調用msg.target.dispatchMessage()來作處理。其實咱們在說到Message的時候提到過,msg.target其實就是發送這個消息的handler。這句代碼的本質就是調用handler的dispatchMessage()。Handler。上面作了這麼多鋪墊,終於到了最重要的部分。Handler的分析着重在兩個部分:發送消息和處理消息

  • Handler。上面作了這麼多鋪墊,終於到了最重要的部分。Handler的分析着重在兩個部分:發送消息和處理消息

發送消息。其實發送消息除了sendMessage以外還有sendMessageDelayed和post以及postDelayed等等不一樣的方式。但它們的本質都是調用了sendMessageAtTime。在sendMessageAtTime這個方法中調用了enqueueMessage。在enqueueMessage這個方法中作了兩件事:經過msg.target = this實現了消息與當前handler的綁定。而後經過queue.enqueueMessage實現了消息入隊。

處理消息。消息處理的核心其實就是dispatchMessage()這個方法。這個方法裏面的邏輯很簡單,先判斷msg.callback是否爲null,若是不爲空則執行這個runnable。若是爲空則會執行咱們的handleMessage方法。

8.Android性能優化

Android中的性能優化在我看來分爲如下幾個方面:內存優化、佈局優化、網絡優化、安裝包優化。

內存優化: 下一個問題就是。

佈局優化: 佈局優化的本質就是減小View的層級。常見的佈局優化方案以下

  • 在LinearLayout和RelativeLayout均可以完成佈局的狀況下優先選擇RelativeLayout,能夠減小View的層級
  • 將經常使用的佈局組件抽取出來使用 < include > 標籤
  • 經過 < ViewStub > 標籤來加載不經常使用的佈局
  • 使用 < Merge > 標籤來減小布局的嵌套層次

網絡優化: 常見的網絡優化方案以下

  • 儘可能減小網絡請求,可以合併的就儘可能合併
  • 避免DNS解析,根據域名查詢可能會耗費上百毫秒的時間,也可能存在DNS劫持的風險。能夠根據業務需求採用增長動態更新IP的方式,或者在IP方式訪問失敗時切換到域名訪問方式。
  • 大量數據的加載採用分頁的方式
  • 網絡數據傳輸採用GZIP壓縮
  • 加入網絡數據的緩存,避免頻繁請求網絡
  • 上傳圖片時,在必要的時候壓縮圖片

安裝包優化: 安裝包優化的核心就是減小apk的體積,常見的方案以下

  • 使用混淆,能夠在必定程度上減小apk體積,但實際效果微乎其微
  • 減小應用中沒必要要的資源文件,好比圖片,在不影響APP效果的狀況下儘可能壓縮圖片,有必定的效果
  • 在使用了SO庫的時候優先保留v7版本的SO庫,刪掉其餘版本的SO庫。緣由是在2018年,v7版本的SO庫能夠知足市面上絕大多數的要求,可能八九年前的手機知足不了,但咱們也不必去適配老掉牙的手機。實際開發中減小apk體積的效果是十分顯著的,若是你使用了不少SO庫,比方說一個版本的SO庫一共10M,那麼只保留v7版本,刪掉armeabi和v8版本的SO庫,一共能夠減小20M的體積。

9.Android內存優化

Android的內存優化在我看來分爲兩點: 避免內存泄漏、擴大內存,其實就是開源節流。

其實內存泄漏的本質就是較長生命週期的對象引用了較短生命週期的對象。

常見的內存泄漏:

  • 單例模式致使的內存泄漏。 最多見的例子就是建立這個單例對象須要傳入一個Context,這時候傳入了一個Activity類型的Context,因爲單例對象的靜態屬性,致使它的生命週期是從單例類加載到應用程序結束爲止,因此即便已經finish掉了傳入的Activity,因爲咱們的單例對象依然持有Activity的引用,因此致使了內存泄漏。解決辦法也很簡單,不要使用Activity類型的Context,使用Application類型的Context能夠避免內存泄漏。
  • 靜態變量致使的內存泄漏。 靜態變量是放在方法區中的,它的生命週期是從類加載到程序結束,能夠看到靜態變量生命週期是很是久的。最多見的因靜態變量致使內存泄漏的例子是咱們在Activity中建立了一個靜態變量,而這個靜態變量的建立須要傳入Activity的引用this。在這種狀況下即便Activity調用了finish也會致使內存泄漏。緣由就是由於這個靜態變量的生命週期幾乎和整個應用程序的生命週期一致,它一直持有Activity的引用,從而致使了內存泄漏。
  • 非靜態內部類致使的內存泄漏。 非靜態內部類致使內存泄漏的緣由是非靜態內部類持有外部類的引用,最多見的例子就是在Activity中使用Handler和Thread了。使用非靜態內部類建立的Handler和Thread在執行延時操做的時候會一直持有當前Activity的引用,若是在執行延時操做的時候就結束Activity,這樣就會致使內存泄漏。解決辦法有兩種:第一種是使用靜態內部類,在靜態內部類中使用弱引用調用Activity。第二種方法是在Activity的onDestroy中調用handler.removeCallbacksAndMessages來取消延時事件。
  • 使用資源未及時關閉致使的內存泄漏。 常見的例子有:操做各類數據流未及時關閉,操做Bitmap未及時recycle等等。
  • 使用第三方庫未能及時解綁。有的三方庫提供了註冊和解綁的功能,最多見的就是EventBus了,咱們都知道使用EventBus要在onCreate中註冊,在onDestroy中解綁。若是沒有解綁的話,EventBus實際上是一個單例模式,他會一直持有Activity的引用,致使內存泄漏。一樣常見的還有RxJava,在使用Timer操做符作了一些延時操做後也要注意在onDestroy方法中調用disposable.dispose()來取消操做。
  • 屬性動畫致使的內存泄漏。 常見的例子就是在屬性動畫執行的過程當中退出了Activity,這時View對象依然持有Activity的引用從而致使了內存泄漏。解決辦法就是在onDestroy中調用動畫的cancel方法取消屬性動畫。
  • WebView致使的內存泄漏。 WebView比較特殊,即便是調用了它的destroy方法,依然會致使內存泄漏。其實避免WebView致使內存泄漏的最好方法就是讓WebView所在的Activity處於另外一個進程中,當這個Activity結束時殺死當前WebView所處的進程便可,我記得阿里釘釘的WebView就是另外開啓的一個進程,應該也是採用這種方法避免內存泄漏。

擴大內存,爲何要擴大咱們的內存呢?有時候咱們實際開發中不可避免的要使用不少第三方商業的SDK,這些SDK其實有好有壞,大廠的SDK可能內存泄漏會少一些,但一些小廠的SDK質量也就不太靠譜一些。那應對這種咱們沒法改變的狀況,最好的辦法就是擴大內存。

擴大內存一般有兩種方法:一個是在清單文件中的Application下添加largeHeap=」true」這個屬性,另外一個就是同一個應用開啓多個進程來擴大一個應用的總內存空間。第二種方法其實就很常見了,比方說我使用過個推的SDK,個推的Service其實就是處在另一個單獨的進程中。

Android中的內存優化總的來講就是開源和節流,開源就是擴大內存,節流就是避免內存泄漏。

10.Binder機制

在Linux中,爲了不一個進程對其餘進程的干擾,進程之間是相互獨立的。在一個進程中其實還分爲用戶空間和內核空間。這裏的隔離分爲兩個部分,進程間的隔離和進程內的隔離。

既然進程間存在隔離,那其實也是存在着交互。進程間通訊就是IPC,用戶空間和內核空間的通訊就是系統調用。

Linux爲了保證獨立性和安全性,進程之間不能直接相互訪問,Android是基於Linux的,因此也是須要解決進程間通訊的問題。

其實Linux進程間通訊有不少方式,好比管道、socket等等。爲何Android進程間通訊採用了Binder而不是Linux已有的方式,主要是有這麼兩點考慮:性能和安全

性能。在移動設備上對性能要求是比較嚴苛的。Linux傳統的進程間通訊好比管道、socket等等進程間通訊是須要複製兩次數據,而Binder則只須要一次。因此Binder在性能上是優於傳統進程通訊的。

安全。傳統的Linux進程通訊是不包含通訊雙方的身份驗證的,這樣會致使一些安全性問題。而Binder機制自帶身份驗證,從而有效的提升了安全性。

Binder是基於CS架構的,有四個主要組成部分。

  • Client。客戶端進程。
  • Server。服務端進程。
  • ServiceManager。提供註冊、查詢和返回代理服務對象的功能。
  • Binder驅動。主要負責創建進程間的Binder鏈接,進程間的數據交互等等底層操做。

Binder機制主要的流程是這樣的:

  • 服務端經過Binder驅動在ServiceManager中註冊咱們的服務。
  • 客戶端經過Binder驅動查詢在ServiceManager中註冊的服務。
  • ServiceManager經過Binder驅動返回服務端的代理對象。
  • 客戶端拿到服務端的代理對象後便可進行進程間通訊。

四丶總結

整體發現整個面試下來,投簡歷發現今年996的公司還蠻多的,前兩年沒有這麼多。有的人事,boss會直接說是996,要麼本身面試過程當中問是否是996,996的公司是拒絕的,壓根不想去。面試的過程當中發現本身的信心不夠,技術能力也不夠、本身也着急。面試想一想這幾點要特別注意。

  • 像大一點的廠,投簡歷過去,在加上面試的時間回覆,須要兩週。
  • 若是面試官過程當中,發現面試官沒有問什麼技術問題,或者問的問題不夠深刻,基本上能夠判定這家公司不是靠技術做爲驅動公司發展的。
  • 提升本身的信心,本身要會的多,對知識點的理解要深刻。

寫代碼總結如下幾點:

1.須要確認需求的,理解有誤差的。寫代碼以前必定要和產品經理溝通交流。寧願多花時間去和測試的、設計師溝通,也不要去埋頭寫代碼。一樣的,認真想一想怎麼實現這樣一個功能,思路理清了在敲代碼。
2.養成良好的編碼習慣,風格。多看看Google開源的在github上示例,或者其它知名公司的。
3.六大設計原則、一些經常使用的設計模式理解透牢記於心,多在編碼過程當中使用。
4.代碼要有思路,寫好註釋,寫的代碼不僅僅是本身要看,也是給別人看的。
5.平時學習須要多總結、多體會、代碼須要多動手敲。

關於我

更多面試內容分析,分享,學習筆記整理,視頻整理。核心筆記PDF

https://github.com/xiangjiana...
(點個star。專一分享技術乾貨/編程資源/求職面試/成長感悟等,關注送983頁PDF面試資源,免費下載CSDN資源。領取PDF面試大全,附送CSDN下載資源)
相關文章
相關標籤/搜索