歡迎光臨
每天分享高質量文章

2019校招Android面試題解

作者:minmin_1123

鏈接:https://www.jianshu.com/p/718aa3c1a70b

大家還記得,一篇2019 Android 秋招提前批面試總結 (已拿BAT等6家口頭offer),作者借國慶期間,對文中歸納的校招面試題做個小解答(除演算法,演算法部分另做總結),作者全文特別特別長,一共分為上中下三篇,本篇文章由於篇幅問題,只能小部分內容,具體大家去原文查看即可。 感謝作者的無私奉獻!

1、前言

過去的這一個月,忙過了爆炸開學周、結束了推免、和朋友出門閑游放鬆了幾天、回家休息慶祝了中秋節國慶節,最後才是這篇姍姍來遲的題解,請原諒我的懶吧!

題解乃見仁見智之事,以下只是自己的看法,僅供參考,因本人水平有限,如有問題歡迎指正。

之所以是題解1.0,是希望隨著學習的深入,以後能更深刻的理解,再出個題解2.0最好不過了。

以下為作者文章中部分內容。

2、View 相關題解

Q:MotionEvent是什麼?包含幾種事件?什麼條件下會產生?


技術點:View觸控

參考回答:MotionEvent是手指觸摸屏幕鎖產生的一系列事件。包含的事件有:

  • ACTION_DOWN:手指剛接觸屏幕

  • ACTION_MOVE:手指在屏幕上滑動

  • ACTION_UP:手指在屏幕上鬆開的一瞬間

  • ACTION_CANCEL:手指保持按下操作,並從當前控制元件轉移到外層控制元件時會觸發

Q:scrollTo()和scrollBy()的區別?


技術點:View滑動

參考回答:scrollBy內部呼叫了scrollTo,它是基於當前位置的相對滑動;而scrollTo是絕對滑動,因此如果利用相同輸入引數多次呼叫scrollTo()方法,由於View初始位置是不變只會出現一次View滾動的效果而不是多次。

引申:兩者都只能對view內容進行滑動,而不能使view本身滑動,且非平滑,可使用Scroller有過渡滑動的效果

Q:Scroller中最重要的兩個方法是什麼?主要目的是?

技術點:View滑動


思路:從Scroller實現滑動的具體過程出發,

參考回答:Scroller實現滑動的具體過程:

  • 在MotionEvent.ACTION_UP事件觸發時呼叫startScroll()方法,該方法並沒有進行實際的滑動操作,而是記錄滑動相關量

  • 馬上呼叫invalidate/postInvalidate()方法,請求View重繪,導致View.draw方法被執行

  • 緊接著會呼叫View.computeScroll()方法,此方法是空實現,需要自己處理邏輯。具體邏輯是:先判斷computeScrollOffset(),若為true(表示滾動未結束),則執行scrollTo()方法,它會再次呼叫postInvalidate(),如此反覆執行,直到傳回值為false。流程圖如下:

其中,最重要的兩個方法是startScroll()和computeScroll()

Q:談一談View的事件分發機制?

技術點:View事件分發

思路:從分發本質、傳遞順序、核心方法展開

參考回答:

事件分發本質:就是對MotionEvent事件分發的過程。即當一個MotionEvent產生了以後,系統需要將這個點擊事件傳遞到一個具體的View上。

點擊事件的傳遞順序:Activity(Window) -> ViewGroup -> View

三個主要方法:

  • dispatchTouchEvent:進行事件的分發(傳遞)。傳回值是 boolean 型別,受當前onTouchEvent和下級view的dispatchTouchEvent影響

  • onInterceptTouchEvent:對事件進行攔截。該方法只在ViewGroup中有,View(不包含 ViewGroup)是沒有的。一旦攔截,則執行ViewGroup的onTouchEvent,在ViewGroup中處理事件,而不接著分發給View。且只呼叫一次,所以後面的事件都會交給ViewGroup處理。

  • onTouchEvent:進行事件處理。

Q:如何解決View的滑動衝突?

技術點:View滑動衝突

思路:從處理規則和具體實現方法展開討論

