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 差很少.

紅黑樹android

  • 平衡的二叉查找樹
  • 節點是紅色或者是黑色
  • 根節點是黑色
  • 每一個葉子的節點都是黑色的空節點(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(注意單線程與多線程)。實際狀況按需而取吧,既然已經知道了裏面的原理。

2. 安卓基礎

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

Android 5.0數據庫

  • Material Design
  • ART虛擬機

Android 6.0api

  • 應用權限管理
  • 官方指紋支持
  • 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數組的前面(先複製出來,再合併,再經過反射放回去).

一句話總結多線程

將修復好的類放在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層,完成他們之間的交互

MVVM

爲了更加分離M,V層,因此有了MVVM.

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

推薦閱讀:

Android開發四年面試相關知識整理

安卓虛擬定位實現與反做弊思路

Android應屆畢業生「過五關斬六將」,怒刷千題,讓你面試一路暢通!

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

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等觀察者模式的框架忘記手動解除註冊

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 系統架構

應用層,應用框架層,系統運行庫層,硬件抽象層和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

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

這個你們能夠跟着hencoder老師的文章系統學習一下.

2.22 Android 數字簽名

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

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

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

今年年初我花一個月的時間收錄整理了一套知識體系,若是有想法深刻的系統化的去學習的,能夠點擊傳送門,我會把我收錄整理的資料都送給你們,幫助你們更快的進階。

相關文章
相關標籤/搜索