Android面試復(fù)習(xí)筆記 4

5. 優(yōu)化問題

1. ANR

Application Not Responding即應(yīng)用無響應(yīng)。
通常主線程中進行耗時操作就會引起。

應(yīng)用啟動時,系統(tǒng)會為應(yīng)用創(chuàng)建一個名為“主線程”的執(zhí)行線程。 此線程非常重要,因為它負責(zé)將事件分派給相應(yīng)的用戶界面小部件,其中包括繪圖事件。 此外,它也是應(yīng)用與 Android UI 工具包組件(來自 android.widget 和 android.view 軟件包的組件)進行交互的線程。因此,主線程有時也稱為 UI 線程。

系統(tǒng)不會為每個組件實例創(chuàng)建單獨的線程。運行于同一進程的所有組件均在 UI 線程中實例化,并且對每個組件的系統(tǒng)調(diào)用均由該線程進行分派。 因此,響應(yīng)系統(tǒng)回調(diào)的方法(例如,報告用戶操作的 onKeyDown() 或生命周期回調(diào)方法)始終在進程的 UI 線程中運行。如果 UI 線程用來需要處理耗時很長的操作(例如,網(wǎng)絡(luò)訪問或數(shù)據(jù)庫查詢)將會阻塞整個 UI,被阻塞超過5秒鐘時間,就會引發(fā)“應(yīng)用無響應(yīng)”(ANR)。

1.1 主要分三種情況

A)KeyDispatchTimeout
頁面按鍵無響應(yīng)超時,這個Key事件分發(fā)超時的時間,Android默認是5秒,主要是定義在ActivityTaskManagerService.java
KEY_DISPATCHING_TIMEOUT_MS = 5*1000;

B)BroadcastTimeOut
廣播的超時時間,分為FG前臺廣播和BG后臺廣播,分別是10秒和60秒(這里是指單個廣播接收器可以處理的最大的時間)。定義在ActivityManagerService.java
BROADCAST_FG_TIMEOUT = 101000;
BROADCAST_BG_TIMEOUT = 60
1000;

前臺廣播和后臺廣播都有各自對應(yīng)的廣播隊列,主要區(qū)別就是隊列最大處理時間不同,前者10S后者60S。
發(fā)送時系統(tǒng)默認是后臺廣播。
若要修改為前臺廣播,intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);

如果想要廣播能夠更快的被接收,那么可以將它定義為前臺廣播。
因為系統(tǒng)默認是后臺廣播隊列,前臺廣播隊列比較空閑,可以更快響應(yīng)。

C)ServiceTimeOut
Service的超時時間為20秒(后端是200S),定義在ActiveServices.java
static final int SERVICE_TIMEOUT = 20*1000;
static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;

1.2 如何避免ANR

a)UI主線程盡量只做跟UI相關(guān)的工作.
b)耗時的操作,如I/O,網(wǎng)絡(luò)連接,把它放入單獨的線程處理
c)盡量用Handler來處理UI線程和非UI線程之間的交互

2. 內(nèi)存

2.1 堆、棧和方法區(qū)

JAVA是在JVM所虛擬出的內(nèi)存環(huán)境中運行的,內(nèi)存分為三個區(qū):堆、棧和方法區(qū)。

  1. 棧(stack):是簡單的數(shù)據(jù)結(jié)構(gòu),程序運行時系統(tǒng)自動分配,使用完畢后自動釋放。優(yōu)點:速度快。

  2. 堆(heap):用于存放由new創(chuàng)建的對象和數(shù)組。在堆中分配的內(nèi)存,一方面由java虛擬機自動垃圾回收器來管理,另一方面還需要程序員提供修養(yǎng),防止內(nèi)存泄露問題。

  3. 方法區(qū)(method):又叫靜態(tài)區(qū),跟堆一樣,被所有的線程共享。方法區(qū)包含所有的class和static變量。

