互聯(lián)網(wǎng)大廠真實(shí)流出的Android面試題,你能答對(duì)幾個(gè)?

前言

下面跟大家分享的這些面試題都是互聯(lián)網(wǎng)大廠真實(shí)流出的面試內(nèi)容,每個(gè)問題都附帶完整詳細(xì)的答案,不像網(wǎng)上的那些資料三教九流有的甚至還沒答案,這些面試題我也是經(jīng)過日積月累才整理出來的精品資料。

這些面試題主要是針對(duì)1-5年左右的Android開發(fā)程序員提升的,不管是傳統(tǒng)行業(yè)還是互聯(lián)網(wǎng)行業(yè),掌握這些技術(shù)基本都能拿到一個(gè)不錯(cuò)的薪資,希望對(duì)大家有所幫助。

面試題展示如下:

Java篇

一、抽象類與接口的區(qū)別?

大體區(qū)別如下:

  • 抽象類可以提供成員方法的實(shí)現(xiàn)細(xì)節(jié),而接口中只能存在 public 抽象方法;
  • 抽象類中的成員變量可以是各種類型的,而接口中的成員變量只能是 public static final 類型的;
  • 接口中不能含有構(gòu)造器、靜態(tài)代碼塊以及靜態(tài)方法,而抽象類可以有構(gòu)造器、靜態(tài)代碼塊和靜態(tài)方法;
  • 一個(gè)類只能繼承一個(gè)抽象類,而一個(gè)類卻可以實(shí)現(xiàn)多個(gè)接口;
  • 抽象類訪問速度比接口速度要快,因?yàn)榻涌谛枰獣r(shí)間去尋找在類中具體實(shí)現(xiàn)的方法;
  • 如果你往抽象類中添加新的方法,你可以給它提供默認(rèn)的實(shí)現(xiàn)。因此你不需要改變你現(xiàn)在的代碼。如果你往接口中添加方法,那么你必須改變實(shí)現(xiàn)該接口的類。
  • 接口更多的為了約束類的行為,可用于解耦,而抽象類更加側(cè)重于代碼復(fù)用。

二、談?wù)凩ist,Set,Map的區(qū)別?

List中存儲(chǔ)的數(shù)據(jù)是有順序的,并且值允許重復(fù);Map中存 儲(chǔ)的數(shù)據(jù)是無序的,它的鍵是不允許重復(fù)的,但是值是允
許重復(fù)的;Set中存儲(chǔ)的數(shù)據(jù)是無順序的,并且不允許重 復(fù),但元素在集合中的位置是由元素的hashcode決定,即
位置是固定的(Set集合是根據(jù)hashcode來進(jìn)行數(shù)據(jù)存儲(chǔ) 的,所以位置是固定的,但是這個(gè)位置不是用戶可以控制
的,所以對(duì)于用戶來說set中的元素還是無序的)

三、說一下線程的幾種狀態(tài)?

  • 第一是創(chuàng)建狀態(tài)。在生成線程對(duì)象,并沒有調(diào)用該對(duì)象的start方法,這是線程處于創(chuàng)建狀態(tài)。
  • 第二是就緒狀態(tài)。當(dāng)調(diào)用了線程對(duì)象的start方法之后,該線程就進(jìn)入了就緒狀態(tài),但是此時(shí)線程調(diào)度程序還沒有把該線程設(shè)置為當(dāng)前線程,此時(shí)處于就緒狀態(tài)。在線程運(yùn)行之后,從等待或者睡眠中回來之后,也會(huì)處于就緒狀態(tài)。
  • 第三是運(yùn)行狀態(tài)。線程調(diào)度程序?qū)⑻幱诰途w狀態(tài)的線程設(shè)置為當(dāng)前線程,此時(shí)線程就進(jìn)入了運(yùn)行狀態(tài),開始運(yùn)行run函數(shù)當(dāng)中的代碼。
  • 第四是阻塞狀態(tài)。線程正在運(yùn)行的時(shí)候,被暫停,通常是為了等待某個(gè)時(shí)間的發(fā)生(比如說某項(xiàng)資源就緒)之后再繼續(xù)運(yùn)行。sleep,suspend,wait等方法都可以導(dǎo)致線程阻塞。
  • 第五是死亡狀態(tài)。如果一個(gè)線程的run方法執(zhí)行結(jié)束或者調(diào)用stop方法后,該線程就會(huì)死亡。對(duì)于已經(jīng)死亡的線程,無法再使用start方法令其進(jìn)入就緒。

四、如何實(shí)現(xiàn)多線程中的同步?

