Android面試 - Android部分

最近準備面試,整理了一些面試問題

關於Fragment問題

1、onPause onResume方法不執行問題

FragmentViewPager 搭配使用的時候切換Fragment時,顯示一個Fragment或者隱藏一個Fragment時,他們的onPauseonResume 並不會執行,viewpager只會保留三個頁面,切換的時候只有裝不下三個的時候, 就會捨棄最早的那個Fragement 執行,onPause方法; 所以不能通過onResume onPause 判斷Fragment的隱藏和顯示。
通過閱讀ViewPagerPageAdapter相關的代碼,切換Fragment實際上就是通過設置setUserVisibleHintsetMenuVisibility來實現的,當fragment進入視線時,他會調用這個方法,這個參數爲true,當退出視線時,調用這個方法,參數爲false;

2、fragment 棧管理問題

okhttp源碼

Dispatcher 任務調度器,裏面有三個隊列,將要執行的異步隊列 正在執行的異步隊列, 正在執行的同步隊列 和一個線程池
當正在正在執行隊列的數量小於64 且請求的主機數小於5 的時候, add到正在執行的隊列,否則add到將要執行的隊列中 。 每次add 實際是一個 RealCall實例

RealCall 實際的執行者 它有execute方法 ,調用一個 getReaponseWithInterceptorChain獲取響應消息和攔截器鏈 方法
得到響應結果後,最後還會Dispatcher的finish方法,這裏還會xxx call方法,重新add一個將要執行的任務到 正在執行的隊列中

Android事件傳遞

Android事件傳遞的入口,首先從屏幕把物理信號經過系統一系列的轉換,最後到Activity 的 dispatchTouchEvent 方法,傳遞到ViewGroup 的dispatchTouchEvent 和 onInterceptTouchEvent 然後傳到View dispatchTouchEvent 然後調用 View onTouchEvent 向上調用。

如果dispatchTouchEvent 或者 onTouchEvent 返回true ,代表事件被該層View 消耗,那麼事件就會走到這個方法後就會被終止。
如果ViewGroup的onInterceptTouchEvent 返回true ,代表事件在該層被攔截,那麼事件就不會在向下傳遞,他會向上走onTouchEvent

Rxjava原理

Rxjava一共有五種被觀察者,每一個被觀察者調用一個操作符之後,它就會把創建一個新的被觀察者,然後把上一個被觀察者傳給這個被觀察者的source接受,直到遇到subscribe 他會創建一個觀察者對象,然後向上每調用一個操作符,就會包裝一下新的Observer對象,繼續訂閱上一個操作符對應的Observer對象 ,直到遇到數據源,然後數據依次調用onSuccess 方法。
最後調用最底下我們自己創建的那個 Observer 的 onNext方法。

註解處理器 AbstractProcessor

第一個是註解,就是給代碼打上標記,然後AbstractProcessor去找到這些標記,通過註解的類型 編寫不同的邏輯, 其次是元註解,就是作用到註解上的註解
第二個就是 Eelement 主要有三個 TypeElement ExcutableElement VariableEelemt 這三個類就是,前面說到的,註解作用的位置,最後會轉嫁到這三個Element上面, 通過這些Element 我們可以得到我們註解標註的方法名 字段 類名 以及 方法參數 類型等信息, 得到這些信息,我們就可以通過AbstractProcessor 編寫代碼。

RecyclerView相關

RecyclerView的四級緩存分析

Recyclerview的緩存類
RecycleView的四級緩存是由三個類共同作用完成的,Recycler、RecycledViewPool和ViewCacheExtension。

Recycler
用於管理已經廢棄或者與RecyclerView分離的ViewHolder,這裏面有兩個重要的成員

屏幕內緩存: 屏幕內緩存指在屏幕中顯示的ViewHolder,
這些ViewHolder會緩存在mAttachedScrap、mChangedScrap中
mChangedScrap: 表示數據已經改變的viewHolder列表
mAttachedScrap: 表示未與RecyclerView分離的ViewHolder列表