2.2 內(nèi)存溢出、內(nèi)存泄漏、內(nèi)存抖動

  1. 內(nèi)存溢出(Out of Memory):系統(tǒng)會給每個APP分配內(nèi)存也就是Heap Size值。當(dāng)APP占用的內(nèi)存加上我們申請的內(nèi)存資源超過了Dalvik虛擬機的最大內(nèi)存時就會拋出的Out Of Memory異常。

  2. 內(nèi)存泄漏(Memory Leak):當(dāng)一個對象不在使用了,本應(yīng)該被垃圾回收器(JVM)回收。但是這個對象由于被其他正在使用的對象所持有,造成無法被回收的結(jié)果。內(nèi)存泄漏最終會導(dǎo)致內(nèi)存溢出。

  3. 內(nèi)存抖動:內(nèi)存抖動是指在短時間內(nèi)有大量的對象被創(chuàng)建或者被回收的現(xiàn)象,主要是循環(huán)中大量創(chuàng)建、回收對象。這種情況應(yīng)當(dāng)盡量避免。

它們?nèi)叩闹匾燃壏謩e:內(nèi)存溢出 > 內(nèi)存泄露 > 內(nèi)存抖動。
內(nèi)存溢出對我們的App來說,影響是非常大的。有可能導(dǎo)致程序閃退,無響應(yīng)等現(xiàn)象,因此,我們一定要優(yōu)先解決OOM的問題。



原因及處理方法:

1. 單例導(dǎo)致內(nèi)存泄露,它對應(yīng)應(yīng)用程序的生命周期,所以我們在構(gòu)造單例的時候盡量避免使用`Activity`的上下文,而是使用`Application`的上下文。否則被持有的`Activity`在關(guān)閉時候無法被回收。

2. 靜態(tài)變量導(dǎo)致內(nèi)存泄露,靜態(tài)變量存儲在方法區(qū),它的生命周期從類加載開始,到整個進程結(jié)束。一旦靜態(tài)變量初始化后,它所持有的引用只有等到進程結(jié)束才會釋放。

3. 非靜態(tài)內(nèi)部類導(dǎo)致內(nèi)存泄露,非靜態(tài)內(nèi)部類(包括匿名內(nèi)部類)默認就會持有外部類的引用,當(dāng)非靜態(tài)內(nèi)部類對象的生命周期比外部類對象的生命周期長時,就會導(dǎo)致內(nèi)存泄露。

   > 非靜態(tài)內(nèi)部類導(dǎo)致的內(nèi)存泄露在Android開發(fā)中有一種典型的場景就是使用`Handler`。
   >
   > 非靜態(tài)內(nèi)部類造成內(nèi)存泄露還有一種情況就是使用`Thread`或者`AsyncTask`。

   > 通常在Android開發(fā)中如果要使用內(nèi)部類,但又要規(guī)避內(nèi)存泄露,一般都會采用*靜態(tài)內(nèi)部類+弱引用*的方式。
   >
   > 同時還需要在`Activity`銷毀時就將`mHandler`的回調(diào)和發(fā)送的消息給移除掉。

4. 未取消注冊或回調(diào)導(dǎo)致內(nèi)存泄露。

5. 集合中的對象未清理造成內(nèi)存泄露。

6. 資源未關(guān)閉或釋放導(dǎo)致內(nèi)存泄露。在使用`IO`、`File`流或者`Sqlite`、`Cursor`等資源時要及時關(guān)閉。

7. 屬性動畫造成內(nèi)存泄露。在`Activity`銷毀的時候`cancel`掉屬性動畫,雖然看不到這個動畫了,但是它還在不斷播放。

8. `Timer`和`TimerTask`導(dǎo)致內(nèi)存泄露。當(dāng)我們`Activity`銷毀的時,有可能`Timer`還在繼續(xù)等待執(zhí)行`TimerTask`,它持有Activity的引用不能被回收,因此當(dāng)我們Activity銷毀的時候要立即`cancel`掉`Timer`和`TimerTask`,以避免發(fā)生內(nèi)存泄漏。

9. `WebView`造成內(nèi)存泄露。因為WebView在加載網(wǎng)頁后會長期占用內(nèi)存而不能被釋放,在銷毀`WebView`之前需要先將`WebView從`父容器中移除,后要調(diào)用它的`destory()`方法來銷毀它以釋放內(nèi)存。

