2019 初級Android開發工程師面經

2019 初級Android開發工程師面經

1. Java基礎

1.1 什麼是樂觀鎖?

  • 樂觀鎖:假設每次去拿數據都認為別人不會修改,所以不會上鎖.但是在更新的時候會判斷一下此期間別人有沒有去更新這個數據. 一般用在讀比較多,寫比較少的情況.
  • 悲觀鎖:假設每次都是最壞情況,每次去拿數據時別人都會修改,所以每次拿數據的時候都會上鎖,這樣別人想拿這個數據就會被阻塞直到它拿到鎖. 多寫少讀時使用.

1.2 volatile關鍵字

  1. 保證可見性,不保證原子性
  2. 禁止指令重排序
  3. 不緩存,每次都是從主存中取

1.3 hashmap 原理,紅黑樹是什麼?

  • 1.7 數組+鏈表,鏈表過長時,會導致查詢效率退化
  • 1.8 數組+鏈表+紅黑樹,當鏈表長度大於8轉為紅黑樹
  • HashMap 的默認初始大小為 16,初始化大小必須為 2 的冪,最大大小為 2 的 30 次方。數組中存儲的鏈表節點 Entry 類實現於 Map.Entry 接口,它實現了對節點的通用操作。HashMap 的閾值默認為 “容量 * 0.75f”,當存儲節點數量超過該值,則對 map 進行擴容處理。
  • 線程不安全的容器,解決併發問題使用ConcurrentHashMap(高效)或者是Collections.synchronizedMap().Collections.synchronizedMap()其實就是每個方法加一個synchronize,其實和HashTable 差不多.

紅黑樹

  • 平衡的二叉查找樹
  • 節點是紅色或者是黑色
  • 根節點是黑色
  • 每個葉子的節點都是黑色的空節點(NULL)
  • 每個紅色節點的兩個子節點都是黑色的
  • 從任意節點到其每個葉子的所有路徑都包含相同的黑色節點
  • 插入時會涉及到變色和旋轉

1.4 jvm內存分配

Java虛擬機書中第二章

  • 程序計數器
  • Java虛擬機棧
  • 本地方法棧
  • Java堆
  • 方法區
  • 運行時常量池
  • 直接內存

1.5 String,StringBuffer,StringBuilder 區別

  • String,StringBuffer,StringBuilder最終底層存儲與操作的都是char數組.但是String裡面的char數組是final的,而StringBuffer,StringBuilder不是,也就是說,String是不可變的,想要新的字符串只能重新生成String.而StringBuffer和StringBuilder只需要修改底層的char數組就行.相對來說,開銷要小很多.
  • String的大多數方法都是重新new一個新String對象返回,頻繁重新生成容易生成很多垃圾.
  • StringBuffer是線程安全的,StringBuilder是線程不安全的.因為StringBuffer的方法是加了synchronized鎖起來了的,而StringBuilder沒有.
  • 增刪比較多時用StringBuffer或StringBuilder(注意單線程與多線程)。實際情況按需而取吧,既然已經知道了裡面的原理。
2019 初級Android開發工程師面經

2. 安卓基礎

2.1 安卓各版本大變化(Android 6.0到10.0有哪些大點變化),兼容適配

Android 5.0

  • Material Design
  • ART虛擬機

Android 6.0

  • 應用權限管理
  • 官方指紋支持
  • Doze電量管理
  • 運行時權限機制->需要動態申請權限

Android 7.0

  • 多窗口模式
  • 支持Java 8語言平臺
  • 需要使用FileProvider訪問照片
  • 安裝apk需要兼容

Android 8.0

  • 通知,渠道
  • 畫中畫
  • 自動填充
  • 後臺限制
  • 自適應桌面圖標
  • 隱式廣播限制
  • 開啟後臺Service限制

Android 9.0

  • 利用 Wi-Fi RTT 進行室內定位
  • 劉海屏 API 支持
  • 多攝像頭支持和攝像頭更新
  • 不允許調用hide api
  • 限制明文流量的網絡請求 http