屏幕外緩存 當列表滑動出了屏幕時,ViewHolder會被緩存在 mCachedViews ,其大小由mViewCacheMax決定,默認DEFAULT_CACHE_SIZE爲2,
可通過Recyclerview.setItemViewCacheSize()動態設置。

RecycledViewPool
RecycledViewPool類是用來緩存ViewHolder用,如果多個RecyclerView之間用setRecycledViewPool(RecycledViewPool)設置同一個RecycledViewPool,他們就可以共享ViewHolder。

ViewCacheExtension
開發者可自定義的一層緩存,是虛擬類ViewCacheExtension的一個實例,開發者可實現方法getViewForPositionAndType(Recycler recycler, int position, int type)來實現自己的緩存。

四級緩存
//1.一級緩存:mAttachedScrap 一級緩存中用來存儲屏幕中顯示的ViewHolde
final ArrayList mAttachedScrap = new ArrayList<>();
//2.二級緩存:mCacheViews 用來存儲屏幕外的緩存
final ArrayList mCachedViews = new ArrayList();
//3.三級緩存:mViewCacheExtension 根據coder自己定義的緩存規則
private ViewCacheExtension mViewCacheExtension;
//4.四級緩存:mRecyclerPool 當屏幕外緩存的大小大於2,便放入mRecyclerPool中緩存。
RecycledViewPool mRecyclerPool;

四級緩存什麼時候執行的?
1.第一級緩存執行流程
RecyclerView.scrollBy()–>RecyclerView.scrollByInternal()–>RecyclerView.resumeRequestLayout()–>RecyclerView.dispatchLayout()–>RecyclerView.dispatchLayoutStep1()–>LayoutManager.onLayoutChildren()–>LayoutManager.scrapOrRecycleView()–>Recycler.scrapView()

2.第二級緩存執行流程 執行條件 第一級緩存不要的纔給第二級緩存
Recycler.recycleViewHolderInternal()

3.第四級緩存 第二級緩存滿了 纔給第四級緩存

RecyclerView itemView是怎樣佈局的?
LinearLayoutManager.onLayoutChildren()–>LinearLayoutManager.fill()–>LinearLayoutManager.layoutChunk()–>LayoutState.next()–>Recycler.getViewForPosition()–>Recycler.getViewForPosition()–>Recycler.tryGetViewHolderForPositionByDeadline()

你在開發中是如何解決內存問題

使用AS Profiler 需要點擊GC垃圾桶回收,然後選取一段內存,點擊 Allocation Tracing 分析,選擇app heap 和package 的形式進行過濾。 可以看到內存中當前存在的一些堆的實例, 檢查byte數組, String對象以及Activity引用等

leakCanaery 使用方便,自動提示並告訴位置

