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

這一年我兜兜轉轉從android到java又回到android,校招面了很多大廠,阿里、京東、小米、頭條、知乎、騰訊、有贊,也收穫了幾個offer。感謝大家的關注,為了回饋大家,一篇

最完全的android面經

誕生了。這是我集合了牛客網、百度等網站的

幾十篇面經

和我自己面試的經歷的合集,希望大家喜歡。

1。android事件分發機制,請詳細說下整個流程

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

2。android 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四大元件的載入過程,請詳細介紹下

1。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。請求:請求行、頭部、資料2。返回:狀態行、頭部、資料1。包結構:2。http返回碼分類:1到5分別是,訊息、成功、重定向、客戶端錯誤、服務端錯誤4。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也關閉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這條訊息接收成功1。可靠連線,三次握手,四次揮手2。四次揮手:TCP是全雙工模式3。滑動視窗,停止等待、後退N、選擇重傳4。擁塞控制,慢啟動、擁塞避免、加速遞減、快重傳快恢復28。資料庫效能最佳化:索引和事務,需要找本專門的書大概瞭解一下 29。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泛型型別擦除發生在什麼時候,萬用字元有什麼需要注意的。

1。發生在編譯的時候

2。PECS,extends善於提供精確的物件 A是B的子集,Super善於插入精確的物件 A是B的超集

3。部落格推薦:

Effective Java筆記(不含反序列化、併發、註解和列舉)

android阿里面試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,不僅進行快取存取追蹤,並且其在記憶體裡面維持著一個 的鍵值對,因為檔案修改頻繁,所有隻是定時重新整理,因此如果在記憶體中找不到,還要去硬碟中找一次。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。部落格推薦:Android Fresco原始碼文件翻譯、從零開始擼一個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。連線池中已經存在連線,就從中取出(get)RealConnection,如果沒有命中就進入下一步2。根據選擇的路線(Route),呼叫Platform。get()。connectSocket選擇當前平臺Runtime下最好的socket庫進行握手3。將建立成功的RealConnection放入(put)連線池快取4。如果存在TLS,就根據SSL版本與證書進行安全握手5。構造HttpStream並維護剛剛的socket連線,管道建立完成1。無代理,那麼在本地使用DNS查詢到ip,注意結果是陣列,即一個域名有多個IP,這就是自動重連的來源2。有代理HTTP:設定socket的ip為代理地址的ip,設定socket的埠為代理地址的埠3。代理好處:HTTP代理會幫你在遠端伺服器進行DNS查詢,可以減少DNS劫持。1。選擇路線有兩種方式:2。建立連線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%

的題目都命中了,所以祝你好運。