Android 10

  • 暗黑模式
  • 隱私增強(後臺能否訪問定位)
  • 限制程序訪問剪貼板
  • 應用黑盒
  • 權限細分需兼容
  • 後臺定位單獨權限需兼容
  • 設備唯一標示符需兼容
  • 後臺打開Activity 需兼容
  • 非 SDK 接口限制 需兼容

2.2 熱修復原理

原理

  1. 安卓在加載class時會通過雙親委託機制去加載一個類,先讓父類去加載,如果找不到再讓子類去加載某個類.
  2. 通過查看ClassLoader源碼發現findClass方法是由每個子類自己實現的,比如BootClassLoader或者BaseDexClassLoader.而PathClassLoader是繼承自BaseDexClassLoader的,它的findClass也是在BaseDexClassLoader裡面實現的.
  3. BaseDexClassLoader的findClass裡面使用了另一個對象DexPathList去查找對應的class,這是安卓裡面特有的實現.在DexPathList對象裡面有一個屬性dexElements,dexElements是用於存放加載好了的dex數組的,查找class是從這個dexElements數組裡面去找的.
  4. dexElements裡面存放的是Element對象,findClass最終會交給Element去實現,Element又會交給Element裡面的一個屬性DexFile去實現.我看了下,最終是用native實現的.
  5. 回到上面的第3步中的DexPathList對象從dexElements數組裡面查找class,從數組的前面往後找,找到了就返回結果,不再繼續查找.
  6. 所以當我們把修復好bug了的class,搞成dex,然後通過反射等技術放到dexElements的最前面,這樣系統在通過PathClassLoader找到class時,就能先找到我們放置的修復好bug的class,然後就不會再往後找了,相當於實現了熱修復.這樣有bug的class就不會被用了.應了一句古話,近水樓臺先得月.
  7. 第6點中的反射,流程是:獲取到PathClassLoader,然後反射獲取到父類中的DexPathList對象,然後再反射到DexPathList對象中的dexElements數組.然後將補丁(dex)轉為Element對象,插入到dexElements數組的前面(先複製出來,再合併,再通過反射放回去).
2019 初級Android開發工程師面經

一句話總結

將修復好的類放在dexElements的最前面,這樣在加載類的時候就會被優先加載到而達到修復的目的.

2.3 MVC,MVP,MVVM

首先需要知道的是為什麼要進行技術框架的設計? 肯定是為了低耦合,提高開發效率是吧.所以不要為了設計而設計.

MVC

在Android中View和Controller一般就是被Activity充當了,當邏輯非常多,操作非常複雜時,Activity代碼量非常龐大,不易維護.

  • Model : 模型層,業務邏輯+數據存儲等
  • View : 用戶界面,一般就是xml+Activity
  • Controller : 控制層,一般就是Activity

MVP

我個人角度,現在(2019年10月29日20:02:49)大多是使用這種方式,既不復雜也解耦合了.

  • Model:模型層,業務邏輯+數據存儲+網絡請求
  • View:視圖層,View繪製和用戶交互等,一般是Activity
  • Presenter:呈現層,連接V層和M層,完成他們之間的交互
2019 初級Android開發工程師面經

MVVM

為了更加分離M,V層,所以有了MVVM.

  • Model:模型層,業務邏輯+數據存儲+網絡請求
  • View:視圖層,View繪製和用戶交互等,一般是Activity
  • ViewModel:其實就是Presenter和View的數據模型的合體.雙向綁定,View的變動會反應到ViewModel中,數據的變動也會反應到View上.

2.4 組件化的好處

  1. 任意修改都需要編譯整個工程,效率低下。
  2. 解耦,有利於多人團隊協作開發
  3. 功能複用

2.5 app啟動流程

  1. Launcher startActivity
  2. AMS startActivity
  3. Zygote fork進程
  4. Activity main()
  5. ActivityThread 進程loop循環
  6. 開啟Activity,開始生命週期回調...

2.6 Activity啟動流程

  1. Activity startActivityForResult
  2. Instrumentation execStartActivity
  3. AMS startActivity
  4. ApplicationThread scheduleLaunchActivity
  5. ActivityThread.H handleMessage -> performLaunchActivity
  6. Activity attach
  7. Instrumentation callActivityOnCreate