多線程同步和異步不是一回事。 幾種情況,

  1. 就是大家說的synchronized 他可以保證原子性,保證多個(gè)線程在操作同一方法時(shí)只有一個(gè)線程可以持有鎖,并且操作該方法,
  2. 就是手動(dòng)調(diào)用讀寫鎖,
  3. 手動(dòng)操作線程的wait和notify
  4. volatile我記得是沒有原子性的,他可以保證內(nèi)存可見性,在多線程的情況下保證每個(gè)線程的數(shù)據(jù)都是最新的

Android篇

一、Activity 與 Fragment 之間常見的幾種通信方式?

viewModel 做數(shù)據(jù)管理,activity 和 fragment 公用同個(gè)viewModel 實(shí)現(xiàn)數(shù)據(jù)傳遞

二、BroadcastReceiver 與LocalBroadcastReceiver 有什么區(qū)別?

  • BroadcastReceiver 是跨應(yīng)用廣播,利用Binder機(jī)制實(shí)現(xiàn),支持動(dòng)態(tài)和靜態(tài)兩種方式注冊(cè)方式。
  • LocalBroadcastReceiver 是應(yīng)用內(nèi)廣播,利用Handler 實(shí)現(xiàn),利用了IntentFilter的match功能,提供消息的發(fā)布與接收功能,實(shí)現(xiàn)應(yīng)用內(nèi)通信,效率和安全性比較高,僅支持動(dòng)態(tài)注冊(cè)。

三、子線程能否更新UI?為什么?

子線程是不能直接更新UI的
注意這句話,是不能直接更新,不是不能更新(極端情況下可更新)繪制過程要保持同步(否則頁面不流暢),而我們的主線程負(fù)責(zé)繪制ui,極端情況就是,在Activity的onResume(含)之前的生命周期中子線程都可以進(jìn)行更新ui,也就是onCreate,onStart和onResume,此時(shí)主線程的繪制還沒開始。

四、 談?wù)?Handler 機(jī)制和原理?

首先在UI線程我們創(chuàng)建了一個(gè)Handler實(shí)例對(duì)象,無論是匿名內(nèi)部類還是自定義類生成的Handler實(shí)例對(duì)象,我們都需要對(duì)handleMessage方法進(jìn)行重寫,在handleMessage方法中我們可以通過參數(shù)msg來寫接受消息過后UIi線程的邏輯處理,接著我們創(chuàng)建子線程,在子線程中需要更新UI的時(shí)候,新建一個(gè)Message對(duì)象,并且將消息的數(shù)據(jù)記錄在這個(gè)消息對(duì)象Message的內(nèi)部,比如arg1,arg2,obj等,然后通過前面的Handler實(shí)例對(duì)象調(diào)用sendMessge方法把這個(gè)Message實(shí)例對(duì)象發(fā)送出去,之后這個(gè)消息會(huì)被存放于MessageQueue中等待被處理,此時(shí)MessageQueue的管家Looper正在不停的把MessageQueue存在的消息取出來,通過回調(diào)dispatchMessage方法將消息傳遞給Handler的handleMessage方法,最終前面提到的消息會(huì)被Looper
從MessageQueue中取出來傳遞給handleMessage方法。

五、談?wù)凙ndroid的事件分發(fā)機(jī)制?

當(dāng)點(diǎn)擊的時(shí)候,會(huì)先調(diào)用頂級(jí)viewgroup的dispatchTouchEvent,如果頂級(jí)的viewgroup攔截了此事件(onInterceptTouchEvent返回true),則此事件序列
由頂級(jí)viewgroup處理。如果頂級(jí)viewgroup設(shè)置setOnTouchListener,則會(huì)回調(diào)接口中的onTouch,此時(shí)頂級(jí)的viewgroup中的onTouchEvent不再回調(diào),如果不設(shè)
置setOnTouchListener則onTouchEvent會(huì)回調(diào)。如果頂級(jí)viewgroup設(shè)置setOnClickListener,則會(huì)回調(diào)接口中的onClick。如果頂級(jí)viewgroup不攔截事件,事件就會(huì)向下傳遞給他的子view,然后子view就會(huì)調(diào)用它的dispatchTouchEvent方法。

Android Framework篇

一、你了解 Android 系統(tǒng)啟動(dòng)流程嗎?

