這段時間麵了幾家公司,也跟不同的面試官切磋了一些面試題,有的沒啥難度,有的則是問到了我的知識盲區,沒辦法,Android能問的東西太多了,要全覆蓋到太難了,既然沒法全覆蓋,那麼只好亡羊補牢,將這些沒答上來的題目做下記錄,讓自己如果下次遇到了可以答上來
TCP與UDP有哪些差異
這道題回答的不全,僅僅只是將兩個協議的概念說了一下,但是真正的差異卻沒有真正答上來,後來查詢了一下資料,兩者的差異如下
TCP是傳輸控制協議,是面向連接的協議,傳送資料前需要建立連線,TCP傳輸的資料不會丟失,不會重複,會按照順序到達
與TCP相對的,UDP是無連線的協議,傳送資料前不需要建立連線,資料沒有可靠性
TCP的通訊類似於打電話,需要確認身份後纔可以通話,而UDP更像是廣播,不關心對方是不是接收,只需要播報出去即可
TCP支援點對點通訊,而UDP支援一對一,一對多,多對一,多對多
TCP傳輸的是位元組流,而UDP傳輸的是報文
TCP首部開銷為20個位元組,而UDP首部開銷是8個位元組
UDP主機不需要維持複雜的連線狀態表
TCP的三次握手
這道題以及下面那道雖然說上來了,但是也沒有說的很對,僅僅只是說了下每次握手或者揮手的目的,中間的過程沒有說出來,以下是三次握手以及四次揮手的詳細過程
第一次握手:客戶端將SYN置為1,隨機生成一個初始序列號seq傳送給服務端,客戶端進入SYN_SENT狀態
第二次握手:服務端收到客戶端的SYN=1之後,知道客戶端請求建立連線,將自己的SYN置1,ACK置1,產生一個ack=seq+1,並隨機產生一個自己的初始序列號,傳送給客戶端,服務端進入SYN_RCVD狀態
第三次握手:客戶端檢查ack是否為序列號+1,ACK是否為1,檢查正確之後將自己的ACK置為1,產生一個ack=伺服器的seq+1,傳送給伺服器;進入ESTABLISHED狀態;伺服器檢查ACK為1和ack為序列號+1之後,也進入ESTABLISHED狀態;完成三次握手,連線建立
TCP的四次揮手
第一次揮手:客戶端將FIN設定為1,傳送一個序列號seq給服務端,客戶端進入FIN_WAIT_1狀態
第二次揮手:服務端收到FIN之後,傳送一個ACK為1,ack為收到的序列號加一,服務端進入CLOSE_WAIT狀態,這個時候客戶端已經不會再向服務端傳送資料了
第三次揮手:服務端將FIN置1,傳送一個序列號給客戶端,服務端進入LAST_ACK狀態
第四次揮手:客戶端收到伺服器的FIN後,進入TIME_WAIT狀態,接著將ACK置1,傳送一個ack=序列號+1給伺服器,伺服器收到後,確認ack後,變為CLOSED狀態,不再向客戶端傳送資料。客戶端等待2* MSL(報文段最長壽命)時間後,也進入CLOSED狀態。完成四次揮手
從瀏覽器輸入地址到最終顯示頁面的整個過程
這個真的知識盲區了,誰會平時沒事在用瀏覽器的時候去思考這個問題呢,結果一查居然還是某大廠的面試題,算了也瞭解下吧
第一步,瀏覽器查詢DNS,獲取域名對應的ip地址
第二步,獲取ip地址後,瀏覽器向伺服器建立連線請求,發起三次握手請求
第三步,連線建立好之後,瀏覽器向伺服器發起http請求
第四步,伺服器收到請求之後,根據路徑的引數對映到特定的請求處理器進行處理,並將處理結果以及相應的檢視返回給瀏覽器
第五步,瀏覽器解析並渲染檢視,若遇到js,css以及圖片等靜態資源,則重複向伺服器請求相應資源
第六步,瀏覽器根據請求到的資料,資源渲染頁面,最終將完整的頁面呈現在瀏覽器上
為什麼Zygote程序使用socket通訊而不是binder
應用層選手遇到偏底層問題就頭疼了,但是這個問題還是要知道的,畢竟跟我們app的啟動流程相關
原因一:從初始化時機上,
Binder
通訊需要在Android執行時以及Binder
驅動已經初始化之後才能使用,而在這之前,Zygote
已經啟動了,所以只能使用socket
通訊原因二:從出現的先後順序上,
Zygote
相比於Binder
機制,更早的被設計以及投入使用,所以在Android的早期版本中,Android就已經使用socket
來監聽其他程序的請求原因三:從使用上,
socket
通訊不依賴於Binder
機制,它是一種簡單通用的IPC機制,也不需要複雜的介面定義原因四:從相容性上來講,
socket
是一種跨平臺的IPC機制,可以在不同的作業系統和環境中使用。原因五:從效能上來講,由於使用
Zygote
通訊並不是頻繁的操作,所以使用socket
通訊不會對系統性能造成顯著影響原因六:從安全性上來講,使用
socket
可以確保只有系統中特定的服務如system_server
才能與Zygote通訊,從而提升一定的安全性
使用Binder的好處有哪些
上面那個問題問好了緊接著就是這道題,我嗯嗯啊啊的零碎說了幾個,肯定也是不過關的,回頭查了下資料,使用Binder
的優勢如下
從效率上來講,
Binder
比較高效,相比較於其他幾種程序的通訊方式(管道,訊息佇列,Socket,共享記憶體),Binder
只需要複製一次記憶體就好了,而除了共享記憶體,其餘都都要複製兩次記憶體,共享記憶體雖然不需要複製,但是實現方式複雜,所以綜合考慮Binder
佔優勢使用的是更加便於理解,更簡單的物件導向的IPC通訊方式
Binder
既支援同步呼叫,也支援非同步呼叫Binder
使用UID和PID來驗證請求的來源,這樣可以確保每個Binder事務可以精確到發起者,為程序間的通訊提供了保障Binder
是基於c/s架構,架構清晰明確,Server端與Client端相對獨立Binder
有一套易於使用的API供程序間通訊,將複雜的內部實現隱藏起來
如果一個執行緒連續呼叫兩次start,會怎樣?
會怎樣?誰知道呀,正常人誰會沒事去呼叫兩次start
呢?但是這個還真有人問了,我只能說沒遇到過,後來回去自己試了下才知道
如上述程式碼所示,有一個執行緒,然後連續呼叫了兩次start
方法,當我們執行一下這段程式碼後,得到的結果如下
可以發現執行緒有正常執行,但同時也因為多調了一次start
而丟擲了異常,這個異常在start
方法裡面就能看到
有一個狀態為started
,正常第一次啟動執行緒時候,started
為false,所以是不會丟擲異常的,started
為true的地方是在下面這個位置
呼叫了native方法nativeCreated
後,started
狀態位才變成true,這個時候如果再去呼叫start
方法,那麼必然會丟擲異常
如何處理協程併發的資料安全
之前遇到過這麼個問題,併發處理的協程之間是否可以保證資料安全,這個由於之前有實驗過,所以想都沒想就說可以保證資料安全,但面試官只是呵呵了一下,我捉摸著難道不對嗎,後來回去試了一下才發現,不一定就能保證資料安全,看下面這段程式碼
這段程式碼裡面在runBlocking
中建立了1000個協程,每一個協程都對變數count
做自增操作,最後把結果列印出來,我們預期的是列印出的結果就是1000,實際結果如下
看到的確就是1000,沒啥毛病,多試幾次也是一樣的,但是如果換一種寫法試試看呢
原本都是runBlocking
裡面的子協程,現在將這些協程變成非runBlocking
的子協程,結果是不是還是1000呢,看下結果
明顯不是了,所以併發處理的協程,並不能保證資料安全,那麼如何可以讓資料安全呢,有以下幾個辦法
原子類
這個好理解,同處理執行緒安全差不多
channel
receive
函式只有等到阻塞佇列裡面有資料的時候纔會執行,沒有資料的時候會一直等待,所以這就能保證這些協程可以併發執行,不過要注意的是這裏的Channel
一定要設定佇列大小,不然程式會一直阻塞,receive
一直在等待佇列裡面有資料
mutex
使用互斥鎖的方式,withLock
函式內部執行了獲取鎖跟釋放鎖邏輯,將變數count
保護起來,實現資料安全,除此之外,還可以使用lock
與unLock
函式來實現,程式碼如下
總結
總的來講自己在系統層面,偏底層的那些問題上,還是掌握的不多,這個也跟自己多年徘徊在應用層開發有關,底層知識用到的不多,自然也就忽略了,但是如果面試的話,就算是麵的應用層,也是需要知道一些底層方面的知識,不然面試官隨便問幾個,你不會,別人會,崗位不就被別人拿走了嗎