參考回答:

(1)處理規則:


  • 對於由於外部滑動和內部滑動方向不一致導致的滑動衝突,可以根據滑動的方向判斷誰來攔截事件。

  • 對於由於外部滑動方向和內部滑動方向一致導致的滑動衝突,可以根據業務需求,規定何時讓外部View攔截事件何時由內部View攔截事件。

  • 對於上面兩種情況的嵌套,相對複雜,可同樣根據需求在業務上找到突破點。

(2)實現方法:

  • 外部攔截法:指點擊事件都先經過父容器的攔截處理,如果父容器需要此事件就攔截,否則就不攔截。具體方法:需要重寫父容器的onInterceptTouchEvent方法,在內部做出相應的攔截。

  • 內部攔截法:指父容器不攔截任何事件,而將所有的事件都傳遞給子容器,如果子容器需要此事件就直接消耗,否則就交由父容器進行處理。具體方法:需要配合requestDisallowInterceptTouchEvent方法。

Q:談一談View的工作原理?


技術點:View工作流程

思路:圍繞三大流程展開

參考回答:View工作流程簡單來說就是,先measure測量,用於確定View的測量寬高,再 layout佈局,用於確定View的最終寬高和四個頂點的位置,最後 draw繪製,用於將View 繪製到屏幕上。具體過程圖見:

ViewRoot對應於ViewRootImpl類,它是連接WindowManager和DecorView的紐帶。

View的繪製流程是從ViewRoot和performTraversals開始。

performTraversals()依次呼叫performMeasure()、performLayout()和performDraw()三個方法,分別完成頂級 View的繪製。

其中,performMeasure()會呼叫measure(),measure()中又呼叫onMeasure(),實現對其所有子元素的measure過程,這樣就完成了一次measure過程;接著子元素會重覆父容器的measure過程,如此反覆至完成整個View樹的遍歷。layout和draw同理。


Q:MeasureSpec是什麼?有什麼作用?


技術點:View工作流程(measure)

思路:從MeasureSpec作用、組成、樣式和決定因素展開

參考回答:

作用:通過寬測量值widthMeasureSpec和高測量值heightMeasureSpec決定View的大小


組成:一個32位int值,高2位代表SpecMode(測量樣式),低30位代表SpecSize( 某種測量樣式下的規格大小)。

三種樣式:

  • UNSPECIFIED:父容器不對View有任何限制,要多大有多大。常用於系統內部。

  • EXACTLY(精確樣式):父視圖為子視圖指定一個確切的尺寸SpecSize。對應LyaoutParams中的match_parent或具體數值。

  • AT_MOST(最大樣式):父容器為子視圖指定一個最大尺寸SpecSize,View的大小不能大於這個值。對應LayoutParams中的wrap_content。


決定因素:值由子View的佈局引數LayoutParams和父容器的MeasureSpec值共同決定。具體規則見下圖:

引申:直接繼承View的自定義View需要重寫onMeasure()並設置wrap_content時的自身大小,否則效果相當於macth_parent。

Q:自定義View/ViewGroup需要註意什麼?

技術點:自定義View

參考回答:

Q:onTouch()、onTouchEvent()和onClick()關係?

技術點:View事件分發

參考回答:優先度onTouch()>onTouchEvent()>onClick()。因此onTouchListener的onTouch()方法會先觸發;如果onTouch()傳回false才會接著觸發onTouchEvent(),同樣的,內置諸如onClick()事件的實現等等都基於onTouchEvent();如果onTouch()傳回true,這些事件將不會被觸發。

引申:OnTouchListener、OnClickListener的衝突

Q:SurfaceView和View的區別?

技術點:View、SurfaceView

參考回答:SurfaceView是從View基類中派生出來的顯示類,他和View的區別有:

  • View需要在UI執行緒對畫面進行掃清,而SurfaceView可在子執行緒進行頁面的掃清

  • View適用於主動更新的情況,而SurfaceView適用於被動更新,如頻繁掃清,這是因為如果使用View頻繁掃清會阻塞主執行緒,導致界面卡頓

  • SurfaceView在底層已實現雙緩衝機制,而View沒有,因此SurfaceView更適用於需要頻繁掃清、掃清時資料處理量很大的頁面

