2019最新Android中級面試題目彙總解答

注:因為實際開發與參考答案會有所不同,再者怕誤導大家,所以這些面試題答案還是自己去理解!面試官會針對簡歷中提到的知識點由淺入深提問,所以不要背答案,多理解。

Android進階延伸點

1、如何進行單元測試,如何保證App穩定 ?

參考回答:

要測試Android應用程序,通常會創建以下類型自動單元測試

本地測試:只在本地機器JVM上運行,以最小化執行時間,這種單元測試不依賴於Android框架,或者即使有依賴,也很方便使用模擬框架來模擬依賴,以達到隔離Android依賴的目的,模擬框架如Google推薦的Mockito;檢測測試:真機或模擬器上運行的單元測試,由於需要跑到設備上,比較慢,這些測試可以訪問儀器(Android系統)信息,比如被測應用程序的上下文,一般地,依賴不太方便通過模擬框架模擬時採用這種方式;

注意:單元測試不適合測試複雜的UI交互事件

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對象。


3.Apk的大小如何壓縮 ?

參考回答:

3.1一個完整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,和mipsresources.arsc:包含已編譯的資源。該文件包含res/values/ 文件夾所有配置中的XML內容。打包工具提取此XML內容,將其編譯為二進制格式,並將內容歸檔。此內容包括語言字符串和樣式,以及直接包含在*resources.arsc8文件中的內容路徑 ,例如佈局文件和圖像。classes.dex:包含以Dalvik / ART虛擬機可理解的DEX文件格式編譯的類。AndroidManifest.xml:包含核心Android清單文件。該文件列出應用程序的名稱,版本,訪問權限和引用的庫文件。該文件使用Android的二進制XML格式。



lib、class.dex和res佔用了超過90%的空間,所以這三塊是優化Apk大小的重點(實際情況不唯一)

3.2.減少res,壓縮圖文文件

圖片文件壓縮是針對jpg和png格式的圖片。我們通常會放置多套不同分辨率的圖片以適配不同的屏幕,這裡可以進行適當的刪減。在實際使用中,只保留一到兩套就足夠了(保留一套的話建議保留xxhdpi,兩套的話就加上hdpi),然後再對剩餘的圖片進行壓縮(jpg採用優圖壓縮,png嘗試採用pngquant壓縮)

3.3.減少dex文件大小

添加資源混淆


shrinkResources為true表示移除未引用資源,和代碼壓縮協同工作。minifyEnabled為true表示通過ProGuard啟用代碼壓縮,配合proguardFiles的配置對代碼進行混淆並移除未使用的代碼。代碼混淆在壓縮apk的同時,也提升了安全性。

3.4.減少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是基礎模塊,包含所有模塊需要的依賴庫,以及一些工具類:如網絡訪問、時間工具等

注意:提供給各業務模塊的基礎組件,需要根據具體情況拆分成 aar 或者 library,像登錄,基礎網絡層這樣較為穩定的組件,一般直接打包成 aar,減少編譯耗時。而像自定義 View 組件,由於隨著版本迭代會有較多變化,就直接以源碼形式抽離成 Library

7.跨組件通信

參考回答:

7.1.跨組件通信場景:

第一種是組件之間的頁面跳轉 (Activity 到 Activity, Fragment 到 Fragment, Activity 到 Fragment, Fragment 到 Activity) 以及跳轉時的數據傳遞 (基礎數據類型和可序列化的自定義類類型)。第二種是組件之間的自定義類和自定義方法的調用(組件向外提供服務)。

7.2.跨組件通信方案分析:

第一種組件之間的頁面跳轉實現簡單,跳轉時想傳遞不同類型的數據提供有相應的 API即可第二種組件之間的自定義類和自定義方法的調用要稍微複雜點,需要 ARouter 配合架構中的 公共服務(CommonService) 實現:提供服務的業務模塊:
在公共服務(CommonService) 中聲明 Service 接口 (含有需要被調用的自定義方法), 然後在自己的模塊中實現這個 Service 接口, 再通過 ARouter API 暴露實現類。
使用服務的業務模塊:通過 ARouter 的 API 拿到這個 Service 接口(多態持有, 實際持有實現類), 即可調用 Service 接口中聲明的自定義方法, 這樣就可以達到模塊之間的交互。此外,可以使用 AndroidEventBus 其獨有的 Tag, 可以在開發時更容易定位發送事件和接受事件的代碼, 如果以組件名來作為 Tag 的前綴進行分組, 也可以更好的統一管理和查看每個組件的事件, 當然也不建議大家過多使用 EventBus。

7.3.如何管理過多的路由表?

RouterHub 存在於基礎庫, 可以被看作是所有組件都需要遵守的通訊協議, 裡面不僅可以放路由地址常量, 還可以放跨組件傳遞數據時命名的各種 Key 值, 再配以適當註釋, 任何組件開發人員不需要事先溝通只要依賴了這個協議, 就知道了各自該怎樣協同工作, 既提高了效率又降低了出錯風險, 約定的東西自然要比口頭上說強Tips: 如果您覺得把每個路由地址都寫在基礎庫的 RouterHub 中, 太麻煩了, 也可以在每個組件內部建立一個私有 RouterHub, 將不需要跨組件的路由地址放入私有 RouterHub 中管理, 只將需要跨組件的路由地址放入基礎庫的公有 RouterHub 中管理, 如果您不需要集中管理所有路由地址的話, 這也是比較推薦的一種方式。

7.4.ARouter路由原理:

ARouter維護了一個路由表Warehouse,其中保存著全部的模塊跳轉關係,ARouter路由跳轉實際上還是調用了startActivity的跳轉,使用了原生的Framework機制,只是通過apt註解的形式製造出跳轉規則,並人為地攔截跳轉和設置跳轉條件

常見的組件化方案如下



Android中級面試題目彙總解答到此就結束了想獲取更多Android方面的技術知識或者面試資料的免費領取方式:轉發後關注我後臺私信關鍵詞【資料】獲取!