如何規避oom?

  • 使用更加輕量的數據結構
    我們可以考慮使用ArrayMap/SparseArray而不是HashMap等傳統數據結構,相比起Android系統專門爲移動操作系統編寫的ArrayMap容器,在大多數情況下,都顯示效率低下,更佔內存。通常的HashMap的實現方式更加消耗內存,因爲它需要一個額外的實例對象來記錄Mapping操作。另外,SparseArray更加高效在於他們避免了對key與value的autobox自動裝箱,並且避免了裝箱後的解箱。

  • 避免在Android裏面使用Enum

  • 減小Bitmap對象的內存佔用
    Bitmap是一個極容易消耗內存的大胖子,減小創建出來的Bitmap的內存佔用是很重要的,通常來說有下面2個措施:
    inSampleSize:縮放比例,在把圖片載入內存之前,我們需要先計算出一個合適的縮放比例,避免不必要的大圖載入。
    decode format:解碼格式,選擇ARGB_8888/RBG_565/ARGB_4444/ALPHA_8,存在很大差異。

  • 用更小的圖片
    在設計給到資源圖片的時候,我們需要特別留意這張圖片是否存在可以壓縮的空間,是否可以使用一張更小的圖片。儘量使用更小的圖片不僅僅可以減少內存的使用,還可以避免出現大量的InflationException。假設有一張很大的圖片被XML文件直接引用,很有可能在初始化視圖的時候就會因爲內存不足而發生InflationException,這個問題的根本原因其實是發生了OOM。

  • 複用系統自帶的資源
    Android系統本身內置了很多的資源,例如字符串/顏色/圖片/動畫/樣式以及簡單佈局等等,這些資源都可以在應用程序中直接引用。這樣做不僅僅可以減少應用程序的自身負重,減小APK的大小,另外還可以一定程度上減少內存的開銷,複用性更好。但是也有必要留意Android系統的版本差異性,對那些不同系統版本上表現存在很大差異,不符合需求的情況,還是需要應用程序自身內置進去。

  • 注意在ListView/GridView等出現大量重複子組件的圖裏面對ConvertView的複用

  • Bitmap對象的複用
    在ListView與GridView等顯示大量圖片的控件裏面需要使用LRU的機制來緩存處理好的Bitmap。
    利用inBitmap的高級特性提高Android系統在Bitmap分配與釋放執行效率上的提升(3.0以及4.4以後存在一些使用限制上的差異)。
    使用inBitmap屬性可以告知Bitmap解碼器去嘗試使用已經存在的內存區域,新解碼的bitmap會嘗試去使用之前那張bitmap在heap中所佔據的pixel data內存區域,
    而不是去問內存重新申請一塊區域來存放bitmap。利用這種特性,即使是上千張的圖片,也只會僅僅只需要佔用屏幕所能夠顯示的圖片數量的內存大小。

  • 避免在onDraw方法裏面執行對象的創建
    類似onDraw等頻繁調用的方法,一定需要注意避免在這裏做創建對象的操作,因爲他會迅速增加內存的使用,而且很容易引起頻繁的gc,甚至是內存抖動。

  • 避免對象的內存泄露(重點)

  • 考慮使用Application Context而不是Activity Context
    通常來說,Activity的泄漏是內存泄漏裏面最嚴重的問題,它佔用的內存多,影響面廣,我們需要特別注意以下兩種情況導致的Activity泄漏:
    內部類引用導致Activity的泄漏
    最典型的場景是Handler導致的Activity泄漏,如果Handler中有延遲的任務或者是等待執行的任務隊列過長,都有可能因爲Handler繼續執行而導致Activity發生泄漏。此時的引用關係鏈是Looper -> MessageQueue -> Message -> Handler -> Activity。爲了解決這個問題,可以在UI退出之前,執行remove Handler消息隊列中的消息與runnable對象。或者是使用Static + WeakReference的方式來達到斷開Handler與Activity之間存在引用關係的目的。
    Activity Context被傳遞到其他實例中,這可能導致自身被引用而發生泄漏。
    內部類引起的泄漏不僅僅會發生在Activity上,其他任何內部類出現的地方,都需要特別留意!我們可以考慮儘量使用static類型的內部類,同時使用WeakReference的機制來避免因爲互相引用而出現的泄露。
    對於大部分非必須使用Activity Context的情況(Dialog的Context就必須是Activity Context),我們都可以考慮使用Application Context而不是Activity的Context,這樣可以避免不經意的Activity泄露。

  • 注意WebView的泄漏(重點)
    Android中的WebView存在很大的兼容性問題,不僅僅是Android系統版本的不同對WebView產生很大的差異,另外不同的廠商出貨的ROM裏面WebView也存在着很大的差異。更嚴重的是標準的WebView存在內存泄露的問題,看這裏WebView causes memory leak - leaks the parent Activity。所以通常根治這個問題的辦法是爲WebView開啓另外一個進程,通過AIDL與主進程進行通信,WebView所在的進程可以根據業務的需要選擇合適的時機進行銷燬,從而達到內存的完整釋放。

  • 資源文件需要選擇合適的文件夾進行存放

  • 謹慎使用static對象(重點)
    因爲static的生命週期過長,和應用的進程保持一致,使用不當很可能導致對象泄漏,在Android中應該謹慎使用static對象。

  • 特別留意單例對象中不合理的持有
    雖然單例模式簡單實用,提供了很多便利性,但是因爲單例的生命週期和應用保持一致,使用不合理很容易出現持有對象的泄漏。

  • 主線程操作UI,子線程操作數據(必填)

  • 優化佈局層次,減少內存消耗
    越扁平化的視圖佈局,佔用的內存就越少,效率越高。我們需要儘量保證佈局足夠扁平化,當使用系統提供的View無法實現足夠扁平的時候考慮使用自定義View來達到目的。

