金三銀四,衝擊大廠,你值得擁有的一份2019初中級移動端社招面試總結+解答
你當前所處:Android篇:2019初中級Android開發社招面試解答(下)
Android篇:2019初中級Android開發社招面試解答(上)
Android篇:2019初中級Android開發社招面試解答(中)
注:因為實際開發與參考答案會有所不同,再者怕誤導大家,所以這些面試題答案還是自己去理解!面試官會針對簡歷中提到的知識點由淺入深提問,所以不要背答案,多理解。
Android進階延伸點
1、如何進行單元測試,如何保證App穩定 ?
- 參考回答:
- 要測試Android應用程式,通常會建立以下型別自動單元測試
- 本地測試:只在本地機器JVM上執行,以最小化執行時間,這種單元測試不依賴於Android框架,或者即使有依賴,也很方便使用模擬框架來模擬依賴,以達到隔離Android依賴的目的,模擬框架如Google推薦的Mockito;
- Android官網-建立本地單元測試
- 檢測測試:真機或模擬器上執行的單元測試,由於需要跑到裝置上,比較慢,這些測試可以訪問儀器(Android系統)資訊,比如被測應用程式的上下文,一般地,依賴不太方便通過模擬框架模擬時採用這種方式;
- Android官網-建立儀表單元測試
- 注意:單元測試不適合測試複雜的UI互動事件
- 推薦文章:Android 單元測試只看這一篇就夠了
- App的穩定主要決定於整體的系統架構設計,同時也不可忽略程式碼程式設計的細節規範,正所謂“千里之堤,潰於蟻穴”,一旦考慮不周,看似無關緊要的程式碼片段可能會帶來整體軟體系統的崩潰,所以上線之前除了自己本地化測試之外還需要進行Monkey壓力測試
- 少部分面試官可能會延伸,如Gradle自動化測試、機型適配測試等
2、Android中如何檢視一個物件的回收情況 ?
- 參考回答:
- 首先要了解Java四種引用型別的場景和使用(強引用、軟引用、弱引用、虛引用)
- 舉個場景例子:SoftReference物件是用來儲存軟引用的,但它同時也是一個Java物件,所以當軟引用物件被回收之後,雖然這個SoftReference物件的get方法返回null,但SoftReference物件本身並不是null,而此時這個SoftReference物件已經不再具有存在的價值,需要一個適當的清除機制,避免大量SoftReference物件帶來的記憶體洩露
- 因此,Java提供ReferenceQueue來處理引用物件的回收情況。當SoftReference所引用的物件被GC後,JVM會先將softReference物件新增到ReferenceQueue這個佇列中。當我們呼叫ReferenceQueue的poll()方法,如果這個佇列中不是空佇列,那麼將返回並移除前面新增的那個Reference物件。
- 推薦文章:Java中的四種引用型別:強引用、軟引用、弱引用和虛引用
3、Apk的大小如何壓縮 ?
- 參考回答:
- 一個完整APK包含以下目錄(將APK檔案拖到Android Studio):
- META-INF/:包含CERT.SF和CERT.RSA簽名檔案以及MANIFEST.MF 清單檔案。
- assets/:包含應用可以使用AssetManager物件檢索的應用資源。
- res/:包含未編譯到的資源 resources.arsc。
- lib/:包含特定於處理器軟體層的編譯程式碼。該目錄包含了每種平臺的子目錄,像armeabi,armeabi-v7a, arm64-v8a,x86,x86_64,和mips。
- resources.arsc:包含已編譯的資源。該檔案包含res/values/ 資料夾所有配置中的XML內容。打包工具提取此XML內容,將其編譯為二進位制格式,並將內容歸檔。此內容包括語言字串和樣式,以及直接包含在**resources.arsc*8檔案中的內容路徑 ,例如佈局檔案和影象。
- classes.dex:包含以Dalvik / ART虛擬機器可理解的DEX檔案格式編譯的類。
- AndroidManifest.xml:包含核心Android清單檔案。該檔案列出應用程式的名稱,版本,訪問許可權和引用的庫檔案。該檔案使用Android的二進位制XML格式。
- lib、class.dex和res佔用了超過90%的空間,所以這三塊是優化Apk大小的重點(實際情況不唯一)
- 減少res,壓縮圖文檔案
- 圖片檔案壓縮是針對jpg和png格式的圖片。我們通常會放置多套不同解析度的圖片以適配不同的螢幕,這裡可以進行適當的刪減。在實際使用中,只保留一到兩套就足夠了(保留一套的話建議保留xxhdpi,兩套的話就加上hdpi),然後再對剩餘的圖片進行壓縮(jpg採用優圖壓縮,png嘗試採用pngquant壓縮)
- 減少dex檔案大小
- 新增資源混淆
- shrinkResources為true表示移除未引用資源,和程式碼壓縮協同工作。
- minifyEnabled為true表示通過ProGuard啟用程式碼壓縮,配合proguardFiles的配置對程式碼進行混淆並移除未使用的程式碼。
- 程式碼混淆在壓縮apk的同時,也提升了安全性。
- 推薦文章:Android混淆最佳實踐
- 減少lib檔案大小
- 由於引用了很多第三方庫,lib資料夾佔用的空間通常都很大,特別是有so庫的情況下。很多so庫會同時引入armeabi、armeabi-v7a和x86這幾種型別,這裡可以只保留armeabi或armeabi-v7a的其中一個就可以了,實際上微信等主流app都是這麼做的。
- 只需在build.gradle直接配置即可,NDK配置同理
- 推薦文章:
4、如何通過Gradle配置多渠道包?
- 參考回答:
- 首先要了解設定多渠道的原因。在安裝包中新增不同的標識,配合自動化埋點,應用在請求網路的時候攜帶渠道資訊,方便後臺做運營統計,比如說統計我們的應用在不同應用市場的下載量等資訊
- 這裡以友盟統計為例
- 首先在manifest.xml檔案中設定動態渠道變數:
- 接著在app目錄下的build.gradle中配置productFlavors,也就是配置打包的渠道:
- 最後在編輯器下方的Teminal輸出命令列
- 執行./gradlew assembleRelease ,將會打出所有渠道的release包;
- 執行./gradlew assembleVIVO,將會打出VIVO渠道的release和debug版的包;
- 執行./gradlew assembleVIVORelease將生成VIVO的release包。
- 推薦文章:
5、外掛化原理分析
- 參考回答:
- 外掛化是指將 APK 分為宿主和外掛的部分。把需要實現的模組或功能當做一個獨立的提取出來,在 APP 執行時,我們可以動態的載入或者替換外掛部分,減少宿主的規模
- 宿主: 就是當前執行的APP。
- 外掛: 相對於外掛化技術來說,就是要載入執行的apk類檔案。
- 而熱修復則是從修復bug的角度出發,強調的是在不需要二次安裝應用的前提下修復已知的bug。能
- 類載入機制
- Android中常用的兩種類載入器,DexClassLoader和PathClassLoader,它們都繼承於BaseDexClassLoader,兩者區別在於PathClassLoader只能載入內部儲存目錄的dex/jar/apk檔案。DexClassLoader支援載入指定目錄(不限於內部)的dex/jar/apk檔案
- 外掛通訊:通過給外掛apk生成相應的DexClassLoader便可以訪問其中的類,可分為單DexClassLoader和多DexClassLoader兩種結構。
- 若使用多ClassLoader機制,主工程引用外掛中類需要先通過外掛的ClassLoader載入該類再通過反射呼叫其方法。外掛化框架一般會通過統一的入口去管理對各個外掛中類的訪問,並且做一定的限制。
- 若使用單ClassLoader機制,主工程則可以直接通過類名去訪問外掛中的類。該方式有個弊端,若兩個不同的外掛工程引用了一個庫的不同版本,則程式可能會出錯。
- 資源載入
- 原理在於通過反射將外掛apk的路徑加入AssetManager中並建立Resource物件載入資源,有兩種處理方式:
- 合併式:addAssetPath時加入所有外掛和主工程的路徑;由於AssetManager中加入了所有外掛和主工程的路徑,因此生成的Resource可以同時訪問外掛和主工程的資源。但是由於主工程和各個外掛都是獨立編譯的,生成的資源id會存在相同的情況,在訪問時會產生資源衝突。
- 獨立式:各個外掛只新增自己apk路徑,各個外掛的資源是互相隔離的,不過如果想要實現資源的共享,必須拿到對應的Resource物件。
- 推薦文章:
6、元件化原理
- 參考回答:
- 引入元件化的原因:專案隨著需求的增加規模變得越來越大,規模的增大導致了各種業務錯中複雜的交織在一起, 每個業務模組之間,程式碼沒有約束,帶來了程式碼邊界的模糊,程式碼衝突時有發生, 更改一個小問題可能引起一些新的問題, 牽一髮而動全身,增加一個新需求,需要熟悉相關的程式碼邏輯,增加開發時間
- 避免重複造輪子,可以節省開發和維護的成本。
- 可以通過元件和模組為業務基準合理地安排人力,提高開發效率。
- 不同的專案可以共用一個元件或模組,確保整體技術方案的統一性。
- 為未來外掛化共用同一套底層模型做準備。
- 元件化開發流程就是把一個功能完整的App或模組拆分成多個子模組(Module),每個子模組可以獨立編譯執行,也可以任意組合成另一個新的 App或模組,每個模組即不相互依賴但又可以相互互動,但是最終釋出的時候是將這些元件合併統一成一個apk,遇到某些特殊情況甚至可以升級或者降級
- 舉個簡單的模型例子 App是主application,ModuleA和ModuleB是兩個業務模組(相對獨立,互不影響),Library是基礎模組,包含所有模組需要的依賴庫,以及一些工具類:如網路訪問、時間工具等
7、跨元件通訊
- 參考回答:
- 跨元件通訊場景:
- 第一種是元件之間的頁面跳轉 (Activity 到 Activity, Fragment 到 Fragment, Activity 到 Fragment, Fragment 到 Activity) 以及跳轉時的資料傳遞 (基礎資料型別和可序列化的自定義類型別)。
- 第二種是元件之間的自定義類和自定義方法的呼叫(元件向外提供服務)。
- 跨元件通訊方案分析:
- 第一種元件之間的頁面跳轉實現簡單,跳轉時想傳遞不同型別的資料提供有相應的 API即可。
- 第二種元件之間的自定義類和自定義方法的呼叫要稍微複雜點,需要 ARouter 配合架構中的 公共服務(CommonService) 實現:
- 提供服務的業務模組:
- 在公共服務(CommonService) 中宣告 Service 介面 (含有需要被呼叫的自定義方法), 然後在自己的模組中實現這個 Service 介面, 再通過 ARouter API 暴露實現類。
- 使用服務的業務模組:
- 通過 ARouter 的 API 拿到這個 Service 介面(多型持有, 實際持有實現類), 即可呼叫 Service 介面中宣告的自定義方法, 這樣就可以達到模組之間的互動。
- 此外,可以使用 AndroidEventBus 其獨有的 Tag, 可以在開發時更容易定位傳送事件和接受事件的程式碼, 如果以元件名來作為 Tag 的字首進行分組, 也可以更好的統一管理和檢視每個元件的事件, 當然也不建議大家過多使用 EventBus。
- 如何管理過多的路由表?
- RouterHub 存在於基礎庫, 可以被看作是所有元件都需要遵守的通訊協議, 裡面不僅可以放路由地址常量, 還可以放跨元件傳遞資料時命名的各種 Key 值, 再配以適當註釋, 任何元件開發人員不需要事先溝通只要依賴了這個協議, 就知道了各自該怎樣協同工作, 既提高了效率又降低了出錯風險, 約定的東西自然要比口頭上說強。
- Tips: 如果您覺得把每個路由地址都寫在基礎庫的 RouterHub 中, 太麻煩了, 也可以在每個元件內部建立一個私有 RouterHub, 將不需要跨元件的路由地址放入私有 RouterHub 中管理, 只將需要跨元件的路由地址放入基礎庫的公有 RouterHub 中管理, 如果您不需要集中管理所有路由地址的話, 這也是比較推薦的一種方式。
- ARouter路由原理:
- ARouter維護了一個路由表Warehouse,其中儲存著全部的模組跳轉關係,ARouter路由跳轉實際上還是呼叫了startActivity的跳轉,使用了原生的Framework機制,只是通過apt註解的形式製造出跳轉規則,並人為地攔截跳轉和設定跳轉條件。
- 常見的元件化方案如下
8、元件化中路由、埋點的實現
- 參考回答:
- 因為在元件化中,各個業務模組之間是各自獨立的, 並不會存在相互依賴的關係, 所以一個業務模組是訪問不了其他業務模組的程式碼的, 如果想從 A 業務模組的 A 頁面跳轉到 B 業務模組的 B 頁面, 光靠模組自身是不能實現的,這就需要一種跨元件通訊方案—— 路由(Router)
- 路由主要有以下兩種場景:
- 第一種是元件之間的頁面跳轉 (Activity 到 Activity, Fragment 到 Fragment, Activity 到 Fragment, Fragment 到 Activity) 以及跳轉時的資料傳遞 (基礎資料型別和可序列化的自定義類型別)
- 第二種是元件之間的自定義類和自定義方法的呼叫(元件向外提供服務)
- 其原理在於將分佈在不同元件module中的某些類按照一定規則生成對映表(資料結構通常是Map,Key為一個字串,Value為類或物件),然後在需要用到的時候從對映表中根據字串從對映表中取出類或物件,本質上是類的查詢
- 埋點則是在應用中特定的流程收集一些資訊,用來跟蹤應用使用的狀況
- 程式碼埋點:在某個事件發生時呼叫SDK裡面相應的介面傳送埋點資料,百度統計、友盟、TalkingData、Sensors Analytics等第三方資料統計服務商大都採用這種方案
- 全埋點:全埋點指的是將Web頁面/App內產生的所有的、滿足某個條件的行為,全部上報到後臺伺服器
- 視覺化埋點:通過視覺化工具(例如Mixpanel)配置採集節點,在Android端自動解析配置並上報埋點資料,從而實現所謂的自動埋點
- 無埋點:它並不是真正的不需要埋點,而是Android端自動採集全部事件並上報埋點資料,在後端資料計算時過濾出有用資料
- 推薦文章:
9、Hook以及插樁技術
- 參考回答:
- Hook是一種用於改變API執行結果的技術,能夠將系統的API函式執行重定向(應用的觸發事件和後臺邏輯處理是根據事件流程一步步地向下執行。而Hook的意思,就是在事件傳送到終點前截獲並監控事件的傳輸,像個鉤子鉤上事件一樣,並且能夠在鉤上事件時,處理一些自己特定的事件,例如逆向破解App)
- Android 中的 Hook 機制,大致有兩個方式:
- 要 root 許可權,直接 Hook 系統,可以幹掉所有的 App。
- 無 root 許可權,但是隻能 Hook 自身app,對系統其它 App 無能為力。
- 插樁是以靜態的方式修改第三方的程式碼,也就是從編譯階段,對原始碼(中間程式碼)進行編譯,而後重新打包,是靜態的篡改; 而Hook則不需要再編譯階段修改第三方的原始碼或中間程式碼,是在執行時通過反射的方式修改呼叫,是一種動態的篡改
- 推薦文章:
10、Android的簽名機制?
- 參考回答:
- Android的簽名機制包含有訊息摘要、數字簽名和數字證照
- 訊息摘要:在訊息資料上,執行一個單向的 Hash 函式,生成一個固定長度的Hash值
- 數字簽名:一種以電子形式儲存訊息簽名的方法,一個完整的數字簽名方案應該由兩部分組成:簽名演算法和驗證演算法
- 數字證照:一個經證照授權(Certificate Authentication)中心數字簽名的包含公鑰擁有者資訊以及公鑰的檔案
- 推薦文章:
11、v3簽名key和v2還有v1有什麼區別
- 參考回答:
- 在v1版本的簽名中,簽名以檔案的形式存在於apk包中,這個版本的apk包就是一個標準的zip包,V2和V1的差別是V2是對整個zip包進行簽名,而且在zip包中增加了一個apk signature block,裡面儲存簽名資訊。
- v2版本簽名塊(APK Signing Block)本身又主要分成三部分:
- SignerData(簽名者資料):主要包括簽名者的證照,整個APK完整性校驗hash,以及一些必要資訊
- Signature(簽名):開發者對SignerData部分資料的簽名資料
- PublicKey(公鑰):用於驗籤的公鑰資料
- v3版本簽名塊也分成同樣的三部分,與v2不同的是在SignerData部分,v3新增了attr塊,其中是由更小的level塊組成。每個level塊中可以儲存一個證照資訊。前一個level塊證照驗證下一個level證照,以此類推。最後一個level塊的證照,要符合SignerData中本身的證照,即用來簽名整個APK的公鑰所屬於的證照
- 推薦文章:
12、Android5.0~10.0之間大的變化
- 參考回答:
- Android5.0新特性
- MaterialDesign設計風格
- 支援64位ART虛擬機器(5.0推出的ART虛擬機器,在5.0之前都是Dalvik。他們的區別是:Dalvik,每次執行,位元組碼都需要通過即時編譯器轉換成機器碼(JIT)。 ART,第一次安裝應用的時候,位元組碼就會預先編譯成機器碼(AOT))
- 通知詳情可以使用者自己設計
- Android6.0新特性
- 動態許可權管理
- 支援快速充電的切換
- 支援資料夾拖拽應用
- 相機新增專業模式
- Android7.0新特性
- 多視窗支援
- V2簽名
- 增強的Java8語言模式
- 夜間模式
- Android8.0(O)新特性
- 優化通知:通知渠道 (Notification Channel) 通知標誌 休眠 通知超時 通知設定 通知清除
- 畫中畫模式:清單中Activity設定android:supportsPictureInPicture
- 後臺限制
- 自動填充框架
- 系統優化
- 等等優化很多
- Android9.0(P)新特性
- 室內WIFI定位
- “劉海”螢幕支援
- 安全增強
- 等等優化很多
- Android10.0(Q)目前曝光的新特性
- 夜間模式:包括手機上的所有應用都可以為其設定暗黑模式。
- 桌面模式:提供類似於PC的體驗,但是遠遠不能代替PC。
- 螢幕錄製:通過長按“電源”選單中的"螢幕快照"來開啟。
- 推薦文章:Android Developers 官方文件
13、說下Measurepec這個類
- 參考回答:
- 作用:通過寬測量值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值共同決定。具體規則見下圖:
14、請例舉Android中常用佈局型別,並簡述其用法以及排版效率
- 參考回答:
- Android中常用佈局分為傳統佈局和新型佈局
- 傳統佈局(編寫XML程式碼、程式碼生成):
- 框架佈局(FrameLayout):
- 線性佈局(LinearLayout):
- 絕對佈局(AbsoluteLayout):
- 相對佈局(RelativeLayout):
- 表格佈局(TableLayout):
- 新型佈局(視覺化拖拽控制元件、編寫XML程式碼、程式碼生成):
- 約束佈局(ConstrainLayout):
- 注:圖片出自Carson_Ho的Android:常用佈局介紹&屬性設定大全
- 對於巢狀多層View而言,其排版效率:LinearLayout = FrameLayout >> RelativeLayout
15、區別Animation和Animator的用法,概述其原理
- 參考回答:
- 動畫的種類:前者只有透明度,旋轉,平移,伸縮4種屬性,而對於後者,只要是該控制元件的屬性,且有setter該屬性的方法就都可以對該屬性執行一種動態變化的效果。
- 可操作的物件:前者只能對UI元件執行動畫,但屬性動畫幾乎可以對任何物件執行動畫(不管它是否顯示在螢幕上)。
- 動畫播放順序:在Animator中,AnimatorSet正是通過playTogether()、playSequentially()、animSet.play().with()、before()、after()這些方法來控制多個動畫協同工作,從而做到對動畫播放順序的精確控制
16、使用過什麼圖片載入庫?Glide的原始碼設計哪裡很微妙?
- 參考回答:
- 圖片載入庫:Fresco、Glide、Picasso等
- Glide的設計微妙在於:
- Glide的生命週期繫結:可以控制圖片的載入狀態與當前頁面的生命週期同步,使整個載入過程隨著頁面的狀態而啟動/恢復,停止,銷燬
- Glide的快取設計:通過(三級快取,Lru演算法,Bitmap複用)對Resource進行快取設計
- Glide的完整載入過程:採用Engine引擎類暴露了一系列方法供Request操作
- 推薦文章:
17、如何繞過9.0限制?
- 參考回答:
18、用過哪些網路載入庫?OkHttp、Retrofit實現原理?
- 參考回答:
- 網路載入庫:OkHttp、Retrofit、xUtils、Volley等
- 推薦文章:
19、對於應用更新這塊是如何做的? (灰度,強制更新、分割槽域更新)
- 參考回答:
- 內部更新:
- 通過介面獲取線上版本號,versionCode
- 比較線上的versionCode 和本地的versionCode,彈出更新視窗
- 下載APK檔案(檔案下載)
- 安裝APK
- 灰度更新:
- 找單一渠道投放特別版本。
- 做升級平臺的改造,允許針對部分使用者推送升級通知甚至版本強制升級。
- 開放單獨的下載入口。
- 是兩個版本的程式碼都打到app包裡,然後在app端植入測試框架,用來控制顯示哪個版本。測試框架負責與伺服器端api通訊,由伺服器端控制app上A/B版本的分佈,可以實現指定的一組使用者看到A版本,其它使用者看到B版本。服務端會有相應的報表來顯示A/B版本的數量和效果對比。最後可以由服務端的後臺來控制,全部使用者線上切換到A或者B版本~
- 無論哪種方法都需要做好版本管理工作,分配特別的版本號以示區別。 當然,既然是做灰度,資料監控(常規資料、新特性資料、主要業務資料)還是要做到位,該打的資料樁要打。 還有,灰度版最好有收回的能力,一般就是強制升級下一個正式版。
- 強制更新:
- 一般的處理就是進入應用就彈窗通知使用者有版本更新,彈窗可以沒有取消按鈕並不能取消。這樣使用者就只能選擇更新或者關閉應用了,當然也可以新增取消按鈕,但是如果使用者選擇取消則直接退出應用。
- 增量更新:
- 二進位制差分工具bsdiff是相應的補丁合成工具,根據兩個不同版本的二進位制檔案,生成補丁檔案.patch檔案。通過bspatch使舊的apk檔案與不定檔案合成新的apk。 注意通過apk檔案的md5值進行區分版本。
20、會用Kotlin、Fultter嗎? 談談你的理解
- 參考回答:
- Kotlin是一種具有型別推斷的跨平臺,靜態型別的通用程式語言。 Kotlin旨在與Java完全互操作,其標準庫的JVM版本依賴於Java類庫,但型別推斷允許其語法更簡潔。
- Flutter是由Google建立的開源移動應用程式開發框架。它用於開發Android和iOS的應用程式,以及為Google Fuchsia建立應用程式的主要方法
- 關於kotlin的重要性,相信大家在日常開發可以體會到,應用到實際開發中,需要避免語法糖(例如單列模式、空值判斷、高階函式等)
- 至於Flutter,目前Google官方文件還不完善,市面上採用此語言編寫的專案較少,如需要具體深入,請參考閒魚和官方文件