Q:invalidate()和postInvalidate()的區別?

技術點:View掃清

參考回答:invalidate()與postInvalidate()都用於掃清View,主要區別是invalidate()在主執行緒中呼叫,若在子執行緒中使用需要配合handler;而postInvalidate()可在子執行緒中直接呼叫。

3、執行緒相關題解

Q:Android中還瞭解哪些方便執行緒切換的類?

技術點:執行緒通信

參考回答:對Handler進一步的封裝的幾個類:

  • AsyncTask:底層封裝了執行緒池和Handler,便於執行後臺任務以及在子執行緒中進行UI操作。

  • HandlerThread:一種具有訊息迴圈的執行緒,其內部可使用Handler。

  • IntentService:是一種異步、會自動停止的服務,內部採用HandlerThread。

引申:更多是對訊息機制的理解

Q:AsyncTask相比Handler有什麼優點?不足呢?


技術點:AsyncTask、Handler

參考回答:

Handler機制存在的問題:多任務同時執行時不易精確控制執行緒。

引入AsyncTask的好處:創建異步任務更簡單,直接繼承它可方便實現後臺異步任務的執行和進度的回呼更新UI,而無需編寫任務執行緒和Handler實體就能完成相同的任務。

Q:使用AsyncTask需要註意什麼?


技術點:AsyncTask

參考回答:

  • 不要直接呼叫onPreExecute()、doInBackground()、onProgressUpdate()、onPostExecute()和onCancelled()方法

  • 一個異步物件只能呼叫一次execute()方法

引申:談談AsyncTask初始化、五個核心方法如何配合進而體現Handler的作用

Q:AsyncTask中使用的執行緒池大小?


技術點:AsyncTask

參考回答:在AsyncTask內部實現有兩個執行緒池:

  • SerialExecutor:用於任務的排隊,預設是串行的執行緒池,在3.0以前核心執行緒數為5、執行緒池大小為128,而3.0以後變為同一時間只能處理一個任務

  • THREAD_POOL_EXECUTOR:用於真正執行任務。

引申:談談對執行緒池的理解

Q:HandlerThread有什麼特點?

技術點:HandlerThread

參考回答:HandlerThread是一個執行緒類,它繼承自Thread。與普通Thread不同,HandlerThread具有訊息迴圈的效果,這是因為它內部HandlerThread.run()方法中有Looper,能通過Looper.prepare()來創建訊息佇列,並通過Looper.loop()來開啟訊息迴圈。

Q:快速實現子執行緒使用Handler

技術點:HandlerThread

思路:不同於之前手動在子執行緒創建Looper再構建Handler的想法,這裡從HandlerThread角度去快速實現在子執行緒使用Handler

參考回答:HandlerThread實現方法

  • 實體化一個HandlerThread物件,引數是該執行緒的名稱;

  • 通過 HandlerThread.start()開啟執行緒;

  • 實體化一個Handler並傳入HandlerThread中的looper物件,使得與HandlerThread系結;

  • 利用Handler即可執行異步任務;

  • 當不需要HandlerThread時,通過HandlerThread.quit()/quitSafely()方法來終止執行緒的執行。

Q:IntentService的特點?

技術點:IntentService

思路:和普通執行緒和普通Service比較突出其特點

參考回答: 不同於執行緒,IntentService是服務,優先級比執行緒高,更不容易被系統殺死,因此較適合執行一些高優先級的後臺任務;不同於普通Service,IntentService可自動創建子執行緒來執行任務,且任務執行完畢後自動退出。

Q:為何不用bindService方式創建IntentService?

技術點:IntentService

思路:從底層實現出發

參考回答:IntentService的工作原理是,在IntentService的onCreate()里會創建一個HandlerThread,並利用其內部的Looper實體化一個ServiceHandler物件;而這個ServiceHandler用於處理訊息的handleMessage()方法會去呼叫IntentService的onHandleIntent(),這也是為什麼可在該方法中處理後臺任務的邏輯;當有Intent任務請求時會把Intent封裝到Message,然後ServiceHandler會把訊息發送出,而發送訊息是在onStartCommand()完成的,只能通過startService()才可走該生命周期方法,因此不能通過bindService創建IntentService。