Android有時候用起來爲什麼會卡頓,卡頓的原因是什麼,我們該怎麼去優化?

面試官想知道的是你對Android UI繪製流程的瞭解怎麼樣,在你自定義UI的時候是如何避免卡頓的。
那造成UI卡頓的無非是兩個主要原因,第一個是UI太複雜,嵌套太多導致的,第二個是我們自定義的UI或者控件本身存在問題。
那所以我們可以從這兩點出發去分析:
那這個時候我們站在繪製流程的角度去講,會好很多

卡頓原因:
一. 佈局Layout過於複雜,佈局無法在16ms內完成渲染。
那這個時候我們可以從佈局無法在16ms內完成渲染這個點去進行分析,卡頓的原因就是無法在規定的時候內完成渲染,那我們應該怎麼去說呢?
比如說:我們佈局渲染的一幀它是需要在16MS內完成的,假設16ms沒有完成,那它會發生什麼樣的情況呢,那你可以告訴面試官。
假設現在有個佈局,渲染花了20ms,它沒有在16ms中完成渲染,那這個時候會出現什麼問題呢。在UI繪製中有一個概念叫做vysnc(垂直同步信號量),
我們UI之所以能進行渲染,全是由它來通知的,vysnc它是用來同步渲染,讓AppUI和SurfaceFlinger可以按硬件產生的VSync節奏進行工作。那我們剛剛
說到,繪製一幀,系統規定的時間是16ms,因爲Android系統每隔16ms發出VSYNC信號通知渲染下一幀。現在我們遇到的情況是畫了20ms才渲染完畢,
那這個時候當VSync通知畫第二幀的時候,你第一幀還沒有畫完,
那就會繼續延遲16ms,也就是說原本16ms完成的事情,現在畫了32ms。那可能有很多同學會問,爲什麼是16ms,那是因爲,16ms意味着1000/60hz,相當於60fps。這是因爲人眼與大腦之間的協作無法感知超過60fps的畫面更新,
16ms的渲染時間的話,人眼是無法察覺到的,就好比顯示器,它也有一個幀率的概念,幀率越高,顯示器越高級,看起來越舒服,或者玩遊戲,當我們FPS很低的時候,
我們人眼就會感覺到像是在一幀一幀跳,這就是因爲渲染的速度能被我們人眼所察覺。但現在我們如果32ms完成一幀的話,那1S就只有30多幀,所以我就能察覺到卡頓。
那這個問題我們說清楚了,解決方案是什麼呢?

  1. 減少UI嵌套。
  2. 儘量少調用requestLayout()。
  3. 避免屏幕多次measure,layout,onDraw。
  4. 注意內存抖動問題。
  5. 避免在類似onDraw這樣的方法中創建對象,因爲它會迅速佔用大量內存,引起頻繁的GC甚至內存抖動

二.View頻繁的觸發measure,layout,onDraw導致measure,layout累計耗時過多及導致整個View頻繁的重新渲染。
6. 儘量少調用requestLayout()。
7. 避免屏幕多次measure,layout,onDraw。
8. 避免在類似onDraw這樣的方法中創建對象,因爲它會迅速佔用大量內存,引起頻繁的GC甚至內存抖動
9. 爲什麼要少調用requestLayout() 因爲requestLayout它會重新測量佈局和繪製,但是有時候在我們實際的開發中是不需要重新測繪量佈局只需要重新繪製就可以,
所以這個時候我們就不要去調用requestLayout()而是invalidate()

