本文首發於微信公衆號——世界上有意思的事,搬運轉載請註明出處,不然將追究版權責任。微信號:a1018998632,交流qq羣:859640274html
上次寫這篇文章的時候也差很少是一年前了,這一年我兜兜轉轉從android到java又回到android,校招面了不少大廠,阿里、京東、小米、頭條、知乎、騰訊、有贊,也收穫了幾個offer。感謝你們的關注,讓我在簡書上面也混到了一個簡書優秀程序員做者的稱號,因此爲了回饋你們,一篇最徹底的android面經誕生了。這是我集合了牛客網、百度、簡書等網站的幾十篇面經和我本身面試的經歷的合集,但願你們喜歡。(ps:裏面固然會有紕漏,若是有問題歡迎你們留言或者加我QQ討論)java
1.安卓事件分發機制,請詳細說下整個流程
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-9九、子Window在1000-199九、系統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.搜索算法:
- 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雙向依賴
- 3.mvvm:數據、View、ViewModel,View將操做給ViewModel,ViewModel去獲取數據,數據和界面綁定了,數據更新界面更新。
- 1.viewModel的業務邏輯能夠單獨拿來測試
- 2.一個view 對應一個 viewModel 業務邏輯能夠分離,不會出現全能類
- 3.數據和界面綁定了,不用寫垃圾代碼,可是複用起來不舒服
40.java的線程如何實現
- 1.Thread繼承
- 2.Runnale
- 3.Future
- 4.線程池
41.ArrayList 如何刪除重複的元素或者指定的元素;
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纔是異步的,二、三、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:實現了三、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%**的題目都命中了,因此祝你好運.linux
不販賣焦慮,也不標題黨。分享一些這個世界上有意思的事情。題材包括且不限於:科幻、科學、科技、互聯網、程序員、計算機編程。下面是個人微信公衆號:世界上有意思的事,乾貨多多等你來看。 android