Q:執行緒池的好處、原理、型別?

技術點:執行緒池

參考回答:

(1)執行緒池的好處:

  • 重用執行緒池中的執行緒,避免執行緒的創建和銷毀帶來的性能消耗;

  • 有效控制執行緒池的最大併發數,避免大量的執行緒之間因互相搶占系統資源而導致阻塞現象;

  • 進行執行緒管理,提供定時/迴圈間隔執行等功能

(2)執行緒池的分類:

  • FixThreadPool:執行緒數量固定的執行緒池,所有執行緒都是核心執行緒,當執行緒空閑時不會被回收;能快速響應外界請求。

  • CachedThreadPool:執行緒數量不定的執行緒池(最大執行緒數為Integer.MAX_VALUE),只有非核心執行緒,空閑執行緒有超時機制,超時回收;適合於執行大量的耗時較少的任務

  • ScheduledThreadPool:核心執行緒數量固定,非核心執行緒數量不定;可進行定時任務和固定周期的任務。

  • SingleThreadExecutor:只有一個核心執行緒,可確保所有的任務都在同一個執行緒中按順序執行;好處是無需處理執行緒同步問題。

(3)執行緒池的原理:實際上通過ThreadPoolExecutor並通過一系列引數來配置各種各樣的執行緒池,具體的引數有:

  • corePoolSize核心執行緒數:一般會在執行緒中一直存活

  • maximumPoolSize最大執行緒數:當活動執行緒數達到這個數值後,後續的任務將會被阻塞keepAliveTime非核心執行緒超時時間:超過這個時長,閑置的非核心執行緒就會被回收

  • unit:用於指定keepAliveTime引數的時間單位

  • workQueue任務佇列:通過執行緒池的execute()方法提交的Runnable物件會儲存在這個引數中。

  • threadFactory:執行緒工廠,可創建新執行緒

  • handler:在執行緒池無法執行新任務時進行調度

引申:使用Executors各個方法創建執行緒池的弊端

https://www.jianshu.com/p/4b89d681c5a0

Q:ThreadPoolExecutor的工作策略?

技術點:執行緒池

參考回答:ThreadPoolExecutor的預設工作策略:

  • 若任務無法插入到任務串列中,往往由於任務串列已滿,此時如果

  • 執行緒數量未達到執行緒池最大執行緒數,則會啟動一個非核心執行緒執行任務;

  • 執行緒數量已達到執行緒池規定的最大值,則拒絕執行此任務,ThreadPoolExecutor會呼叫RejectedExecutionHandler的rejectedExecution方法來通知呼叫者。

  • 若程池中的執行緒數量未達到核心執行緒數,則會直接啟動一個核心執行緒執行任務。

  • 若執行緒池中的執行緒數量已達到或者超過核心執行緒數量,則任務會被插入到任務串列等待執行。

引申:ThreadPoolExecutor的拒絕策略

4、IPC相關題解

Q:Android中行程和執行緒的關係?

技術點:行程、執行緒

參考回答:

  • 一般對應一個行程,當然,可以在AndroidMenifest中給四大組件指定屬性android:process開啟多行程樣式

  • 有限個執行緒:執行緒是一種受限的系統資源,不可無限制的產生且執行緒的創建和銷毀都有一定的開銷。

  • 形象理解:如果把安卓系統比喻成一片土壤,可以把App看做扎根在這片土壤上的工廠,每個APP一般對應一個行程,那麼執行緒就像是工廠的生產線。其中,主執行緒好比是主生產線,只有一條,子執行緒就像是副生產線,可以有很多條。

  • 關係:一個APP一般對應一個行程和有限個執行緒

Q:為何需要進行IPC?多行程通信可能會出現什麼問題?

技術點:多行程通信

思路:討論多行程通信會出現的問題得出IPC的必要性