2.3 強引用、軟引用、弱引用、虛引用

  1. 強引用:強引用是使用最普遍的引用。如果一個對象具有強引用,那垃圾回收器絕不會回收它。 當(dāng)內(nèi)存空間不足,Java虛擬機寧愿拋出OutOfMemoryError錯誤,使程序異常終止,也不會靠隨意回收具有強引用的對象來解決內(nèi)存不足的問題。

  2. 軟引用:如果一個對象只具有軟引用,但內(nèi)存空間足夠時,垃圾回收器就不會回收它;直到虛擬機報告內(nèi)存不夠時才會回收, 只要垃圾回收器沒有回收它,該對象就可以被程序使用。軟引用可用來實現(xiàn)內(nèi)存敏感的高速緩存。 軟引用可以和一個引用隊列(ReferenceQueue)聯(lián)合使用,如果軟引用所引用的對象被垃圾回收器回收,Java虛擬機就會把這個軟引用加入到與之關(guān)聯(lián)的引用隊列中。

  3. 弱引用(谷歌推薦Android使用):只具有弱引用的對象擁有更短暫的生命周期。在垃圾回收器線程掃描它所管轄的內(nèi)存區(qū)域的過程中,一旦發(fā)現(xiàn)了只具有弱引用的對象,不管當(dāng)前內(nèi)存空間是否足夠,都會回收它的內(nèi)存。 不過,由于垃圾回收器是一個優(yōu)先級很低的線程,因此不一定會很快發(fā)現(xiàn)那些只具有弱引用的對象。 弱引用可以和一個引用隊列(ReferenceQueue)聯(lián)合使用,如果弱引用所引用的對象被垃圾回收,Java虛擬機就會把這個弱引用加入到與之關(guān)聯(lián)的引用隊列中。

  4. 虛引用:虛引用可以理解為虛設(shè)的引用,與其他幾種引用都不同,虛引用并不會決定對象的生命周期。如果一個對象僅持有虛引用,那么它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收。 虛引用主要用來跟蹤對象被垃圾回收器回收的活動。

    虛引用與軟引用和弱引用的一個區(qū)別在于:虛引用必須和引用隊列 (ReferenceQueue)聯(lián)合使用。 當(dāng)垃圾回收器準備回收一個對象時,如果發(fā)現(xiàn)它還有虛引用,就會在回收對象的內(nèi)存之前,把這個虛引用加入到與之 關(guān)聯(lián)的引用隊列中。 程序可以通過判斷引用隊列中是否已經(jīng)加入了虛引用,來了解被引用的對象是否將要被垃圾回收。 如果程序發(fā)現(xiàn)某個虛引用已經(jīng)被加入到引用隊列,那么就可以在所引用的對象的內(nèi)存被回收之前采取必要的行動。

2.4 常用檢測內(nèi)存是否泄漏的工具

    LeackCanary、Memory Monitor、DDMS

2.5 GC回收機制與優(yōu)化

當(dāng)系統(tǒng)內(nèi)存不足時就會觸發(fā)GC回收機制。 還有就是當(dāng)系統(tǒng)空閑時候會觸發(fā),在優(yōu)先級最低的進程中進行。優(yōu)先級有五個等級:前臺進程,可見進程,服務(wù)進程,后臺進程,空進程。

1)盡早釋放無用對象。
2)合理使用軟引用(內(nèi)存不夠時候,遇到就會清理)和弱引用(遇到就會清理)。
3)如果需要使用經(jīng)常使用的圖片,可以使用軟引用類型。它可以盡可能將圖片保存在內(nèi)存中,供程序調(diào)用,而不引起OutOfMemory;
4)盡量少用靜態(tài)對象變量,靜態(tài)變量屬于全局變量,不會被GC回收,它們會一直占用內(nèi)存。
5)能用基本類型如int,long,就不用Integer,Long對象?;绢愋妥兞空加玫膬?nèi)存資源比相應(yīng)對象占用的少得多

3. RecyclerView優(yōu)化

  1. 數(shù)據(jù)處理與視圖綁定分離

RecyclerView的bindViewHolder方法是在UI線程進行的,如果在該方法進行耗時操作,將會影響滑動的流暢性。

  1. 數(shù)據(jù)優(yōu)化

分頁加載遠端數(shù)據(jù),對拉取的遠端數(shù)據(jù)進行緩存,提高二次加載速度;

  1. 布局優(yōu)化

減少布局層級,可以考慮使用自定義View來減少層級,或者更合理的設(shè)置布局來減少層級。

  1. 減少View對象的創(chuàng)建

一個稍微復(fù)雜的 Item 會包含大量的 View,而大量的 View 的創(chuàng)建也會消耗大量時間,所以要盡可能簡化 ItemView;設(shè)計 ItemType 時,對多 ViewType 能夠共用的部分盡量設(shè)計成自定義 View,減少 View 的構(gòu)造和嵌套

  1. 設(shè)置高度固定