2019 初級Android開發工程師面經

2.7 app體積優化

  • 可以使用lint工具,檢測出沒有用的文件.同時可以開啟資源壓縮,自動刪除無用的資源.
  • 儘量多使用可繪製對象,某些圖像不需要靜態圖像資源,框架可以在運行時動態繪製圖像.儘量自己寫Drawable,能不用UI切圖就不用,佔用空間小.
  • 重用資源. 比如一個三角按鈕,點擊前三角朝上代表收起的意思,點擊後三角朝下,代表展開,一般情況下,我們會用兩張圖來切換,我們其實完全可以用旋轉的形式去改變.
  • 比如同一圖像的著色不同,我們可以用android:tint和tintMode屬性,低版本可以使用ColorFilter
  • 壓縮PNG和JPEG文件.可以減少PNG文件的大小,而不會丟失圖像質量.
  • 使用WebP文件格式,可以使用WebP文件格式,而不是使用PNG或JPEG文件. 可以使用AS將現有的BMP,JPG,PNG或靜態GIF圖像轉換成WebP格式.
  • 使用矢量圖形. svg
  • 代碼混淆. 使用proGuard代碼混淆器工具,它包括壓縮,優化,混淆等功能.這個大家太熟悉.
  • 插件化. 將功能模塊放服務器上,按需下載,可以減少安裝包大小.

2.8 app啟動優化

  • 利用提前展示出來的Window,快速展示出來一個節目,給用戶快速反饋的體驗.障眼法,治標不治本.
  • 避免在啟動時做密集沉重的初始化(Heavy app initialization).某些SDK初始化放在異步去加載(比如友盟,bugly這樣的業務非必要可以異步加載),比如地圖,推送等,非第一時間需要的可以在主線程做延時啟動(比如閃屏頁),當程序已經啟動起來之後,再進行初始化. 對於網絡,圖片請求框架就必須在主線程中初始化了.
  • 啟動時 避免I/O操作,反序列化,網絡操作,佈局嵌套等耗時操作

2.9 app佈局優化

  • 如果父控件有顏色,也是自己需要的顏色,那麼就不必在子控件加背景顏色
  • 如果子控件有背景顏色,並且能完全覆蓋父控件,那麼父控件不用設置背景顏色
  • 儘量減少不必要的嵌套
  • 能用LinearLayout和FrameLayout,就不要用RelativeLayout,因為RelativeLayout相對比較複雜,測繪也相對耗時.
  • include和merge一起使用,增加複用,減少層級
  • ViewStub按需加載,更加輕便
  • 複雜界面選擇ConstraintLayout,可有效減少層級

2.10 app內存優化

  • 頻繁使用字符串拼接用StringBuilder或者StringBuffer
  • ArrayMap、SparseArray替換HashMap
  • 避免內存洩漏集合類洩漏(集合一直引用著被添加進來的元素對象)單例/靜態變量造成的內存洩漏(生命週期長的持有了生命週期短的引用)匿名內部類/非靜態內部類資源未關閉造成的內存洩漏
  • 檢測內存洩漏的幾個工具: LeakCanary,TraceView,Systrace,Android Lint,Memory Monitor+mat

2.11 內存洩漏有哪些

  • 集合類洩漏(集合一直引用著被添加進來的元素對象)
  • 單例/靜態變量造成的內存洩漏(生命週期長的持有了生命週期短的引用)
  • 匿名內部類/非靜態內部類
  • 資源未關閉造成的內存洩漏網絡,文件等流忘記關閉手動註冊廣播時,退出時忘記unregisterReceiver()Service執行完成後忘記stopSelf()EventBus等觀察者模式的框架忘記手動解除註冊
2019 初級Android開發工程師面經

2.12 app線程優化

線程池 避免存在大量的Thread,重用線程池內部的線程,從而避免了線程的創建和銷燬帶來的性能開銷,同時能有效控制線程池的最大併發數,避免大量線程因互相搶佔系統資源而導致阻塞線現象發生. 推薦閱讀 《Android開發藝術探索》 第11章.

