Android大廠面試題錦集(BAT TMD JD 小米)

何時夕發表於2018-02-02

本文首發於微信公眾號——世界上有意思的事,搬運轉載請註明出處,否則將追究版權責任。微訊號:a1018998632,交流qq群:859640274

上次寫這篇文章的時候也差不多是一年前了,這一年我兜兜轉轉從android到java又回到android,校招面了很多大廠,阿里、京東、小米、頭條、知乎、騰訊、有贊,也收穫了幾個offer。感謝大家的關注,讓我在簡書上面也混到了一個簡書優秀程式設計師作者的稱號,所以為了回饋大家,一篇最完全的android面經誕生了。這是我集合了牛客網、百度、簡書等網站的幾十篇面經和我自己面試的經歷的合集,希望大家喜歡。(ps:裡面當然會有紕漏,如果有問題歡迎大家留言或者加我QQ討論)

1.安卓事件分發機制,請詳細說下整個流程

事件分發(面試).png

2.安卓view繪製機制和載入過程,請詳細說下整個流程

  • 1.ViewRootImpl會呼叫performTraversals(),其內部會呼叫performMeasure()、performLayout、performDraw()。
  • 2.performMeasure()會呼叫最外層的ViewGroup的measure()-->onMeasure(),ViewGroup的onMeasure()是抽象方法,但其提供了measureChildren(),這之中會遍歷子View然後迴圈呼叫measureChild()這之中會用getChildMeasureSpec()+父View的MeasureSpec+子View的LayoutParam一起獲取本View的MeasureSpec,然後呼叫子View的measure()到View的onMeasure()-->setMeasureDimension(getDefaultSize(),getDefaultSize()),getDefaultSize()預設返回measureSpec的測量數值,所以繼承View進行自定義的wrap_content需要重寫。
  • 3.performLayout()會呼叫最外層的ViewGroup的layout(l,t,r,b),本View在其中使用setFrame()設定本View的四個頂點位置。在onLayout(抽象方法)中確定子View的位置,如LinearLayout會遍歷子View,迴圈呼叫setChildFrame()-->子View.layout()。
  • 4.performDraw()會呼叫最外層ViewGroup的draw():其中會先後呼叫background.draw()(繪製背景)、onDraw()(繪製自己)、dispatchDraw()(繪製子View)、onDrawScrollBars()(繪製裝飾)。
  • 5.MeasureSpec由2位SpecMode(UNSPECIFIED、EXACTLY(對應精確值和match_parent)、AT_MOST(對應warp_content))和30位SpecSize組成一個int,DecorView的MeasureSpec由視窗大小和其LayoutParams決定,其他View由父View的MeasureSpec和本View的LayoutParams決定。ViewGroup中有getChildMeasureSpec()來獲取子View的MeasureSpec。
  • 6.三種方式獲取measure()後的寬高:
    • 1.Activity#onWindowFocusChange()中呼叫獲取
    • 2.view.post(Runnable)將獲取的程式碼投遞到訊息佇列的尾部。
    • 3.ViewTreeObservable.

3.android四大元件的載入過程,請詳細介紹下

4.Activity的啟動模式

  • 1.standard:預設標準模式,每啟動一個都會建立一個例項,
  • 2.singleTop:棧頂複用,如果在棧頂就呼叫onNewIntent複用,從onResume()開始
  • 3.singleTask:棧內複用,本棧內只要用該型別Activity就會將其頂部的activity出棧
  • 4.singleInstance:單例模式,除了3中特性,系統會單獨給該Activity建立一個棧,

5.A、B、C、D分別是四種Activity的啟動模式,那麼A->B->C->D->A->B->C->D分別啟動,最後的activity棧是怎麼樣的

  • 1.這個題目需要深入瞭解activity的啟動模式
  • 2.最後的答案是:兩個棧,前臺棧是隻有D,後臺棧從底至上是A、B、C

6.Activity快取方法

  • 1.配置改變導致Activity被殺死,橫屏變豎屏:在onStop之前會呼叫onSaveInstanceState()儲存資料在重建Activity之後,會在onStart()之後呼叫onRestoreInstanceState(),並把儲存下來的Bundle傳給onCreate()和它會預設重建Activity當前的檢視,我們可以在onCreate()中,回覆自己的資料。
  • 2.記憶體不足殺掉Activity,優先順序分別是:前臺可見,可見非前臺,後臺。

7.Service的生命週期,兩種啟動方法,有什麼區別

  • 1.context.startService() ->onCreate()- >onStart()->Service running-->(如果呼叫context.stopService() )->onDestroy() ->Service shut down
    • 1.如果Service還沒有執行,則呼叫onCreate()然後呼叫onStart();
    • 2.如果Service已經執行,則只呼叫onStart(),所以一個Service的onStart方法可能會重複呼叫多次。
    • 3.呼叫stopService的時候直接onDestroy,
    • 4.如果是呼叫者自己直接退出而沒有呼叫stopService的話,Service會一直在後臺執行。該Service的呼叫者再啟動起來後可以通過stopService關閉Service。
  • 2.context.bindService()->onCreate()->onBind()->Service running-->onUnbind() -> onDestroy() ->Service stop
    • 1.onBind將返回給客戶端一個IBind介面例項,IBind允許客戶端回撥服務的方法,比如得到Service執行的狀態或其他操作。
    • 2.這個時候會把呼叫者和Service繫結在一起,Context退出了,Service就會呼叫onUnbind->onDestroy相應退出。
    • 3.所以呼叫bindService的生命週期為:onCreate --> onBind(只一次,不可多次繫結) --> onUnbind --> onDestory。

8.怎麼保證service不被殺死

  • 1.提升service優先順序
  • 2.提升service程式優先順序
  • 3.onDestroy方法裡重啟service

9.靜態的Broadcast 和動態的有什麼區別

  • 1.動態的比靜態的安全
  • 2.靜態在app啟動的時候就初始化了 動態使用程式碼初始化
  • 3.靜態需要配置 動態不需要
  • 4.生存期,靜態廣播的生存期可以比動態廣播的長很多
  • 5.優先順序動態廣播的優先順序比靜態廣播高