如果item高度是固定的話,可以使用RecyclerView.setHasFixedSize(true);來避免requestLayout浪費資源。

  1. 加大RecyclerView的緩存

用空間換時間,來提高滾動的流暢性。

  1. 減少ItemView監(jiān)聽器的創(chuàng)建

對ItemView設(shè)置監(jiān)聽器,不要對每個item都創(chuàng)建一個監(jiān)聽器,而應(yīng)該共用一個Listener,然后根據(jù)ID來進行不同的操作,優(yōu)化了對象的頻繁創(chuàng)建帶來的資源消耗。

  1. 優(yōu)化滑動操作

設(shè)置RecyclerView.addOnScrollListener();來在滑動過程中停止加載的操作

  1. 回收資源

通過重寫RecyclerView.onViewRecycled(holder)來回收資源。

  1. 共用RecycledViewPool

如果多個 RecycledView 的 Adapter 是一樣的,比如嵌套的 RecyclerView 中存在一樣的 Adapter,可以通過設(shè)置 RecyclerView.setRecycledViewPool(pool); 來共用一個 RecycledViewPool。

  1. 處理刷新閃爍

用notifyDataSetChange時,適配器不知道整個數(shù)據(jù)集中的那些內(nèi)容以及存在,再重新匹配ViewHolder時會花生閃爍。
設(shè)置adapter.setHasStableIds(true),并重寫getItemId()來給每個Item一個唯一的ID

4. 如何優(yōu)化頁面啟動速度

  1. 不要在Application的構(gòu)造方法、attachBaseContext()、onCreate()里面進行初始化耗時操作。盡可能使用IntentService代替初始化工作。
  2. 不要Activity在onCreate、onStart、onResume當(dāng)中做耗時操作。
  3. 合理使用延遲加載。

$ UI適配

1.smallestWidth 限定符屏幕適配方案

根據(jù)主流屏幕的 最小寬度 (smallestWidth) 生成一系列 values-sw<N>dp 文件夾 (含有 dimens.xml 文件),當(dāng)把項目運行到設(shè)備上時,系統(tǒng)會根據(jù)當(dāng)前設(shè)備屏幕的 最小寬度 (smallestWidth) 去匹配對應(yīng)的 values-sw<N>dp 文件夾,而對應(yīng)的 values-sw<N>dp 文件夾中的 dimens.xml 文字中的值,又是根據(jù)當(dāng)前設(shè)備屏幕的 最小寬度 (smallestWidth) 而定制的,所以一定能適配當(dāng)前設(shè)備。

2.AndroidAutoSize適配

今日頭條適配方案適配方案的升級版。它支持對Activity、Fragment進行取消適配,靈活性會更強。

在確保設(shè)計圖總寬度(單位dp)一定時,通過修改density值,確保所有不同尺寸分辨率設(shè)備計算出的真實寬度值正好是屏幕寬度,這樣就能達到適配所有設(shè)備的目的。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1、Handler: 1):系統(tǒng)Handler的創(chuàng)建過程及相關(guān)對象的創(chuàng)建:在ActivityThread的main...
    Steven_SHH閱讀 1,006評論 0 5
  • 面試必背 會舍棄、總結(jié)概括——根據(jù)我這些年面試和看面試題搜集過來的知識點匯總而來 建議根據(jù)我的寫的面試應(yīng)對思路中的...
    luoyangzk閱讀 7,164評論 6 173
  • 聲明本文由作者:Man不經(jīng)心授權(quán)轉(zhuǎn)載,轉(zhuǎn)載請聯(lián)系原文作者原文鏈接:http://www.itdecent.cn/...
    猿天下閱讀 679評論 0 7
  • 面試,無非都是問上面這些問題(挺多的 - -!),聘請中高級的安卓開發(fā)會往深的去問,并且會問一延伸二。以下我先提出...
    那年的歌閱讀 529評論 0 0
  • 寫在開頭 由于杭州的房價實在太高,所以我可恥的跑路到了西安。幾個月前在西安買了房,所以最近總結(jié)了一些還算全面的An...
    BlackFlag閱讀 10,116評論 10 200

友情鏈接更多精彩內(nèi)容