參考回答:

(1)多行程造成的影響可總結為以下四方面:

  • 靜態變數和單例樣式失效:由獨立的虛擬機造成

  • 執行緒同步機制失效:由獨立的虛擬機造成

  • SharedPreference的不可靠下降:不支持兩個行程同時進行讀寫操作,即不支持併發讀寫,有一定幾率導致資料丟失

  • Application多次創建: Android系統會為新的行程分配獨立虛擬機,相當於系統又把這個應用重新啟動了一次。

(2)需要行程間通信的必要性:所有運行在不同行程的四大組件,只要它們之間需要通過記憶體在共享資料,都會共享失敗。這是由於Android為每個應用分配了獨立的虛擬機,不同的虛擬機在記憶體分配上有不同的地址空間,這會導致在不同的虛擬機中訪問同一個類的物件會產生多份副本。

引申: 談談IPC的使用場景

Q:什麼是序列化?Serializable接口和Parcelable接口的區別?為何推薦使用後者?


技術點:序列化

參考回答:序列化表示將一個物件轉換成可儲存或可傳輸的狀態。序列化後的物件可以在網絡上進行傳輸,也可以儲存到本地。

應用場景:需要通過Intent和Binder等傳輸類物件就必須完成物件的序列化過程。

兩種方式:實現Serializable/Parcelable接口。不同點如圖:

Q:Android中為何新增Binder來作為主要的IPC方式?


技術點:Binder機制

思路:回答Binder優點

參考回答:Binder機制有什麼幾條優點:

1. 傳輸效率高、可操作性強:傳輸效率主要影響因素是記憶體拷貝的次數,拷貝次數越少,傳輸速率越高。從Android行程架構角度分析:對於訊息佇列、Socket和管道來說,資料先從發送方的快取區拷貝到內核開闢的快取區中,再從內核快取區拷貝到接收方的快取區,一共兩次拷貝,如圖:

而對於Binder來說,資料從發送方的快取區拷貝到內核的快取區,而接收方的快取區與內核的快取區是映射到同一塊物理地址的,節省了一次資料拷貝的過程,如圖:

由於共享記憶體操作複雜,綜合來看,Binder的傳輸效率是最好的。

2. 實現C/S架構方便:Linux的眾IPC方式除了Socket以外都不是基於C/S架構,而Socket主要用於網絡間的通信且傳輸效率較低。Binder基於C/S架構 ,Server端與Client端相對獨立,穩定性較好。

3. 安全性高:傳統Linux IPC的接收方無法獲得對方行程可靠的UID/PID,從而無法鑒別對方身份;而Binder機製為每個行程分配了UID/PID且在Binder通信時會根據UID/PID進行有效性檢測。

Q:使用Binder進行資料傳輸的具體過程?

技術點:Binder機制

思路:通過AIDL實現方式解釋Binder資料傳輸的具體過程

參考回答:服務端中的Service給與其系結的客戶端提供Binder物件,客戶端通過AIDL接口中的asInterface()將這個Binder物件轉換為代理Proxy,並通過它發起RPC請求。客戶端發起請求時會掛起當前執行緒,並將引數寫入data然後呼叫transact(),RPC請求會通過系統底層封裝後由服務端的onTransact()處理,並將結果寫入reply,最後傳回呼叫結果並喚醒客戶端執行緒。

Q:Binder框架中ServiceManager的作用?

技術點:Binder機制

思路:從Binder框架出發討論每個元素的作用

參考回答:在Binder框架定義了四個角色:Server,Client,ServiceManager和Binder驅動。其中Server、Client、ServiceManager運行於用戶空間,Binder驅動運行於內核空間。關係如圖:

  • Server&Client;:服務器&客戶端。在Binder驅動和Service Manager提供的基礎設施上,進行Client-Server之間的通信。

  • ServiceManager服務的管理者,將Binder名字轉換為Client中對該Binder的取用,使得Client可以通過Binder名字獲得Server中Binder物體的取用。流程如圖:

Binder驅動:

  • 與硬體設備沒有關係,其工作方式與設備驅動程式是一樣的,工作於內核態。

  • 提供open()、mmap()、poll()、ioctl() 等標準檔案操作。

  • 以字符驅動設備中的misc設備註冊在設備目錄/dev下,用戶通過/dev/binder訪問該它。

  • 負責行程之間binder通信的建立,傳遞,計數管理以及資料的傳遞交互等底層支持。

  • 驅動和應用程式之間定義了一套接口協議,主要功能由ioctl() 接口實現,由於ioctl()靈活、方便且能夠一次呼叫實現先寫後讀以滿足同步交互,因此不必分別呼叫write()和read()接口。

  • 其代碼位於linux目錄的drivers/misc/binder.c中。

Q:Android中有哪些基於Binder的IPC方式?簡單對比下?

技術點:IPC方式

思路:分析每種IPC方式的優缺點和使用場景的差異

參考回答:

Q:是否瞭解AIDL?原理是什麼?如何優化多模塊都使用AIDL的情況?

技術點:AIDL

參考回答:

工作原理:每個業務模塊創建自己的AIDL接口並實現此接口,然後向服務端提供自己的唯一標識和其對應的Binder物件。服務端只需要一個Service,服務器提供一個queryBinder接口,它會根據業務模塊的特征來傳回相應的Binder對像,不同的業務模塊拿到所需的Binder物件後就可進行遠程方法的呼叫了。

流程如圖:

  • AIDL接口:繼承IInterface。

  • Stub類:Binder的實現類,服務端通過這個類來提供服務。

  • Proxy類:服務器的本地代理,客戶端通過這個類呼叫服務器的方法。

  • asInterface():客戶端呼叫,將服務端的傳回的Binder物件,轉換成客戶端所需要的AIDL接口型別物件。傳回物件。

  • asBinder():根據當前呼叫情況傳回代理Proxy的Binder物件。

  • onTransact():運行服務端的Binder執行緒池中,當客戶端發起跨行程請求時,遠程請求會通過系統底層封裝後交由此方法來處理。

  • transact():運行在客戶端,當客戶端發起遠程請求的同時將當前執行緒掛起。之後呼叫服務端的onTransact()直到遠程請求傳回,當前執行緒才繼續執行。

  • 若客戶端和服務端位於同一行程,則直接傳回Stub物件本身;

  • 否則,傳回的是系統封裝後的Stub.proxy物件。

  • AIDL(Android Interface Definition Language,Android接口定義語言):如果在一個行程中要呼叫另一個行程中物件的方法,可使用AIDL生成可序列化的引數,AIDL會生成一個服務端物件的代理類,通過它客戶端實現間接呼叫服務端物件的方法。

  • AIDL的本質是系統提供了一套可快速實現Binder的工具。關鍵類和方法:當有多個業務模塊都需要AIDL來進行IPC,此時需要為每個模塊創建特定的aidl檔案,那麼相應的Service就會很多。必然會出現系統資源耗費嚴重、應用過度重量級的問題。解決辦法是建立Binder連接池,即將每個業務模塊的Binder請求統一轉發到一個遠程Service中去執行,從而避免重覆創建Service。

由於篇幅原因,以上僅為一小部分,原文非常長,各位刻意仔細閱讀作者原文。

下麵三篇該為面試總結題解了:

2019 Android 秋招提前批面試總結

  • 2019校招Android面試題解1.0(上篇)

    https://www.jianshu.com/p/718aa3c1a70b

  • 2019校招Android面試題解1.0(中篇)

    https://www.jianshu.com/p/2dd855aa1938

  • 2019校招Android面試題解1.0(下篇)

    https://www.jianshu.com/p/168e52336b53



●編號362,輸入編號直達本文

●輸入m獲取到文章目錄

推薦↓↓↓

Java編程

更多推薦18個技術類公眾微信

涵蓋:程式人生、演算法與資料結構、黑客技術與網絡安全、大資料技術、前端開發、Java、Python、Web開發、安卓開發、iOS開發、C/C++、.NET、Linux、資料庫、運維等。

赞(0)

分享創造快樂