10.Intent可以傳遞哪些資料型別

  • 1.Serializable
  • 2.charsequence: 主要用來傳遞String,char等
  • 3.parcelable
  • 4.Bundle

11.Json有什麼優劣勢、解析的原理

  • 1.JSON的速度要遠遠快於XML
  • 2.JSON相對於XML來講,資料的體積小
  • 3.JSON對資料的描述性比XML較差
  • 4.解析的基本原理是:詞法分析

12.一個語言的編譯過程

  • 1.詞法分析:將一串文字按規則分割成最小的結構,關鍵字、識別符號、運算子、界符和常量等。一般實現方法是自動機和正規表示式
  • 2.語法分析:將一系列單片語合成語法樹。一般實現方法有自頂向下和自底向上
  • 3.語義分析:對結構上正確的源程式進行上下文有關性質的審查
  • 4.目的碼生成
  • 5.程式碼優化:優化生成的目的碼,

13.動畫有哪幾類,各有什麼特點

  • 1.動畫的基本原理:其實就是利用插值器和估值器,來計算出各個時刻View的屬性,然後通過改變View的屬性來,實現View的動畫效果。
  • 2.View動畫:只是影像變化,view的實際位置還在原來的地方。
  • 3.幀動畫是在xml中定義好一系列圖片之後,使用AnimationDrawable來播放的動畫。
  • 4.View的屬性動畫:
    • 1.插值器:作用是根據時間的流逝的百分比來計算屬性改變的百分比
    • 2.估值器:在1的基礎上由這個東西來計算出屬性到底變化了多少數值的類

14.Handler、Looper訊息佇列模型,各部分的作用

  • 1.MessageQueue:讀取會自動刪除訊息,單連結串列維護,在插入和刪除上有優勢。在其next()中會無限迴圈,不斷判斷是否有訊息,有就返回這條訊息並移除。
  • 2.Looper:Looper建立的時候會建立一個MessageQueue,呼叫loop()方法的時候訊息迴圈開始,loop()也是一個死迴圈,會不斷呼叫messageQueue的next(),當有訊息就處理,否則阻塞在messageQueue的next()中。當Looper的quit()被呼叫的時候會呼叫messageQueue的quit(),此時next()會返回null,然後loop()方法也跟著退出。
  • 3.Handler:在主執行緒構造一個Handler,然後在其他執行緒呼叫sendMessage(),此時主執行緒的MessageQueue中會插入一條message,然後被Looper使用。
  • 4.系統的主執行緒在ActivityThread的main()為入口開啟主執行緒,其中定義了內部類Activity.H定義了一系列訊息型別,包含四大元件的啟動停止。
  • 5.MessageQueue和Looper是一對一關係,Handler和Looper是多對一

15.怎樣退出終止App

  • 1.自己設定一個Activity的棧,然後一個個finish()

16.Android IPC:Binder原理 ##

  • 1.在Activity和Service進行通訊的時候,用到了Binder。
    • 1.當屬於同個程式我們可以繼承Binder然後在Activity中對Service進行操作
    • 2.當不屬於同個程式,那麼要用到AIDL讓系統給我們建立一個Binder,然後在Activity中對遠端的Service進行操作。
  • 2.系統給我們生成的Binder:
    • 1.Stub類中有:介面方法的id,有該Binder的標識,有asInterface(IBinder)(讓我們在Activity中獲取實現了Binder的介面,介面的實現在Service裡,同程式時候返回Stub否則返回Proxy),有onTransact()這個方法是在不同程式的時候讓Proxy在Activity進行遠端呼叫實現Activity操作Service
    • 2.Proxy類是代理,在Activity端,其中有:IBinder mRemote(這就是遠端的Binder),兩個介面的實現方法不過是代理最終還是要在遠端的onTransact()中進行實際操作。
  • 3.哪一端的Binder是副本,該端就可以被另一端進行操作,因為Binder本體在定義的時候可以操作本端的東西。所以可以在Activity端傳入本端的Binder,讓Service端對其進行操作稱為Listener,可以用RemoteCallbackList這個容器來裝Listener,防止Listener因為經歷過序列化而產生的問題。
  • 4.當Activity端向遠端進行呼叫的時候,當前執行緒會掛起,當方法處理完畢才會喚醒。
  • 5.如果一個AIDL就用一個Service太奢侈,所以可以使用Binder池的方式,建立一個AIDL其中的方法是返回IBinder,然後根據方法中傳入的引數返回具體的AIDL。
  • 6.IPC的方式有:Bundle(在Intent啟動的時候傳入,不過是一次性的),檔案共享(對於SharedPreference是特例,因為其在記憶體中會有快取),使用Messenger(其底層用的也是AIDL,同理要操作哪端,就在哪端定義Messenger),AIDL,ContentProvider(在本程式中繼承實現一個ContentProvider,在增刪改查方法中呼叫本程式的SQLite,在其他程式中查詢),Socket

17.描述一次跨程式通訊

  • 1.client、proxy、serviceManager、BinderDriver、impl、service
  • 2.client發起一個請求service資訊的Binder請求到BinderDriver中,serviceManager發現BinderDiriver中有自己的請求 然後將clinet請求的service的資料返回給client這樣完成了一次Binder通訊
  • 3.clinet獲取的service資訊就是該service的proxy,此時呼叫proxy的方法,proxy將請求傳送到BinderDriver中,此時service的 Binder執行緒池迴圈發現有自己的請求,然後用impl就處理這個請求最後返回,這樣完成了第二次Binder通訊 4.中間client可掛起,也可以不掛起,有一個關鍵字oneway可以解決這個