分類

  • FixedThreadPool 數量固定的線程池
  • CachedThreadPool 只有非核心線程,數量不定,空閒線程有超時機制,比較適合執行大量耗時較少的任務
  • ScheduledThreadPool 核心線程數量固定,非核心線程沒有限制.主要用於執行定時任務和具有固定中週期的重複任務.
  • SingleThreadPool 只有一個核心線程,確保所有的任務在同一個線程順序執行,統一外界任務到一個線程中,這使得在這些任務之間不需要處理線程同步 的問題. 優點
  • 減少在創建和銷燬線程上所花的時間以及系統資源的開銷
  • 不使用線程池有可能造成系統創建大量的線程而導致消耗完系統內存以及"過度切換" 注意點
  1. 如果線程池中的數量未達到核心線程的數量,則直接啟動一個核心線程來執行任務
  2. 如果線程池中的數量已經達到或超過核心線程的數量,則任何會被插入到任務隊列中等待執行
  3. 如果2中的任務無法插入到任務隊列中,由於任務隊列已滿,這時候如果線程數量未達到線程池規定的最大值,則會啟動一個非核心線程來執行任務
  4. 如果3中的線程數量已經達到線程池最大值,則會拒絕執行此任務,ThreadPoolExecutor會調用RejectedExecutionHandler的rejectedExecution()方法通知調用者

2.13 Android換膚如何實現,原理

重新設置LayoutInflater的Factory2,從而攔截創建View的過程,然後搞成自己的控件,想怎麼換膚就怎麼換膚.

2.14 fresco原理,glide原理,兩者區別,哪個更省內存

這塊暫時不懂,加入todo

2.15 Handler原理,Android 消息機制

裡面介紹得很詳細,順便說了一下為什麼loop不會阻塞主線程問題.

Handler機制的關鍵在於對於ThreadLocal原理的理解,線程私有數據.利用ThreadLocal機制將Looper存放到線程內部,perfect !

2.16 Android 系統架構

2019 初級Android開發工程師面經

應用層,應用框架層,系統運行庫層,硬件抽象層和Linux內核層

2.17 常用佈局有哪些

  • FrameLayout,LinearLayout,RelativeLayout,ConstraintLayout,CoordinatorLayout等

2.18 Android數據存儲有幾種方式

  • SharedPreferences: 小東西,最終是xml文件中,key-value的形式存儲的.
  • 文件
  • 數據庫
  • ContentProvider
  • 網絡

2.19 View,SurfaceView

  • View是Android中所有控件的基類
  • View適用於主動更新的情況,而SurfaceView則適用於被動更新的情況,比如頻繁刷新界面。
  • View在主線程中對頁面進行刷新,而SurfaceView則開啟一個子線程來對頁面進行刷新。
  • View在繪圖時沒有實現雙緩衝機制,SurfaceView在底層機制中就實現了雙緩衝機制。

2.20 組件之間相互引用 如何解決

  • 調用其他組件的對外提供的方法:之前看到過一種思路,利用"接口+實現"的方式,定義一個ComponentBase 中間層,然後裡面有每個組件對外提供方法調用的Interface,每個組件在初始化的時候就把這些Interface給實現了,然後其他組件需要用的時候就從ComponentBase裡面取.
  • 界面跳轉:ARouter
2019 初級Android開發工程師面經

2.21 自定義View 餅狀圖,點擊事件,畫文字

這個大家可以跟著hencoder老師的文章系統學習一下.

2.22 Android 數字簽名

校驗用戶身份,校驗數據的完整性

2.23 fragment用在哪裡,與Activity的區別

  • 當Activity需要模塊化的時候
  • 不同設備上的適配,比如平臺和手機
  • Activity相對Fragment而言,非常笨重,一般小界面小模塊用Fragment比較合適.或者首頁的tab之類的.

今年年初我花一個月的時間收錄整理了一套知識體系,如果有想法深入的系統化的去學習的,可以私信我【安卓】,我會把我收錄整理的資料都送給大家,幫助大家更快的進階。

重要的事說三遍,轉發+轉發+轉發,讓更多需要的朋友們都可以看到並且領到!

2019 初級Android開發工程師面經


分享到:


相關文章: