2019校招Android面試題解1.0(上篇)
本篇將針對 ofollow,noindex">2019Android秋招提前批面試總結 中歸納的校招面試題做個小解答(除演算法,演算法部分另做總結),然後開了個“彩蛋”小專區,對簡友的一些疑問做個統一回復,特此感謝大家的支援呀~
- 面試題解
- 彩蛋時刻
1.面試題解
前言:
- 過去的這一個月,忙過了爆炸開學周、結束了推免、和朋友出門閒遊放鬆了幾天、回家休息慶祝了中秋節國慶節,最後才是這篇姍姍來遲的題解,請原諒我的懶吧!
- 題解乃見仁見智之事,以下只是自己的看法,僅供參考,因本人水平有限,如有問題歡迎指正。
- 之所以是題解1.0,是希望隨著學習的深入,以後能更深刻的理解,再出個題解2.0最好不過了。
1.1 Android
Q:說下Activity的生命週期?
- 技術點:Activity生命週期
- 思路:分條解釋Activity從建立到銷燬整個生命週期中涉及到的方法及作用
- 參考回答:在Activity的生命週期涉及到七大方法,分別是:
- onCreate()表示Activity 正在建立 ,常做 初始化 工作,如setViewContent介面資源、初始化資料
- onStart()表示Activity 正在啟動 ,這時Activity 可見但不在前臺 ,無法和使用者互動
- onResume()表示Activity 獲得焦點 ,此時Activity 可見且在前臺 並開始活動
- onPause()表示Activity 正在停止 ,可做 資料儲存、停止動畫 等操作
- onStop()表示activity 即將停止 ,可做 稍微重量級回收工作 ,如取消網路連線、登出廣播接收器等
- onDestroy()表示Activity 即將銷燬 ,常做 回收工作、資源釋放
- 另外,當Activity由後臺切換到前臺,由不可見到可見時會呼叫onRestart(),表示Activity 重新啟動
Q:onStart()和onResume()/onPause()和onStop()的區別?
- 技術點:Activity生命週期
- 思路:可從使用細節出發
- 參考回答:onStart()與onStop()是從Activity是否可見這個角度呼叫的,onResume()和onPause()是從Activity是否顯示在前臺這個角度來回調的,在實際使用沒其他明顯區別。
Q:Activity A啟動另一個Activity B會回撥哪些方法?如果Activity B是完全透明呢?如果啟動的是一個Dialog呢?
- 技術點:Activity生命週期
- 參考回答:Activity A啟動另一個Activity B會回撥的方法:Activity A的onPause() -->Activity B的onCreate()-->onStart()-->onResume()-->Activity A的onStop();如果Activity B是完全透明的,則最後不會呼叫Activity A的onStop();如果是Dialog,同後種情況。
Q:談談onSaveInstanceState()方法?何時會呼叫?
- 技術點:Activity重建
- 思路:可從使用場景、呼叫次序上解釋
- 參考回答:系統異常終止時,比如系統配置發生改變時導致Activity被殺死並重新建立、資源記憶體不足導致低優先順序的Activity被殺死,會呼叫 onSavaInstanceState() 來儲存狀態。該方法呼叫在onStop之前,但和onPause沒有時序關係。
- 引申:可再談談它和onRestoreInstanceState()的關係以及後者的作用
Q:onSaveInstanceState()與onPause()的區別?
- 技術點:Activity重建
- 思路:可從適用場景出發
- 參考回答:onSaveInstanceState()適用於對臨時性狀態的儲存,而onPause()適用於對資料的持久化儲存。
Q:如何避免配置改變時Activity重建?
- 技術點:Activity重建
- 思路:一種解決思路是設定配置檔案中Activity的configChanges屬性
- 參考回答:為了避免由於配置改變導致Activity重建,可在AndroidManifest.xml中對應的Activity中設定 android:configChanges="orientation|keyboardHidden|screenSize" 。此時再次旋轉螢幕時,該Activity不會被系統殺死和重建,只會呼叫onConfigurationChanged。因此,當配置程式需要響應配置改變,指定 configChanges屬性 ,重寫onConfigurationChanged方法即可。
Q:優先順序低的Activity在記憶體不足被回收後怎樣做可以恢復到銷燬前狀態?
- 技術點:Activity重建
- 思路:可從Activity重建會被呼叫的方法出發
- 參考回答:優先順序低的Activity在記憶體不足被回收後重新開啟會引發Activity重建。Activity被重新建立時會呼叫 onRestoreInstanceState (該方法在onStart之後),並將onSavaInstanceState儲存的 Bundle 物件作為引數傳到onRestoreInstanceState與onCreate方法。因此可通過onRestoreInstanceState(Bundle savedInstanceState)和onCreate((Bundle savedInstanceState)來判斷Activity是否被重建,並取出資料進行恢復。但需要注意的是,在onCreate取出資料時一定要先判斷savedInstanceState是否為空。另外,谷歌更推薦使用onRestoreInstanceState進行資料恢復。
Q:說下Activity的四種啟動模式?(有時會出個實際問題來分析返回棧中Activity的情況)
- 技術點:Activity啟動模式
- 思路:分條解釋四個啟動模式的特點
- 參考回答:
- standard標準模式:每次啟動一個Activity就會建立一個新的例項
- singleTop棧頂複用模式:如果新Activity已經位於任務棧的棧頂,就不會重新建立,並回調 onNewIntent(intent) 方法
- singleTask棧內複用模式:只要該Activity在一個任務棧中存在,都不會重新建立,並回調 onNewIntent(intent) 方法。如果不存在,系統會先尋找是否存在需要的棧,如果不存在該棧,就建立一個任務棧,並把該Activity放進去;如果存在,就會建立到已經存在的棧中
- singleInstance單例項模式:具有此模式的Activity只能單獨位於一個任務棧中,且此任務棧中只有 唯一 一個例項
Q:談談singleTop和singleTask的區別以及應用場景
- 技術點:Activity啟動模式
- 思路:可先解釋兩個啟動模式的含義(見上一個問題),再總結不同點,最後給出應用例項
- 參考回答:singleTop和singleTask的含義分別是......,可見兩者大致區別有:
- singleTop:同個Activity例項在棧中可以有多個,即可能重複建立;該模式的Activity會預設進入啟動它所屬的任務棧,即不會引起任務棧的變更;為防止快速點選時多次startActivity,可以將目標Activity設定為singleTop
- singleTask:同個Activity例項在棧中只有一個,即不存在重複建立;可通過android:taskAffinity設定該Activity需要的任務棧,即可能會引起任務棧的變更;常用於主頁和登陸頁
Q:onNewIntent()呼叫時機?
- 技術點:Activity啟動模式
- 參考回答:啟動模式為singleTop或singleTask的Activity在以下情況會回撥onNewIntent():
- singleTop:如果新Activity已經位於任務棧的棧頂,就不會重新建立,並回調 onNewIntent(intent) 方法
- singleTask:只要該Activity在一個任務棧中存在,都不會重新建立,並回調 onNewIntent(intent) 方法
Q:瞭解哪些Activity啟動模式的標記位?
- 技術點:Activity啟動模式
- 參考回答:常見的兩個標記為:
- FLAG_ACTIVITY_SINGLE_TOP :對應singleTop啟動模式
- FLAG_ACTIVITY_NEW_TASK :對應singleTask模式
- 引申:可再談談singleTop和singleTask具體效果
Q:如何啟動其他應用的Activity?
- 技術點:Activity啟動、IntentFilter匹配
- 思路:可從隱式Intent角度出發
- 參考回答:在保證有許可權訪問的情況下,通過隱式Intent進行目標Activity的IntentFilter匹配,原則是:
- 一個intent只有 同時 匹配某個Activity的intent-filter中的action、category、data才算 完全匹配 ,才能啟動該Activity。
- 一個Activity可以有 多個 intent-filter,一個 intent只要成功匹配 任意一組 intent-filter,就可以啟動該Activity。
- 引申:如有必要可展開說明action、category、data的具體匹配規則
Q:Activity的啟動過程?
- 技術點:Activity啟動、ActivityManagerServie、ApplicationThread
- 思路:可大致介紹Activity啟動過程涉及到的類,尤其是ActivityManagerServie、ApplicationThread從中發揮的作用。詳見 要點提煉|開發藝術之四大元件
- 參考回答:呼叫startActivity()後經過重重方法會轉移到ActivityManagerService的startActivity(),並通過一個IPC回到ActivityThread的內部類 ApplicationThread 中,並呼叫其scheduleLaunchActivity()將啟動Activity的訊息傳送並交由 Handler H 處理。Handler H對訊息的處理會呼叫handleLaunchActivity()->performLaunchActivity()得以完成Activity物件的建立和啟動。
- 引申:由於ActivityManagerService是一個Binder物件,可引申談談Binder機制
Q:談一談Fragment的生命週期?
- 技術點:Fragment生命週期
- 思路:分條解釋Fragment從建立到銷燬整個生命週期中涉及到的方法及作用
- 參考回答:Fragment從建立到銷燬整個生命週期中涉及到的方法依次為:onAttach()->onCreate()-> onCreateView()->onActivityCreated()->onStart()->onResume()->onPause()->onStop()->onDestroyView()->onDestroy()->onDetach(),其中和Activity有不少名稱相同作用相似的方法,而不同的方法有:
- onAttach() :當Fragment和Activity建立關聯時呼叫
- onCreateView() :當Fragment建立檢視時呼叫
- onActivityCreated() :當與Fragment相關聯的Activity完成onCreate()之後呼叫
- onDestroyView() :在Fragment中的佈局被移除時呼叫
- onDetach() :當Fragment和Activity解除關聯時呼叫
Q:Activity和Fragment的異同?
- 技術點:Fragment作用
- 思路:分別解釋“異”“同”
- 參考回答:
- Activity和Fragment的相似點在於,它們都可包含佈局、可有自己的生命週期,Fragment可看似迷你活動。
- 不同點是,由於Fragment是依附在Activity上的,多了些和宿主Activity相關的生命週期方法,如onAttach()、onActivityCreated()、onDetach();另外,Fragment的生命週期方法是由宿主Activity而不是作業系統呼叫的,Activity中生命週期方法都是protected,而Fragment都是public,也能印證了這一點,因為Activity需要呼叫Fragment那些方法並管理它。
- 引申:可具體談談Activity和Fragment的關係
Q:Activity和Fragment的關係?
- 技術點:Fragment作用
- 思路:可從Fragment出現的目的、兩者數量關係、呼叫關係展開
- 參考回答:
- 正如Fragment的名字“碎片”,它的出現是為了解決Android碎片化 ,它可作為Activity介面的組成部分,可在Activity執行中實現動態地加入、移除和交換。
- 一個Activity中可同時出現多個Fragment,一個Fragment也可在多個Activity中使用。
- 另外,Activity的 FragmentManager 負責呼叫佇列中Fragment的生命週期方法,只要Fragment的狀態與Activity的狀態保持了同步,宿主Activity的FragmentManager便會繼續呼叫其他生命週期方法以繼續保持Fragment與Activity的狀態一致。
Q:何時會考慮使用Fragment?
- 技術點:Fragment作用
- 思路:列舉更適合使用Fragment的情況
- 參考回答:非常經典的例子,即用兩個Fragment封裝兩個介面模組,這樣只使一套程式碼就能適配兩種裝置,達到兩種介面效果;單一場景切換時使用Fragment更輕量化,如ViewPager和Fragment搭配使用
Q:談一談Service的生命週期?
- 技術點:Service生命週期
- 思路:分條解釋Service從建立到銷燬整個生命週期中涉及到的方法及作用
- 參考回答:在Service的生命週期涉及到六大方法,分別是:
- onCreate() :服務第一次被建立時呼叫
- onStartComand() :服務啟動時呼叫
- onBind() :服務被繫結時呼叫
- onUnBind() :服務被解綁時呼叫
- onDestroy() :服務停止時呼叫
- 引申:談談相對應的兩種啟動方式
Q:Service的兩種啟動方式?區別在哪?
- 技術點:Service生命週期
- 思路:分別解釋兩種啟動模式如何啟動和停止Service,並引起生命週期怎樣的變化
- 參考回答:
- 第一種,其他元件呼叫Context的 startService() 方法可以啟動一個Service,並回調服務中的onStartCommand()。如果該服務之前還沒建立,那麼回撥的順序是onCreate()->onStartCommand()。服務啟動了之後會一直保持執行狀態,直到 stopService() 或 stopSelf() 方法被呼叫,服務停止並回調onDestroy()。另外,無論呼叫多少次startService()方法,只需呼叫一次stopService()或stopSelf()方法,服務就會停止了。
- 第二種,其它元件呼叫Context的 bindService() 可以繫結一個Service,並回調服務中的onBind()方法。類似地,如果該服務之前還沒建立,那麼回撥的順序是onCreate()->onBind()。之後,呼叫方可以獲取到onBind()方法裡返回的IBinder物件的例項,從而實現和服務進行通訊。只要呼叫方和服務之間的連線沒有斷開,服務就會一直保持執行狀態,直到呼叫了 unbindService() 方法服務會停止,回撥順序onUnBind()->onDestroy()。
Q:一個Activty先start一個Service後,再bind時會回撥什麼方法?此時如何做才能回撥Service的destory()方法?
- 技術點:Service生命週期
- 參考回答:startService()啟動Service之後,再bindService()繫結,此時只會回撥onBind()方法;若想回調Service的destory()方法,需要同時呼叫 stopService()和 unbindService()方法才能讓服務銷燬掉。
Q:Service如何和Activity進行通訊?
- 技術點:Service資訊傳遞
- 思路:簡單介紹Service如何和Activity雙向通訊的流程
- 參考回答:通過bindService()可以實現Activity呼叫Service中的方法,具體步驟見 學習筆記| AS入門(十) 元件篇之Service ; 通過廣播實現Service向Activity傳送訊息
- 引申:談談底層的Binder機制
Q:用過哪些系統Service?
- 技術點:Service型別(系統Service)
-
參考回答:
Q:是否能在Service進行耗時操作?如果非要可以怎麼做?
- 技術點:Service使用注意
- 參考回答:Service預設並不會執行在子執行緒中,也不執行在一個獨立的程序中,它同樣執行在主執行緒中(UI執行緒)。換句話說,不要在Service裡執行耗時操作,除非手動開啟一個子執行緒,否則有可能出現主執行緒被阻塞(ANR)的情況。
- 引申:可以引申談談開子執行緒的幾種方法
Q:AlarmManager能實現定時的原理?
- 技術點:系統服務(後臺定時)
- 思路:AlarmManager
- 參考回答:通過呼叫AlarmManager的 set() 方法就可以設定一個定時任務,並提供三個引數(工作型別,定時任務觸發的時間,PendingIntent物件)。其中第三個PendingIntent物件是關鍵,一般會呼叫它的 getBroadcast() 方法來獲取一個能夠執行廣播的PendingIntent。這樣當定時任務被觸發的時候,廣播接收器的onReceive()方法就可以得到執行。即通過服務和廣播的迴圈觸發實現定時服務。
- 引申:可談談底層定時機制實現原理
Q:前臺服務是什麼?和普通服務的不同?如何去開啟一個前臺服務?
- 技術點:Service型別(前臺Service)
- 參考回答:和一般執行在後臺的服務不同,前臺服務的服務狀態可以被使用者一眼看到。它和普通服務最大的區別是,前者會一直有一個正在執行的圖示在系統的狀態列顯示,下拉狀態列後可以看到更加詳細的資訊,非常類似於通知的效果,且當系統記憶體不足服務被殺死時,通知會被移除。實現一個前臺服務也非常簡單,和傳送一個通知非常類似,只不過在構建好一個Notification之後,不需要NotificationManager將通知顯示出來,而是呼叫了 startForeground() 方法。
Q:是否瞭解ActivityManagerService,談談它發揮什麼作用?
- 技術點:Service型別(ActivityManagerService)
- 思路:可談談在四大元件建立中ActivityManagerService發揮的作用,詳見 要點提煉|開發藝術之四大元件
- 參考回答:ActivityManagerService是Android中最核心的服務 , 主要負責系統中四大元件的啟動、切換、排程及應用程序的管理和排程等工作,其職責與作業系統中的程序管理和排程模組類似。
- 引申:看原始碼談談AMS啟動過程: ActivityManagerService分析——AMS啟動流程
Q:如何保證Service不被殺死?
- 技術點:Service應用
- 思路:列舉幾種解決辦法
- 參考回答:可以採取以下幾種解決方法:
- 在Service的onStartCommand()中設定flages值為START_STICKY,使得Service被殺死後嘗試再次啟動Service
- 提升Service優先順序,比如設定為一個前臺服務
- 在Activity的onDestroy()通過傳送廣播,並在廣播接收器的onReceive()中啟動Service
Q:廣播有幾種形式?什麼特點?
- 技術點:Broadcast型別
- 思路:分條解釋每種廣播型別的特點
- 參考回答:常見以下四種廣播:
- 普通廣播:一種 完全非同步 執行的廣播,在廣播發出之後,所有的廣播接收器幾乎都會在同一時刻接收到這條廣播訊息,因此它們接收的先後是隨機的。
- 有序廣播:一種 同步執行 的廣播,在廣播發出之後,同一時刻只會有一個廣播接收器能夠收到這條廣播訊息,當這個廣播接收器中的邏輯執行完畢後,廣播才會繼續傳遞,所以此時的廣播接收器是有先後順序的,且優先順序(priority)高的廣播接收器會先收到廣播訊息。有序廣播可以被接收器截斷使得後面的接收器無法收到它。
- 本地廣播:發出的廣播只能夠在應用程式的 內部 進行傳遞,並且廣播接收器也只能接收本應用程式發出的廣播。
- 粘性廣播:這種廣播會一直滯留,當有匹配該廣播的接收器被註冊後,該接收器就會收到此條廣播。
Q:廣播的兩種註冊形式?區別在哪?
- 技術點:Broadcast使用
- 參考回答:廣播的註冊有兩種方法:一種在活動裡通過程式碼 動態註冊 ,另一種在配置檔案裡 靜態註冊 。兩種方式的相同點是都完成了對接收器以及它能接收的廣播值這兩個值的定義;不同點是動態註冊的接收器必須要在程式啟動之後才能接收到廣播,而靜態註冊的接收器即便程式未啟動也能接收到廣播,比如想接收到手機開機完成後系統發出的廣播就只能用靜態註冊了。
Q:ContentProvider瞭解多少?
- 技術點:ContentProvider
- 思路:ContentProvider功能
- 參考回答:作為四大元件之一,ContentProvider主要負責儲存和共享資料。與檔案儲存、SharedPreferences儲存、SQLite資料庫儲存這幾種資料儲存方法不同的是,後者儲存下的資料只能被該應用程式使用,而前者可以讓不同應用程式之間進行資料共享,它還可以選擇只對哪一部分資料進行共享,從而保證程式中的隱私資料不會有洩漏風險。
- 引申:談談ContentProvider底層使用Binder機制原理
Q:Android中提供哪些資料持久儲存的方法?
- 技術點:資料持久化
- 思路:分條解釋每種資料持久儲存的特點
- 參考回答:Android平臺實現資料儲存的常見幾種方式:
Q:Java中的I/O流讀寫怎麼做?
- 技術點:資料持久化(檔案儲存)
- 思路:大致介紹核心類和核心方法
- 參考回答:和 Java中實現I/O的程式是一樣的,Context類中提供了openFileInput()和openFileOutput()方法來開啟資料檔案裡的檔案IO流,有關讀寫涉及的主要方法詳見文章 學習筆記| AS入門(七) 資料儲存篇
Q:SharePreferences適用情形?使用中需要注意什麼?
- 技術點:資料持久化(SharePreferences儲存)
- 參考回答:SharePreferences是一種輕型的資料儲存方式,適用於儲存一些簡單的配置資訊,如int、string、boolean、float和long。由於系統對SharedPreferences的讀/寫有一定的快取策略,即在記憶體中有一份該檔案的快取,因此在多程序模式下,其讀/寫會變得不可靠,甚至丟失資料。
- 引申:談談Android中多程序通訊(Binder)
Q:瞭解SQLite中的事務處理嗎?是如何做的?
- 技術點:資料持久化(SQLite)
- 參考回答:SQLite在做CRDU操作時都預設開啟了事務,然後把SQL語句翻譯成對應的SQLiteStatement並呼叫其相應的CRUD方法,此時整個操作還是在rollback journal這個臨時檔案上進行,只有操作順利完成才會更新.db資料庫,否則會被回滾。
- 引申:談談如何模仿SQLite中事務的思想更高效進行批量操作
Q:使用SQLite做批量操作有什麼好的方法嗎?
- 技術點:資料持久化(SQLite)
- 思路:模仿SQLite的事務處理
- 參考回答:使用SQLiteDatabase的beginTransaction()方法開啟一個事務,將批量操作SQL語句轉化成SQLiteStatement並進行批量操作,結束後endTransaction()
Q:如果現在要刪除SQLite中表的一個欄位如何做?
- 技術點:資料持久化(SQLite)
- 參考回答:SQLite資料庫只允許增加表字段而不允許修改和刪除表字段,只能採取複製表思想,即建立一個新表保留原表想要的欄位、再將原表刪除
Q:使用SQLite時會有哪些優化操作?
- 技術點:資料持久化(SQLite)
- 思路:列舉可優化點
- 參考回答:
- 使用事務做批量操作:具體操作見上
- 及時關閉Cursor,避免記憶體洩漏
- 耗時操作非同步化:資料庫的操作屬於本地IO,通常比較耗時,建議將這些耗時操作放入非同步執行緒中處理
- ContentValues的容量調整:ContentValues內部採用HashMap來儲存Key-Value資料,ContentValues初始容量為8,擴容時翻倍。因此建議對ContentValues填入的內容進行估量,設定合理的初始化容量,減少不必要的內部擴容操作
- 使用索引加快檢索速度:對於查詢操作量級較大、業務對要求查詢要求較高的推薦使用索引
Q:Android中程序和執行緒的關係?
- 技術點:程序、執行緒
- 參考回答:
- 形象理解:如果把安卓系統比喻成一片土壤,可以把App看做紮根在這片土壤上的工廠,每個APP一般對應一個程序,那麼執行緒就像是工廠的生產線。其中,主執行緒好比是主生產線,只有一條,子執行緒就像是副生產線,可以有很多條。
- 關係:一個APP一般對應一個程序和有限個執行緒
- 一般對應一個程序,當然,可以在AndroidMenifest中給四大元件指定屬性
android:process
開啟多程序模式 - 有限個執行緒:執行緒是一種 受限 的系統資源,不可無限制的產生且執行緒的建立和銷燬都有一定的開銷。
- 一般對應一個程序,當然,可以在AndroidMenifest中給四大元件指定屬性
Q:為何需要進行IPC?多程序通訊可能會出現什麼問題?
- 技術點:多程序通訊
- 思路:討論多程序通訊會出現的問題得出IPC的必要性
- 參考回答:
- (1) 多程序造成的影響 可總結為以下四方面:
- 靜態變數和單例模式失效:由獨立的虛擬機器造成
- 執行緒同步機制失效:由獨立的虛擬機器造成
- SharedPreference的不可靠下降:不支援兩個程序同時進行讀寫操作,即不支援併發讀寫,有一定機率導致資料丟失
- Application多次建立: Android系統會為新的程序分配獨立虛擬機器,相當於系統又把這個應用重新啟動了一次。
- (2) 需要程序間通訊的必要性 :所有執行在不同程序的四大元件,只要它們之間需要通過記憶體在共享資料,都會共享失敗。這是由於Android為每個應用分配了 獨立 的虛擬機器,不同的虛擬機器在記憶體分配上有不同的地址空間,這會導致在不同的虛擬機器中訪問同一個類的物件會產生多份副本。
- 引申: 談談IPC的使用場景
Q:什麼是序列化?Serializable介面和Parcelable介面的區別?為何推薦使用後者?
- 技術點:序列化
- 參考回答:序列化表示將一個物件轉換成 可儲存或可傳輸 的狀態。序列化後的物件可以在網路上進行傳輸,也可以儲存到本地。
- 應用場景:需要通過Intent和Binder等傳輸 類物件 就必須完成物件的序列化過程。
-
兩種方式:實現Serializable/Parcelable介面。不同點如圖:
Q:Android中為何新增Binder來作為主要的IPC方式?
- 技術點:Binder機制
- 思路:回答Binder優點
- 參考回答:Binder機制有什麼幾條 優點 :
-
傳輸效率高、可操作性強:傳輸效率主要影響因素是記憶體拷貝的次數,拷貝次數越少,傳輸速率越高。從Android程序架構角度分析:對於訊息佇列、Socket和管道來說,資料先從傳送方的快取區拷貝到核心開闢的快取區中,再從核心快取區拷貝到接收方的快取區,一共兩次拷貝,如圖:
而對於Binder來說,資料從傳送方的快取區拷貝到核心的快取區,而接收方的快取區與核心的快取區是對映到同一塊實體地址的,節省了一次資料拷貝的過程,如圖:
- 實現C/S架構方便:Linux的眾IPC方式除了Socket以外都不是基於C/S架構,而Socket主要用於網路間的通訊且傳輸效率較低。Binder基於C/S架構 ,Server端與Client端相對獨立,穩定性較好。
- 安全性高:傳統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(Android Interface Definition Language, Android介面定義語言 ):如果在一個程序中要呼叫另一個程序中物件的方法,可使用AIDL生成可序列化的引數,AIDL會生成一個服務端物件的 代理類 ,通過它客戶端實現間接呼叫服務端物件的方法。
- AIDL的 本質 是系統提供了一套可快速實現Binder的工具。關鍵類和方法:
- AIDL介面 :繼承 IInterface 。
- Stub類 :Binder的實現類,服務端通過這個類來提供服務。
- Proxy類 :伺服器的本地代理,客戶端通過這個類呼叫伺服器的方法。
- asInterface() :客戶端呼叫,將服務端的返回的Binder物件,轉換成客戶端所需要的AIDL介面型別物件。返回物件:
- 若客戶端和服務端位於同一程序,則直接返回Stub物件本身;
- 否則,返回的是系統封裝後的Stub.proxy物件。
- asBinder() :根據當前呼叫情況返回代理Proxy的Binder物件。
- onTransact() :執行服務端的Binder執行緒池中,當客戶端發起跨程序請求時,遠端請求會通過系統底層封裝後交由此方法來處理。
- transact() :執行在客戶端,當客戶端發起遠端請求的同時將當前執行緒掛起。之後呼叫服務端的onTransact()直到遠端請求返回,當前執行緒才繼續執行。
- 當有多個業務模組都需要AIDL來進行IPC,此時需要為每個模組建立特定的aidl檔案,那麼相應的Service就會很多。必然會出現系統資源耗費嚴重、應用過度重量級的問題。解決辦法是建立Binder連線池,即將每個業務模組的Binder請求 統一 轉發到一個遠端Service中去執行,從而避免重複建立Service。
-
工作原理:每個業務模組建立自己的AIDL介面並實現此介面,然後向服務端提供自己的唯一標識和其對應的Binder物件。服務端只需要一個Service,伺服器提供一個queryBinder介面,它會根據業務模組的特徵來返回相應的Binder對像,不同的業務模組拿到所需的Binder物件後就可進行遠端方法的呼叫了。流程如圖:
-
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/">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()可在子執行緒中直接呼叫。
- Drawable等資源
Q:瞭解哪些Drawable?適用場景?
- 技術點:res資源
- 參考回答:BitmapDrawable表示一張圖片、NinePatchDrawable可自動地根據所需的寬/高對圖片進行相應的縮放並保證不失真、ShapeDrawable表示純色、有漸變效果的基礎幾何圖形、StateListDrawable表示一個Drawable的集合且每個Drawable對應著View的一種狀態、LayerDrawable可通過將不同的Drawable放置在不同的層上面從而達到一種疊加後的效果
Q:mipmap系列中xxxhdpi、xxhdpi、xhdpi、hdpi、mdpi和ldpi存在怎樣的關係?
- 技術點:res資源
- 參考回答:表示不同密度的圖片資源,畫素從高到低依次排序為xxxhdpi>xxhdpi>xhdpi>hdpi>mdpi>ldpi,根據手機的dpi不同載入不同密度的圖片
Q:dp、dpi、px的區別?
- 技術點:Android適配
- 參考回答:
- px:畫素,如解析度1920x1080表示高為1920個畫素、寬為1080個畫素
- dpi:每英寸的畫素點,如解析度為1920x1080的手機尺寸為4.95英寸,則該手機DPI為(1920x1920+ 1080x1080)½/4.95≈445dpi
- dp:密度無關畫素,是個相對值
Q:res目錄和assets目錄的區別?
- 技術點:res、assets
- 參考回答:
- res/raw中的檔案會被對映到R.java檔案中,訪問時可直接使用資源ID,不可以有目錄結構
- assets資料夾下的檔案不會被對映到R.java中,訪問時需要AssetManager類,可以建立子資料夾