View的繪製流程

自定義控件: 1、組合控件。這種自定義控件不需要我們自己繪製,而是使用原生控件組合成的新控件。如標題欄。 2、繼承原有的控件。這種自定義控件在原生控件提供的方法外,可以自己添加一些方法。如製作圓角,圓形圖片。 3、完全自定義控件:這個View上所展現的內容全部都是我們自己繪製出來的。比如說製作水波紋進度條。

View的繪製流程:OnMeasure()——>OnLayout()——>OnDraw()

第一步:OnMeasure():測量視圖大小。從頂層父View到子View遞歸調用measure方法,measure方法又回調OnMeasure。

第二步:OnLayout():確定View位置,進行頁面佈局。從頂層父View向子View的遞歸調用view.layout方法的過程,即父View根據上一步measure子View所得到的佈局大小和佈局參數,將子View放在合適的位置上。

第三步:OnDraw():繪製視圖。ViewRoot創建一個Canvas對象,然後調用OnDraw()。六個步驟:①、繪製視圖的背景;②、保存畫布的圖層(Layer);③、繪製View的內容;④、繪製View子視圖,如果沒有就不用; ⑤、還原圖層(Layer);⑥、繪製滾動條。

MVC、MVP、MVVM 與組件化

MVC 就是Model 、View 、Controller 其中Model 是徹底解耦,但是View層和Controler 是相互之間聯繫比較緊密。Activity裏面編寫大量的邏輯代碼和UI更新,職責不分明。

MVP 是Model、View、Presenter ,其中Model獲取數據、Presenter負責業務邏輯、View負責UI渲染, V 層通過P層調用M層獲取到數據,然後返回給P層,通知給V渲染。 相比MVC 它做到了P和V層的徹底分離。
優點:層次分明、便於測試
缺點:View和Model之間大量的通過P層來控制,並且全部通過接口抽離,代碼量較大,並且P層控制邏輯臃腫。

MVVM: 是Model 、View、 ViewModel ,View負責UI顯示、Model負責數據獲取,ViewModel負責控制邏輯。 它大體跟MVP相似,ViewModel和View之間的交互通過DataBinding進行雙向綁定的,只要想改其中一方,另一方都能夠及時更新,這樣省去了MVP M層與V層之間大量的接口調用。
優點:繼承了MVP的優點,另外數據UI雙向綁定。結合jectpack使用更加方便。

組件化: 通俗理解就是將一個工程分成各個模塊,各個模塊之間相互解耦,可以獨立開發並編譯成一個獨立的 APP 進行調試,然後又可以將各個模塊組合起來整體構成一個完整的 APP。

jectpack: 它是Google新推出來一個框架集合,框架組件可以單獨使用,也可以配合使用。方便管理activity 如處理後臺任務、生命週期的管理。

  • DataBinding:以聲明方式將可觀察數據綁定到界面元素,通常和ViewModel配合使用。
  • Lifecycle:用於管理Activity和Fragment的生命週期,可幫助開發者生成更易於維護的輕量級代碼。
  • LiveData: 在底層數據庫更改時通知視圖。它是一個可觀察的數據持有者,與常規observable不同,LiveData是生命週期感知的。
  • Navigation:處理應用內導航。
    Paging:可以幫助開發者一次加載和顯示小塊數據,按需加載部分數據可減少網絡帶寬和系統資源的使用。
  • Room:友好、流暢的訪問SQLite數據庫。它在SQLite的基礎上提供了一個抽象層,允許更強大的數據庫訪問。
  • ViewModel: 以生命週期的方式管理界面相關的數據,通常和DataBinding配合使用,爲開發者實現MVVM架構提供了強有力的支持。
  • WorkManager: 管理Android的後臺的作業,即使應用程序退出或設備重新啓動也可以運行可延遲的異步任務。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章