當(dāng)按電源鍵觸發(fā)開機(jī),首先會(huì)從ROM中預(yù)定義的地方加載引導(dǎo)程序BootLoader 到 RAM中,并執(zhí)行BootLoader程序啟動(dòng)Linux
Kernel,然后啟動(dòng)用戶級(jí)別的第一個(gè)進(jìn)程: init 進(jìn)程。init
進(jìn)程會(huì)解析init.rc腳本做一些初始化工作,包括掛載文件系統(tǒng)創(chuàng)建工作目錄以及啟動(dòng)系統(tǒng)服務(wù)進(jìn)程等,其中系統(tǒng)服務(wù)進(jìn)程包括
Zygote、service manager、media 等。在 Zygote中會(huì)進(jìn)一步去啟動(dòng) system_ server進(jìn)程,然后在
system_server 進(jìn)程中會(huì)啟動(dòng)AMS、WMS、PMS等服務(wù),等這些服務(wù)啟動(dòng)之后,AMS中就會(huì)打開Launcher應(yīng)用的home
Activity,最終就看到了手機(jī)的“桌面”。

二、能具體說說是怎么導(dǎo)致的死鎖的嗎?

在POSIX 標(biāo)準(zhǔn)中,fork的行為是這樣的:復(fù)制整個(gè)用戶空間的數(shù)據(jù)(通常使用 copy-on-write
的策略,所以可以實(shí)現(xiàn)的速度快)以及所有系統(tǒng)對(duì)象,然后僅復(fù)制當(dāng)前線程到子進(jìn)程。這里:所有父進(jìn)程中別的線程,到了子進(jìn)程中都是突然蒸發(fā)掉的對(duì)于鎖來說,從
OS 看,每個(gè)鎖有一個(gè)所有者,即最后一次 lock 它的線程。假設(shè)這么一個(gè)環(huán)境,在 fork 之前,有一個(gè)子線程 lock
了某個(gè)鎖,獲得了對(duì)鎖的所有權(quán)。fork
以后,在子進(jìn)程中,所有的額外線程都人間蒸發(fā)了。而鎖卻被正常復(fù)制了,在子進(jìn)程看來,這個(gè)鎖沒有主人,所以沒有任何人可以對(duì)它解鎖。 當(dāng)子進(jìn)程想
lock 這個(gè)鎖時(shí),不再有任何手段可以解開了。

三、Zygote 為什么不采用 Binder 機(jī)制進(jìn)行 IPC 通信?

Binder機(jī)制中存在Binder線程池,是多線程的,如果Zygote采用Binder的話就存在上面說的fork()與多線程的問題了。其實(shí)嚴(yán)格來說,Binder機(jī)制不一定要多線程,所謂的Binder線程只不過是在循環(huán)讀取Binder驅(qū)動(dòng)的消息而已,只注冊(cè)一個(gè)Binder線程也是可以工作的,比如service
manager
就是這樣的。實(shí)際上Zygote盡管沒有采取Binder機(jī)制,它也不是單線程的,但它在fork()前主動(dòng)停止了其他線程,fork()后重新啟動(dòng)了。

四、Binder是如何做到一次拷貝的

主要是因?yàn)長inux是使用的虛擬內(nèi)存尋址方式,它有如下特性:

  • 用戶空間的虛擬內(nèi)存地址是映射到物理內(nèi)存中的。
  • 對(duì)虛擬內(nèi)存的讀寫實(shí)際上是對(duì)物理內(nèi)存的讀寫,這個(gè)過程就是內(nèi)存映射。
  • 這個(gè)內(nèi)存映射過程是通過系統(tǒng)調(diào)用mmap()來實(shí)現(xiàn)的。
  • Binder借助了內(nèi)存映射的方法,在內(nèi)核空間的接收方用戶控件的數(shù)據(jù)緩存區(qū)之間做了一層內(nèi)存映射,就相當(dāng)于直接拷貝到了接收方用戶空間的數(shù)據(jù)緩存區(qū),從而減少了一次數(shù)據(jù)拷貝。

五、Binder機(jī)制是如何跨進(jìn)程的

  1. Binder驅(qū)動(dòng)
  • 在內(nèi)核空間創(chuàng)建一塊接收緩存區(qū),
  • 實(shí)現(xiàn)地址映射:將內(nèi)核緩存區(qū)、接收進(jìn)程用戶空間映射到同一接收緩存區(qū)
  1. 發(fā)送進(jìn)程通過系統(tǒng)調(diào)用 (copy_from_user) 將數(shù)據(jù)發(fā)送到內(nèi)核緩存區(qū)。由于內(nèi)核緩存區(qū)和 接收進(jìn)程用戶空間存在映射關(guān)系,故相當(dāng)于也發(fā)送了接收進(jìn)程的用戶空間,實(shí)現(xiàn)了跨進(jìn)程通信

Flutter篇

一、Flutter 中的生命周期

initState()表示當(dāng)前 State 將和一個(gè) BuildContext 產(chǎn)生關(guān)聯(lián),但是此時(shí)BuildContext
沒有完全裝載完成,如果你需要在該方法中獲取 BuildContext ,可以 new Future.delayed(const
Duration(seconds: 0, (){//context}); 一下。

didChangeDependencies()在 initState()之后調(diào)用,當(dāng) State
對(duì)象的依賴關(guān)系發(fā)生變化時(shí),該方法被調(diào)用,初始化時(shí)也會(huì)調(diào)用。deactivate()當(dāng) State
被暫時(shí)從視圖樹中移除時(shí),會(huì)調(diào)用這個(gè)方法,同時(shí)頁面切換時(shí),也會(huì)調(diào)用。dispose() Widget 銷毀了,在調(diào)用這個(gè)方法之前,總會(huì)先調(diào)用
deactivate()。didUpdateWidge 當(dāng) widget 狀態(tài)發(fā)生變化時(shí),會(huì)調(diào)用。

通過 StreamBuilder 和 FutureBuilder 我們可以快速使用 Stream 和 Future
快速構(gòu)建我們的異步控件,F(xiàn)lutter 中 runApp 啟動(dòng)入口其實(shí)是一個(gè) WidgetsFlutterBinding
,它主要是通過BindingBase 的子類 GestureBinding 、ServicesBinding
、SchedulerBinding 、PaintingBinding 、SemanticsBinding 、 RendererBinding
、WidgetsBinding 等,通過 mixins 的組合而成的。 Flutter 中的 Dart
的線程是以事件循環(huán)和消息隊(duì)列的形式存在,包含兩個(gè)任務(wù)隊(duì)列,一個(gè)是 microtask 內(nèi)部隊(duì)列,一個(gè)是event 外部隊(duì)列,而
microtask 的優(yōu)先級(jí)又高于event。因?yàn)?microtask 的優(yōu)先級(jí)又高于 event,同時(shí)會(huì)阻塞event
隊(duì)列,所以如果microtask 太多就可能會(huì)對(duì)觸摸、繪制等外部事件造成阻塞卡頓哦。

Flutter 中存在四大線程,分別為 UI Runner、GPU Runner、IO Runner, Platform Runner
(原生主線程),同時(shí)在 Flutter 中可以通過 isolate 或者compute 執(zhí)行真正的跨線程異步操作。

二、Widget 和 element 和 RenderObject 之間的關(guān)系?

  • Widget是用戶界面的一部分,并且是不可變的。
  • Element是在樹中特定位置Widget的實(shí)例。
  • RenderObject是渲染樹中的一個(gè)對(duì)象,它的層次結(jié)構(gòu)是渲染庫的核心。

Widget會(huì)被inflate(填充)到Element,并由Element管理底層渲染樹。Widget并不會(huì)直接管理狀態(tài)及渲染,
而是通過State這個(gè)對(duì)象來管理狀態(tài)。Flutter創(chuàng)建Element的可見樹,相對(duì)于Widget來說,是可變的,通常界面開發(fā)中,我們不用直接操作Element,而是由框架層實(shí)現(xiàn)內(nèi)部邏輯。就如一個(gè)UI視圖樹中,可能包含有多個(gè)TextWidget(Widget被使用多次),但是放在內(nèi)部視圖樹的視角,這些TextWidget都是填充到一個(gè)個(gè)獨(dú)立的Element中。Element會(huì)持有renderObject和widget的實(shí)例。記住,Widget
只是一個(gè)配置,RenderObject 負(fù)責(zé)管理布局、繪制等操作。

在第一次創(chuàng)建 Widget 的時(shí)候,會(huì)對(duì)應(yīng)創(chuàng)建一個(gè) Element,然后將該元素插入樹中。如果之后 Widget 發(fā)生了變化,則將其與舊的
Widget 進(jìn)行比較,并且相應(yīng)地更新 Element。重要的是,Element 不會(huì)被重建,只是更新而已。

三、mixin extends implement 之間的關(guān)系?

繼承(關(guān)鍵字 extends)、混入 mixins (關(guān)鍵字 with)、接口實(shí)現(xiàn)(關(guān)鍵字
implements)。這三者可以同時(shí)存在,前后順序是extends -> mixins -> implements。
Flutter中的繼承是單繼承,子類重寫超類的方法要用@Override,子類調(diào)用超類的方法要用super。
在Flutter中,Mixins是一種在多個(gè)類層次結(jié)構(gòu)中復(fù)用類代碼的方法。mixins的對(duì)象是類,mixins絕不是繼承,也不是接口,而是一種全新的特性,可以mixins多個(gè)類,mixins的使用需要滿足一定條件

就舉例這么多了,面試題也不是幾個(gè)就能全部覆蓋的,畢竟面試官不是吃素的,他會(huì)換著花樣問你有需要面試題的朋友可以關(guān)注一下哇哇,以上都可以分享?。?!

面試目錄.png

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容