18.android重要術語解釋

  • 1.ActivityManagerServices,簡稱AMS,服務端物件,負責系統中所有Activity的生命週期
  • 2.ActivityThread,App的真正入口。當開啟App之後,會呼叫main()開始執行,開啟訊息迴圈佇列,這就是傳說中的UI執行緒或者叫主執行緒。與ActivityManagerServices配合,一起完成Activity的管理工作
  • 3.ApplicationThread,用來實現ActivityManagerService與ActivityThread之間的互動。在ActivityManagerService需要管理相關Application中的Activity的生命週期時,通過ApplicationThread的代理物件與ActivityThread通訊。
  • 4.ApplicationThreadProxy,是ApplicationThread在伺服器端的代理,負責和客戶端的ApplicationThread通訊。AMS就是通過該代理與ActivityThread進行通訊的。
  • 5.Instrumentation,每一個應用程式只有一個Instrumentation物件,每個Activity內都有一個對該物件的引用。Instrumentation可以理解為應用程式的管家,ActivityThread要建立或暫停某個Activity時,都需要通過Instrumentation來進行具體的操作。
  • 6.ActivityStack,Activity在AMS的棧管理,用來記錄已經啟動的Activity的先後關係,狀態資訊等。通過ActivityStack決定是否需要啟動新的程式。
  • 7.ActivityRecord,ActivityStack的管理物件,每個Activity在AMS對應一個ActivityRecord,來記錄Activity的狀態以及其他的管理資訊。其實就是伺服器端的Activity物件的映像。
  • 8.TaskRecord,AMS抽象出來的一個“任務”的概念,是記錄ActivityRecord的棧,一個“Task”包含若干個ActivityRecord。AMS用TaskRecord確保Activity啟動和退出的順序。如果你清楚Activity的4種launchMode,那麼對這個概念應該不陌生。

19.理解Window和WindowManager

  • 1.Window用於顯示View和接收各種事件,Window有三種型別:應用Window(每個Activity對應一個Window)、子Window(不能單獨存在,附屬於特定Window)、系統window(Toast和狀態列)
  • 2.Window分層級,應用Window在1-99、子Window在1000-1999、系統Window在2000-2999.WindowManager提供了增刪改View三個功能。
  • 3.Window是個抽象概念:每一個Window對應著一個View和ViewRootImpl,Window通過ViewRootImpl來和View建立聯絡,View是Window存在的實體,只能通過WindowManager來訪問Window。
  • 4.WindowManager的實現是WindowManagerImpl其再委託給WindowManagerGlobal來對Window進行操作,其中有四個List分別儲存對應的View、ViewRootImpl、WindowManger.LayoutParams和正在被刪除的View
  • 5.Window的實體是存在於遠端的WindowMangerService中,所以增刪改Window在本端是修改上面的幾個List然後通過ViewRootImpl重繪View,通過WindowSession(每個應用一個)在遠端修改Window。
  • 6.Activity建立Window:Activity會在attach()中建立Window並設定其回撥(onAttachedToWindow()、dispatchTouchEvent()),Activity的Window是由Policy類建立PhoneWindow實現的。然後通過Activity#setContentView()呼叫PhoneWindow的setContentView。

20.Bitmap的處理

  • 1.當使用ImageView的時候,可能圖片的畫素大於ImageView,此時就可以通過BitmapFactory.Option來對圖片進行壓縮,inSampleSize表示縮小2^(inSampleSize-1)倍。
  • 2.BitMap的快取:
    • 1.使用LruCache進行記憶體快取。
    • 2.使用DiskLruCache進行硬碟快取。
    • 3.實現一個ImageLoader的流程:同步非同步載入、圖片壓縮、記憶體硬碟快取、網路拉取
      • 1.同步載入只建立一個執行緒然後按照順序進行圖片載入
      • 2.非同步載入使用執行緒池,讓存在的載入任務都處於不同執行緒
      • 3.為了不開啟過多的非同步任務,只在列表靜止的時候開啟圖片載入

21.如何實現一個網路框架(參考Volley)

  • 1.快取佇列,以url為key快取內容可以參考Bitmap的處理方式,這裡單獨開啟一個執行緒。
  • 2.網路請求佇列,使用執行緒池進行請求。
  • 3.提供各種不同型別的返回值的解析如String,Json,圖片等等。

22.ClassLoader的基礎知識

  • 1.雙親委託:一個ClassLoader類負責載入這個類所涉及的所有類,在載入的時候會判斷該類是否已經被載入過,然後會遞迴去他父ClassLoader中找。
  • 2.可以動態載入Jar通過URLClassLoader
  • 3.ClassLoader 隔離問題 JVM識別一個類是由:ClassLoader id+PackageName+ClassName。
  • 4.載入不同Jar包中的公共類:
    • 1.讓父ClassLoader載入公共的Jar,子ClassLoader載入包含公共Jar的Jar,此時子ClassLoader在載入公共Jar的時候會先去父ClassLoader中找。(只適用Java)
    • 2.重寫載入包含公共Jar的Jar的ClassLoader,在loadClass中找到已經載入過公共Jar的ClassLoader,也就是把父ClassLoader替換掉。(只適用Java)
    • 3.在生成包含公共Jar的Jar時候把公共Jar去掉。

23.外掛化框架描述:dynamicLoadApk為例子

  • 1.可以通過DexClassLoader來對apk中的dex包進行載入訪問
  • 2.如何載入資源是個很大的問題,因為宿主程式中並沒有apk中的資源,所以呼叫R資源會報錯,所以這裡使用了Activity中的實現ContextImpl的getAssets()和getResources()再加上反射來實現。
  • 3.由於系統啟動Activity有很多初始化動作要做,而我們手動反射很難完成,所以可以採用介面機制,將Activity的大部分生命週期提取成介面,然後通過代理Activity去呼叫外掛Activity的生命週期。同時如果像增加一個新生命週期方法的時候,只需要在介面中和代理中宣告一下就行。
  • 4.缺點:
    • 1.慎用this,因為在apk中使用this並不代表宿主中的activity,當然如果this只是表示自己的介面還是可以的。除此之外可以使用that代替this。
    • 2.不支援Service和靜態註冊的Broadcast
    • 3.不支援LaunchMode和Apk中Activity的隱式呼叫。

24.熱修復:Andfix為例子

  • 1.大致原理:apkpatch將兩個apk做一次對比,然後找出不同的部分。可以看到生成的apatch了檔案,字尾改成zip再解壓開,裡面有一個dex檔案。通過jadx檢視一下原始碼,裡面就是被修復的程式碼所在的類檔案,這些更改過的類都加上了一個_CF的字尾,並且變動的方法都被加上了一個叫@MethodReplace的annotation,通過clazz和method指定了需要替換的方法。然後客戶端sdk得到補丁檔案後就會根據annotation來尋找需要替換的方法。最後由JNI層完成方法的替換。
  • 2.無法新增新類和新的欄位、補丁檔案很容易被反編譯、加固平臺可能會使熱補丁功能失效

25.執行緒同步的問題,常用的執行緒同步

  • 1.sycn:保證了原子性、可見性、有序性
  • 2.鎖:保證了原子性、可見性、有序性
    • 1.自旋鎖:可以使執行緒在沒有取得鎖的時候,不被掛起,而轉去執行一個空迴圈。
      • 1.優點:執行緒被掛起的機率減少,執行緒執行的連貫性加強。用於對於鎖競爭不是很激烈,鎖佔用時間很短的併發執行緒。
      • 2.缺點:過多浪費CPU時間,有一個執行緒連續兩次試圖獲得自旋鎖引起死鎖
    • 2.阻塞鎖:沒得到鎖的執行緒等待或者掛起,Sycn、Lock
    • 3.可重入鎖:一個執行緒可多次獲取該鎖,Sycn、Lock
    • 4.悲觀鎖:每次去拿資料的時候都認為別人會修改,所以會阻塞全部其他執行緒 Sycn、Lock
    • 5.樂觀鎖:每次去拿資料的時候都認為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個資料,可以使用版本號等機制。cas
    • 6.顯示鎖和內建鎖:顯示鎖用Lock來定義、內建鎖用synchronized。
    • 7.讀-寫鎖:為了提高效能,Java提供了讀
  • 3.volatile
    • 1.只能保證可見性,不能保證原子性
    • 2.自增操作有三步,此時多執行緒寫會出現問題
  • 4.cas
    • 1.操作:記憶體值V、舊的預期值A、要修改的值B,當且僅當預期值A和記憶體值V相同時,將記憶體值修改為B並返回true,否則什麼都不做並返回false。
    • 2.解釋:本地副本為A,共享記憶體為V,執行緒A要把V修改成B。某個時刻執行緒A要把V修改成B,如果A和V不同那麼就表示有其他執行緒在修改V,此時就表示修改失敗,否則表示沒有其他執行緒修改,那麼把V改成B。
    • 3.侷限:如果V被修改成V1然後又被改成V,此時cas識別不出變化,還是認為沒有其他執行緒在修改V,此時就會有問題
    • 4.侷限解決:將V帶上版本。
  • 5.執行緒不安全到底是怎麼回事:
    • 1.一個執行緒寫,多個執行緒讀的時候,會造成寫了一半就去讀
    • 2.多執行緒寫,會造成髒資料

26.Asynctask和執行緒池,GC相關(怎麼判斷哪些記憶體該GC,GC演算法)

  • 1.Asynctask:非同步任務類,單執行緒執行緒池+Handler
  • 2.執行緒池:
    • 1.ThreadPoolExecutor:通過Executors可以構造單執行緒池、固定數目執行緒池、不固定數目執行緒池。
    • 2.ScheduledThreadPoolExecutor:可以延時呼叫執行緒或者延時重複排程執行緒。
  • 3.GC相關:重要
    • 1.搜尋演算法:
      • 1.引用計數
      • 2.圖搜尋,可達性分析
    • 2.回收演算法:
      • 1.標記清除複製:用於青年代
      • 2.標記整理:用於老年代
    • 3.堆分割槽:
      • 1.青年區eden 80%、survivor1 10%、survivor2 10%
      • 2.老年區
    • 4.虛擬機器棧分割槽:
      • 1.區域性變數表
      • 2.運算元棧
      • 3.動態連結
      • 4.方法返回地址
    • 5.GC Roots:
      • 1.虛擬機器棧(棧楨中的本地變數表)中的引用的物件
      • 2.方法區中的類靜態屬性引用的物件
      • 3.方法區中的常量引用的物件
      • 4.本地方法棧中JNI的引用的物件

27.網路

  • 1.ARP協議:在IP乙太網中,當一個上層協議要發包時,有了該節點的IP地址,ARP就能提供該節點的MAC地址。
  • 2.HTTP HTTPS的區別:
    • 1.HTTPS使用TLS(SSL)進行加密
    • 2.HTTPS預設工作在TCP協議443埠
    • 3.它的工作流程一般如以下方式:
      • 1.完成TCP三次同步握手
      • 2.客戶端驗證伺服器數字證照,通過,進入步驟3
      • 3.DH演算法協商對稱加密演算法的金鑰、hash演算法的金鑰
      • 4.SSL安全加密隧道協商完成
      • 5.網頁以加密的方式傳輸,用協商的對稱加密演算法和金鑰加密,保證資料機密性;用協商的hash演算法進行資料完整性保護,保證資料不被篡改
    • 3.http請求包結構,http返回碼的分類,400和500的區別
      • 1.包結構:
        • 1.請求:請求行、頭部、資料
        • 2.返回:狀態行、頭部、資料
      • 2.http返回碼分類:1到5分別是,訊息、成功、重定向、客戶端錯誤、服務端錯誤
    • 4.Tcp
      • 1.可靠連線,三次握手,四次揮手
        • 1.三次握手:防止了伺服器端的一直等待而浪費資源,例如只是兩次握手,如果s確認之後c就掉線了,那麼s就會浪費資源
          • 1.syn-c = x,表示這訊息是x序號
          • 2.ack-s = x + 1,表示syn-c這個訊息接收成功。syn-s = y,表示這訊息是y序號。
          • 3.ack-c = y + 1,表示syn-s這條訊息接收成功
      • 2.四次揮手:TCP是全雙工模式
        • 1.fin-c = x , 表示現在需要關閉c到s了。ack-c = y,表示上一條s的訊息已經接收完畢
        • 2.ack-s = x + 1,表示需要關閉的fin-c訊息已經接收到了,同意關閉
        • 3.fin-s = y + 1,表示s已經準備好關閉了,就等c的最後一條命令
        • 4.ack-c = y + 1,表示c已經關閉,讓s也關閉
      • 3.滑動視窗,停止等待、後退N、選擇重傳
      • 4.擁塞控制,慢啟動、擁塞避免、加速遞減、快重傳快恢復

28.資料庫效能優化:索引和事務,需要找本專門的書大概瞭解一下

29.13.APK打包流程和其內容

  • 1.流程
    • 1.aapt生成R檔案
      • 2.aidl生成java檔案
      • 3.將全部java檔案編譯成class檔案
      • 4.將全部class檔案和第三方包合併成dex檔案
      • 5.將資源、so檔案、dex檔案整合成apk
      • 6.apk簽名
      • 7.apk位元組對齊
  • 2.內容:so、dex、asset、資原始檔

30.網路劫持的型別原理:可以百度一下了解一下具體概念

  • 1.DNS劫持、欺騙、汙染
  • 2.http劫持:重定向、注入js,http注入、報文擴充套件

31.java類載入過程:

  • 1.載入時機:建立例項、訪問靜態變數或方法、反射、載入子類之前
  • 2.驗證:驗證檔案格式、後設資料、位元組碼、符號引用的正確性
  • 3.載入:根據全類名獲取檔案位元組流、將位元組流轉化為靜態儲存結構放入方法區、生成class物件
  • 4.準備:在堆上為靜態變數劃分記憶體
  • 5.解析:將常量池中的符號引用轉換為直接引用
  • 6.初始化:初始化靜態變數
  • 7.書籍推薦:深入理解java虛擬機器,部落格推薦:Java/Android阿里面試JVM部分理解

32.retrofit的瞭解

  • 1.動態代理建立一個介面的代理類
  • 2.通過反射解析每個介面的註解、入參構造http請求
  • 3.獲取到返回的http請求,使用Adapter解析成需要的返回值。

33.bundle的資料結構,如何儲存

  • 1.鍵值對儲存
  • 2.傳遞的資料可以是boolean、byte、int、long、float、double、string等基本型別或它們對應的陣列,也可以是物件或物件陣列。
  • 3.當Bundle傳遞的是物件或物件陣列時,必須實現Serializable 或Parcelable介面

34.listview內點選buttom並移動的事件流完整攔截過程:

  • 1.點下按鈕的時候:
    • 1.產生了一個down事件,activity-->phoneWindow-->ViewGroup-->ListView-->botton,中間如果有重寫了攔截方法,則事件被該view攔截可能消耗。
    • 2.沒攔截,事件到達了button,這個過程中建立了一條事件傳遞的view連結串列
    • 3.到button的dispatch方法-->onTouch-->view是否可用-->Touch代理
  • 2.移動點選按鈕的時候:
    • 1.產生move事件,listView中會對move事件做攔截
    • 2.此時listView會將該滑動事件消費掉
    • 3.後續的滑動事件都會被listView消費掉
  • 3.手指抬起來時候:前面建立了一個view連結串列,listView的父view在獲取事件的時候,會直接取連結串列中的listView讓其進行事件消耗。

35.service的意義:不需要介面,在後臺執行的程式

36.android的IPC通訊方式,執行緒(程式間)通訊機制有哪些

  • 1.ipc通訊方式:binder、contentprovider、socket
  • 2.作業系統程式通訊方式:共享記憶體、socket、管道

37.作業系統程式和執行緒的區別

  • 1.簡而言之,一個程式至少有一個程式,一個程式至少有一個執行緒.
  • 2.執行緒的劃分尺度小於程式,使得多執行緒程式的併發性高。
  • 3.另外,程式在執行過程中擁有獨立的記憶體單元,而多個執行緒共享記憶體,從而極大地提高了程式的執行效率。
  • 4.多執行緒的意義在於一個應用程式中,有多個執行部分可以同時執行。有將多個執行緒看做多個獨立的應用,來實現程式的排程和管理以及資源分配

38.HashMap的實現過程:Capacity就是buckets的數目,Load factor就是buckets填滿程度的最大比例。如果對迭代效能要求很高的話不要把capacity設定過大,也不要把load factor設定過小。

  • 1.簡單來說HashMap就是一個會自動擴容的陣列連結串列
  • 2.put過程
    • 1.對key的hashCode()做hash,然後再計算index;
    • 2.如果沒碰撞直接放到bucket裡;
    • 3.如果碰撞了,以連結串列的形式存在buckets後;
    • 4.如果碰撞導致連結串列過長(大於等於TREEIFY_THRESHOLD),就把連結串列轉換成紅黑樹;
    • 5.如果節點已經存在就替換old value(保證key的唯一性)
    • 6.如果bucket滿了(超過load factor*current capacity),就要resize。
  • 3.resize:當put時,如果發現目前的bucket佔用程度已經超過了Load Factor所希望的比例,那麼就會發生resize。在resize的過程,簡單的說就是把bucket擴充為2倍,之後重新計算index,把節點再放到新的bucket中
  • 4.get過程
    • 1.根據key的hash算出陣列下表
    • 2.使用equals遍歷連結串列進行比較

39.mvc、mvp、mvvm:

  • 1.mvc:資料、View、Activity,View將操作反饋給Activity,Activitiy去獲取資料,資料通過觀察者模式重新整理給View。迴圈依賴
    • 1.Activity重,很難單元測試
    • 2.View和Model耦合嚴重
  • 2.mvp:資料、View、Presenter,View將操作給Presenter,Presenter去獲取資料,資料獲取好了返回給Presenter,Presenter去重新整理View。PV,PM雙向依賴
    • 1.介面爆炸
    • 2.Presenter很重
  • 3.mvvm:資料、View、ViewModel,View將操作給ViewModel,ViewModel去獲取資料,資料和介面繫結了,資料更新介面更新。
    • 1.viewModel的業務邏輯可以單獨拿來測試
    • 2.一個view 對應一個 viewModel 業務邏輯可以分離,不會出現全能類
    • 3.資料和介面繫結了,不用寫垃圾程式碼,但是複用起來不舒服

40.java的執行緒如何實現

  • 1.Thread繼承
  • 2.Runnale
  • 3.Future
  • 4.執行緒池

41.ArrayList 如何刪除重複的元素或者指定的元素;

  • 1.刪除重複:Set
  • 2.刪除指定:迭代器

42.如何設計在 UDP 上層保證 UDP 的可靠性傳輸;

  • 1.簡單來講,要使用UDP來構建可靠的面向連線的資料傳輸,就要實現類似於TCP協議的超時重傳,有序接受,應答確認,滑動視窗流量控制等機制,等於說要在傳輸層的上一層(或者直接在應用層)實現TCP協議的可靠資料傳輸機制。
  • 2.比如使用UDP資料包+序列號,UDP資料包+時間戳等方法,在伺服器端進行應答確認機制,這樣就會保證不可靠的UDP協議進行可靠的資料傳輸。
  • 3.基於udp的可靠傳輸協議有:RUDP、RTP、UDT

43.Java 中內部類為什麼可以訪問外部類

  • 1.因為內部類建立的時候,需要外部類的物件,在內部類物件建立的時候會把外部類的引用傳遞進去

44.設計移動端的聯絡人儲存與查詢的功能,要求快速搜尋聯絡人,可以用到哪些資料結構?資料庫索引,平衡二叉樹(B樹、紅黑樹)

45.紅黑樹特點

  • 1.root節點和葉子節點是黑色
  • 2.紅色節點後必須為黑色節點
  • 3.從root到葉子每條路徑的黑節點數量相同

46.linux非同步和同步i/o:

  • 1.同步:對於client,client一直等待,但是client不掛起:主執行緒呼叫
  • 2.非同步:對於client,client發起請求,service好了再回撥client:其他執行緒呼叫,呼叫完成之後進行回撥
  • 3.阻塞:對於service,在準備io的時候會將service端掛起,直至準備完成然後喚醒service:bio
  • 3.非阻塞:對於service,在準備io的時候不會將service端掛起,而是service一直去輪詢判斷io是否準備完成,準備完成了就進行操作:nio、linux的select、poll、epoll
  • 4.多路複用io:非阻塞io的一種優化,java nio,用一個執行緒去輪詢多個 io埠是否可用,如果一個可用就通知對應的io請求,這使用一個執行緒輪詢可以大大增強效能。
    • 1.我可以採用 多執行緒+ 阻塞IO 達到類似的效果,但是由於在多執行緒 + 阻塞IO 中,每個socket對應一個執行緒,這樣會造成很大的資源佔用。
    • 2.而在多路複用IO中,輪詢每個socket狀態是核心在進行的,這個效率要比使用者執行緒要高的多。
  • 5.非同步io:aio,使用者執行緒完全不感知io的進行,所有操作都交給核心,io完成之後核心通知使用者執行緒。
    • 1.這種io才是非同步的,2、3、4都是同步io,因為核心進行資料拷貝的過程都會讓使用者執行緒阻塞。
    • 2.非同步IO是需要作業系統的底層支援,也就是核心支援,Java 7中,提供了Asynchronous IO

47.ConcurrentHashMap內部實現,HashTable的實現被廢棄的原因:

  • 1.HashTable容器在競爭激烈的併發環境下表現出效率低下的原因,是因為所有訪問HashTable的執行緒都必須競爭同一把鎖,那假如容器裡有多把鎖,每一把鎖用於鎖容器其中一部分資料,那麼當多執行緒訪問容器裡不同資料段的資料時,執行緒間就不會存在鎖競爭,從而可以有效的提高併發訪問效率,這就是ConcurrentHashMap所使用的鎖分段技術,首先將資料分成一段一段的儲存,然後給每一段資料配一把鎖,當一個執行緒佔用鎖訪問其中一個段資料的時候,其他段的資料也能被其他執行緒訪問。
  • 2.ConcurrentHashMap是由Segment陣列結構和HashEntry陣列結構組成。Segment是一種可重入鎖ReentrantLock,在ConcurrentHashMap裡扮演鎖的角色,HashEntry則用於儲存鍵值對資料。一個ConcurrentHashMap裡包含一個Segment陣列,Segment的結構和HashMap類似,是一種陣列和連結串列結構, 一個Segment裡包含一個HashEntry陣列,每個HashEntry是一個連結串列結構的元素,每個Segment守護者一個HashEntry陣列裡的元素,當對HashEntry陣列的資料進行修改時,必須首先獲得它對應的Segment鎖。

48.HandlerThread是什麼

  • 1.MessageQueue + Looper + Handler

49.IntentService是什麼

  • 1.含有HandlerThread的Service,可以多次startService()來多次在子執行緒中進行 onHandlerIntent()的呼叫。

50.class和dex

  • 1.dvm執行的是dex格式檔案,jvm執行的是class檔案,android程式編譯完之後生產class檔案。然後dex工具會把class檔案處理成dex檔案,然後把資原始檔和.dex檔案等打包成apk檔案。
  • 2.dvm是基於暫存器的虛擬機器,而jvm執行是基於虛擬棧的虛擬機器。暫存器存取速度比棧快的多,dvm可以根據硬體實現最大的優化,比較適合移動裝置。
  • 3.class檔案存在很多的冗餘資訊,dex工具會去除冗餘資訊,並把所有的class檔案整合到dex檔案中。減少了I/O操作,提高了類的查詢速度

51.記憶體洩漏

  • 1.其他執行緒持有一個Listener,Listener操作activity。那麼線上程麼有完畢的時候,activity關閉了,原本是要被回收的但是,不能被回收。
  • 2.例如Handler導致的記憶體洩漏,Handler就相當於Listener。
  • 3.在activity關閉的時候注意停止執行緒,或者將Listener的註冊取消
  • 3.使用弱引用,這樣即使Listener持有了activity,在GC的時候還是會被回收
  • 4.工具:LeakCanary

52.過度繪製、卡頓優化:

  • 1.過度繪製:
    • 1.移除Window預設的Background:getWidow.setBackgroundDrawable(null);
    • 2.移除XML佈局檔案中非必需的Background
    • 3.減少佈局巢狀(扁平化的一個體現,減少View數的深度,也就減少了View樹的遍歷時間,渲染的時候,前後期的工作,總是按View樹結點來)
    • 4.在引入佈局檔案裡面,最外層可以用merge替代LinearLayout,RelativeLayout,這樣把子UI元素直接銜接在include位置
    • 5.工具:HierarchyViewer 檢視檢視層級
  • 2.卡頓優化:16ms資料更新

53.apk瘦身:

  • 1.classes.dex:通過程式碼混淆,刪掉不必要的jar包和程式碼實現該檔案的優化
  • 2.資原始檔:通過Lint工具掃描程式碼中沒有使用到的靜態資源
  • 3.圖片資源:使用tinypng和webP,下面詳細介紹圖片資源優化的方案,向量圖
  • 4.SO檔案將不用的去掉,目前主流app一般只放一個arm的so包

54.ANR的形成,各個元件上出現ARN的時間限制是多少

  • 1.只要是主執行緒耗時的操作就會ARN 如io
  • 2.broadcast超時時間為10秒 按鍵無響應的超時時間為5秒 前臺service無響應的超時時間為20秒,後臺service為200秒

55.Serializable和Parcelable 的區別

  • 1.P 消耗記憶體小
  • 2.網路傳輸用S 程式內使用P
  • 3.S將資料持久化方便
  • 4.S使用了反射 容易觸發垃圾回收 比較慢

56.Sharedpreferences原始碼簡述

  • 1.儲存於硬碟上的xml鍵值對,資料多了會有效能問題
  • 2.ContextImpl記錄著SharedPreferences的重要資料,檔案路徑和例項的鍵值對
  • 3.在xml檔案全部內載入到記憶體中之前,讀取操作是阻塞的,在xml檔案全部內載入到記憶體中之後,是直接讀取記憶體中的資料
  • 4.apply因為是非同步的沒有返回值, commit是同步的有返回值能知道修改是否提交成功
  • 5.多併發的提交commit時,需等待正在處理的commit資料更新到磁碟檔案後才會繼續往下執行,從而降低效率; 而apply只是原子更新到記憶體,後呼叫apply函式會直接覆蓋前面記憶體資料,從一定程度上提高很多效率。 3.edit()每次都是建立新的EditorImpl物件.
  • 6.部落格推薦:全面剖析SharedPreferences

57.作業系統如何管理記憶體的:

  • 1.使用暫存器進行將程式地址和實體記憶體進行對映
  • 2.虛擬記憶體進行記憶體對映到硬碟上增大記憶體
  • 3.虛擬記憶體是進行記憶體分頁管理
  • 4.頁表實現分頁,就是 頁+地址偏移。
  • 5.如果程式的記憶體在硬碟上,那麼就需要用頁置換演算法來將其調入記憶體中:先進先出、最近未使用最少等等
  • 6.部落格推薦:現代作業系統部分章節筆記

58.瀏覽器輸入地址到返回結果發生了什麼

  • 1.DNS解析
  • 2.TCP連線
  • 3.傳送HTTP請求
  • 4.伺服器處理請求並返回HTTP報文
  • 5.瀏覽器解析渲染頁面
  • 6.連線結束

59.java泛型型別擦除發生在什麼時候,萬用字元有什麼需要注意的。

60.activity的生命週期

  • 1.a啟動b,後退鍵再到a的生命週期流程:a.create-->a.start-->a.resume-->a.pause-->b.create-->b.start-->b.resume-->b介面繪製-->a.stop-->b.pause-->b.stop-->b.destroy-->a.restart-->a.start-->a.resume
  • 2.意外銷燬會呼叫saveInstance,重新恢復的時候回撥用restoreInstance。儲存資料的時候使用了委託機制,從activity-->window-->viewGroup-->view 會遞迴呼叫save來保持本view的資料,restore則是遞迴恢復本view資料。我們可以在裡面做一些自己需要的資料操作。

61.面試常考的演算法

  • 1.快排、堆排序為首的各種排序演算法
  • 2.連結串列的各種操作:判斷成環、判斷相交、合併連結串列、倒數K個節點、尋找成環節點
  • 3.二叉樹、紅黑樹、B樹定義以及時間複雜度計算方式
  • 4.動態規劃、貪心演算法、簡單的圖論
  • 5.推薦書籍:演算法導論,將圖論之前的例子寫一遍

62.Launcher程式啟動另外一個程式的過程:啟動一個app

63.開源框架原始碼

  • 1.Fresco
    • 1.mvc框架:
      • 1.Controller控制資料顯示在Hierarchy中的Drawable的顯隱
      • 2.ImagePipeline在Controller中負責進行資料獲取,返回的資料是CloseableImage
      • 3.Drawee把除了初始化之外的操作全部交給Holder去做,Holder持有Controller和Hierarchy
    • 2.Drawable層次以及繪製:
      • 1.如果要繪製一次Drawable就呼叫invalidateSelf()來觸發onDraw()
      • 2.Drawable分為:容器類(儲存一些Drawable)、自我繪製類(進度條)、圖形變換類(scale、rotate、矩陣變換)、動畫類(內部不斷重新整理,進行webp和gif的幀繪製)
      • 3.ImagePipeline返回的CloseableImage是由一個個DrawableFactory解析成Drawable的
      • 4.webp和gif動畫是由jni程式碼解析的,然後其他靜態圖片是根據不同的android平臺使用BitmapFactory來解析的
    • 3.職責鏈模式:producer不做操作標n,表示只是提供一個consumer。獲取圖片--》解碼圖片快取Producer--》後臺執行緒Producer--》client圖片處理producer(n)--》解碼producer(n)--》旋轉或剪裁producer(n)--》編碼圖片記憶體快取producer--》讀硬碟快取producer--》寫硬碟快取producer(n)--》網路producer提供CloseableImage《--解碼圖片快取consumer《--client圖片處理consumer《--解碼consumer《--旋轉或剪裁consumer《--編碼圖片記憶體快取consumer《--寫硬碟快取consumer《--圖片資料
    • 4.記憶體快取:
      • 1.一個CountingLruMap儲存已經沒有被引用的快取條目,一個CountingLruMap儲存所有的條目包括沒有引用的條目。每當快取策略改變和一定時間快取配置的更新的時候,就會將 待銷燬條目Map中的條目一個個移除,直到快取大小符合配置。
      • 2.這裡的引用計數是用Fresco元件實現的引用計數器。
      • 3.快取有一個代理類,用來追蹤快取的存取。
      • 4.CountingLruMap是使用LinkedHashMap來儲存資料的。
    • 5.硬碟快取:
      • 1.DefaultDiskStorage使用Lru策略。
      • 2.為了不讓所有的檔案集中在一個檔案中,建立很多命名不同的資料夾,然後使用hash演算法把快取檔案分散
      • 3.DiskStorageCache封裝了DefaultDiskStorage,不僅進行快取存取追蹤,並且其在記憶體裡面維持著一個 <key,value> 的鍵值對,因為檔案修改頻繁,所有隻是定時重新整理,因此如果在記憶體中找不到,還要去硬碟中找一次。
      • 4.刪除硬碟的快取只出現在硬碟資料大小超限的時候,此時同時也會刪除快取中的key,所以不會出現記憶體中有key,但是硬碟上沒有的情況。
      • 5.在插入硬碟資料的時候,採用的是插入器的形式。返回一個Inserter,在Inserter.writeData()中傳入一個CallBack(裡面封裝了客戶端插入資料的邏輯和檔案引用),讓內部實現呼叫CallBack的邏輯來插入檔案資料,前面寫的檔案字尾是.temp,只有呼叫commit()之後才會修改字尾,讓檔案對客戶端可見。
      • 6.使用了java提供的FileTreeVisitor來遍歷檔案
    • 6.物件池:
      • 1.使用陣列來儲存一個桶,桶內部是一個Queue。陣列下標是資料申請記憶體的byte大小,桶內部的Queue存的是記憶體塊的。所以陣列使用的是稀疏陣列
      • 2.申請記憶體的方式有兩種 1.java堆上開闢的記憶體 2.ashme 的本地記憶體中開闢的記憶體
    • 7.設計模式:Builder、職責鏈、觀察者、代理、組合、享元、介面卡、裝飾者、策略、生產者消費者、提供者
    • 8.自定義計數引用:類似c++智慧指標
      • 1.使用一個靜態IdentityHashMap <儲存需要被計數引用的物件,其被引用的次數>
      • 2.用SharedReference分裝需要被計數引用的物件,提供一個銷燬資源的銷燬器,提供一個靜態工廠方法來複制自己,複製一個引用計數加一。提供一個方法銷燬自己,表示自己需要變成無人引用的物件了,此時引用計數減一。
      • 3.引用計數歸零,銷燬器將銷燬資源,如bitmap的recycle或者是jni記憶體呼叫jni方法歸還記憶體。
    • 9.部落格推薦:從零開始擼一個Fresco之硬碟快取從零開始擼一個Fresco之gif和Webp動畫從零開始擼一個Fresco之記憶體快取從零開始擼一個Fresco之總結
  • 2.oKhttp:
    • 1.同步和非同步:
      • 1.非同步使用了Dispatcher來將儲存在 Deque 中的請求分派給執行緒池中各個執行緒執行。
      • 2.當任務執行完成後,無論是否有異常,finally程式碼段總會被執行,也就是會呼叫Dispatcher的finished函式,它將正在執行的任務Call從佇列runningAsyncCalls中移除後,主動的把快取佇列向前走了一步。
    • 2.連線池:
      • 1.一個Connection封裝了一個socket,ConnectionPool中儲存s著所有的Connection,StreamAllocation是引用計數的一個單位
      • 2.當一個請求獲取一個Connection的時候要傳入一個StreamAllocation,Connection中存著一個弱引用的StreamAllocation列表,每當上層應用引用一次Connection,StreamAllocation就會加一個。反之如果上層應用不使用了,就會刪除一個。
      • 3.ConnectionPool中會有一個後臺任務定時清理StreamAllocation列表為空的Connection。5分鐘時間,維持5個socket
    • 3.選擇路線與建立連線
      • 1.選擇路線有兩種方式:
        • 1.無代理,那麼在本地使用DNS查詢到ip,注意結果是陣列,即一個域名有多個IP,這就是自動重連的來源
        • 2.有代理HTTP:設定socket的ip為代理地址的ip,設定socket的埠為代理地址的埠
        • 3.代理好處:HTTP代理會幫你在遠端伺服器進行DNS查詢,可以減少DNS劫持。
      • 2.建立連線
        • 1.連線池中已經存在連線,就從中取出(get)RealConnection,如果沒有命中就進入下一步
        • 2.根據選擇的路線(Route),呼叫Platform.get().connectSocket選擇當前平臺Runtime下最好的socket庫進行握手
        • 3.將建立成功的RealConnection放入(put)連線池快取
        • 4.如果存在TLS,就根據SSL版本與證照進行安全握手
        • 5.構造HttpStream並維護剛剛的socket連線,管道建立完成
    • 4.職責鏈模式:快取、重試、建立連線等功能存在於攔截器中網路請求相關,主要是網路請求優化。網路請求的時候遇到的問題
    • 5.部落格推薦:Android資料層架構的實現 上篇Android資料層架構的實現 下篇
  • 3.okio
    • 1.簡介;
      • 1.sink:自己--》別人
      • 2.source:別人--》自己
      • 3.BufferSink:有快取區域的sink
      • 4.BufferSource:有快取區域的source
      • 5.Buffer:實現了3、4的快取區域,內部有Segment的雙向連結串列,在在轉移資料的時候,只需要將指標轉移指向就行
    • 2.比java io的好處:
      • 1.減少記憶體申請和資料拷貝
      • 2.類少,功能齊全,開發效率高
    • 3.內部實現:
      • 1.Buffer的Segment雙向連結串列,減少資料拷貝
      • 2.Segment的內部byte陣列的共享,減少資料拷貝
      • 3.SegmentPool的共享和回收Segment
      • 4.sink和source中被實際操作的其實是Buffer,Buffer可以充當sink和source
      • 5.最終okio只是對java io的封裝,所有操作都是基於java io 的

寫在最後:能看到這裡的人,我挺佩服你的.這篇文章是我在頭條面試之前整理的,最後**80%**的題目都命中了,所以祝你好運.

不販賣焦慮,也不標題黨。分享一些這個世界上有意思的事情。題材包括且不限於:科幻、科學、科技、網際網路、程式設計師、計算機程式設計。下面是我的微信公眾號:世界上有意思的事,乾貨多多等你來看。

世界上有意思的事

相關文章